Working i386 paging and liballoc

This commit is contained in:
2025-12-07 00:53:33 +01:00
commit de5350010b
75 changed files with 6716 additions and 0 deletions

1
kernel/mm/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.o

89
kernel/mm/bba.c Normal file
View File

@@ -0,0 +1,89 @@
/*
Copyright 2025 Kamil Kowalczyk
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <libk/types.h>
#include <libk/bitmap.h>
#include <libk/string.h>
#include <libk/util.h>
#include <libk/compiler.h>
#include <sync/spinlock.h>
#include <mm/bba.h>
#include <config.h>
static struct bba bba;
static volatile byte_t bba_membuf[BBA_SIZE] aligned(4096);
static volatile byte_t bba_bm_membuf[BBA_BM_SIZE] aligned(4096);
void bba_init(void) {
memset((void *)&bba, 0, sizeof(bba));
bba.baseptr = (uptr_t)bba_membuf;
bitmap_init(&bba.bm, (byte_t *)bba_bm_membuf, BBA_SIZE / BBA_BLOCK_SIZE);
sl_init(&bba.sl, "bba");
dbgf("bba_init(): initialized BBA %zu blocks, %zu block size, total=%zu\n",
BBA_SIZE / BBA_BLOCK_SIZE, BBA_BLOCK_SIZE, sizeof(bba_membuf));
}
uptr_t bba_alloc(void) {
uptr_t ret = 0;
sl_lock(&bba.sl);
for (usize_t i = 0; i < bba.bm.bit_count; i++) {
if (!bitmap_test(&bba.bm, i)) {
ret = (bba.baseptr + i * BBA_BLOCK_SIZE);
bitmap_set(&bba.bm, i);
goto done;
}
}
done:
sl_unlock(&bba.sl);
return ret;
}
void bba_free(uptr_t addr) {
uptr_t aligned_addr = ALIGN_DOWN(addr, BBA_BLOCK_SIZE);
if (aligned_addr < bba.baseptr)
goto done;
if (aligned_addr >= bba.baseptr + bba.bm.bit_count * BBA_BLOCK_SIZE)
goto done;
usize_t bit = (usize_t)(aligned_addr - bba.baseptr) / BBA_BLOCK_SIZE;
sl_lock(&bba.sl);
bitmap_clear(&bba.bm, bit);
sl_unlock(&bba.sl);
done:
return;
}

54
kernel/mm/bba.h Normal file
View File

@@ -0,0 +1,54 @@
/*
Copyright 2025 Kamil Kowalczyk
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _MM_BBA_H
#define _MM_BBA_H
#include <libk/types.h>
#include <libk/bitmap.h>
#include <sync/spinlock.h>
/* Bootstrap Bitmap Allocator */
#define BBA_BLOCK_SIZE (4096)
#define BBA_SIZE (BBA_BLOCK_SIZE * 128)
#define BBA_BM_SIZE (BBA_BLOCK_SIZE)
struct bba {
struct spinlock sl;
struct bitmap bm;
uptr_t baseptr;
};
void bba_init(void);
uptr_t bba_alloc(void);
void bba_free(uptr_t addr);
#endif // _MM_BBA_H

604
kernel/mm/liballoc.c Normal file
View File

@@ -0,0 +1,604 @@
/* Porting: */
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
#pragma clang diagnostic ignored "-Wself-assign"
#pragma clang optimize off
#endif
#include <libk/stdatomic.h>
#include <libk/types.h>
#include <sys/mm.h>
#include <mm/liballoc.h>
#include <mm/pmm.h>
#include <sync/spinlock.h>
#include <config.h>
struct spinlock liballoc_sl = { .flag = ATOMIC_FLAG_INIT, .name = "liballoc" };
uptr_t liballoc_memory_start = KERNEL_HEAP_START;
int liballoc_lock(void) {
sl_lock(&liballoc_sl);
return 0;
}
int liballoc_unlock(void) {
sl_unlock(&liballoc_sl);
return 0;
}
void *liballoc_alloc(int pages) {
#if defined(__i386__)
struct page_dir *pd = mm_get_kernel_pd();
sl_lock(&pd->sl);
uptr_t vaddr = liballoc_memory_start;
for (uptr_t page = vaddr; page < vaddr + PAGE_SIZE * pages; page += PAGE_SIZE) {
uptr_t phys = pmm_alloc();
mm_map_page(pd, page, phys, PF_WRITABLE | PF_PRESENT);
}
sl_unlock(&pd->sl);
return (void *)vaddr;
#else
#error "platform unimplemented"
#endif
}
int liballoc_free(void *ptr1, int pages) {
#if defined(__i386__)
struct page_dir *pd = mm_get_kernel_pd();
uptr_t ptr = ALIGN_DOWN((uptr_t)ptr1, 0x1000);
sl_lock(&pd->sl);
for (uptr_t page = ptr; page < ptr + PAGE_SIZE * pages; page += PAGE_SIZE) {
uptr_t phys = mm_translate(pd, page, 0);
mm_unmap_page(pd, page, 0);
pmm_free(phys);
}
sl_unlock(&pd->sl);
return 0;
#else
#error "platform unimplemented"
#endif
}
/** Durand's Ridiculously Amazing Super Duper Memory functions. */
//#define DEBUG
#define LIBALLOC_MAGIC 0xc001c0de
#define MAXCOMPLETE 5
#define MAXEXP 32
#define MINEXP 8
#define MODE_BEST 0
#define MODE_INSTANT 1
#define MODE MODE_BEST
#ifdef DEBUG
#include <stdio.h>
#endif
struct boundary_tag* l_freePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks
int l_completePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks
#ifdef DEBUG
unsigned int l_allocated = 0; //< The real amount of memory allocated.
unsigned int l_inuse = 0; //< The amount of memory in use (malloc'ed).
#endif
static int l_initialized = 0; //< Flag to indicate initialization.
static int l_pageSize = 4096; //< Individual page size
static int l_pageCount = 16; //< Minimum number of pages to allocate.
// *********** HELPER FUNCTIONS *******************************
/** Returns the exponent required to manage 'size' amount of memory.
*
* Returns n where 2^n <= size < 2^(n+1)
*/
static inline int getexp( unsigned int size )
{
if ( size < (1<<MINEXP) )
{
#ifdef DEBUG
printf("getexp returns -1 for %i less than MINEXP\n", size );
#endif
return -1; // Smaller than the quantum.
}
int shift = MINEXP;
while ( shift < MAXEXP )
{
if ( (1<<shift) > size ) break;
shift += 1;
}
#ifdef DEBUG
printf("getexp returns %i (%i bytes) for %i size\n", shift - 1, (1<<(shift -1)), size );
#endif
return shift - 1;
}
static void* liballoc_memset(void* s, int c, size_t n)
{
int i;
for ( i = 0; i < n ; i++)
((char*)s)[i] = c;
return s;
}
static void* liballoc_memcpy(void* s1, const void* s2, size_t n)
{
char *cdest;
char *csrc;
unsigned int *ldest = (unsigned int*)s1;
unsigned int *lsrc = (unsigned int*)s2;
while ( n >= sizeof(unsigned int) )
{
*ldest++ = *lsrc++;
n -= sizeof(unsigned int);
}
cdest = (char*)ldest;
csrc = (char*)lsrc;
while ( n > 0 )
{
*cdest++ = *csrc++;
n -= 1;
}
return s1;
}
#ifdef DEBUG
static void dump_array()
{
int i = 0;
struct boundary_tag *tag = NULL;
printf("------ Free pages array ---------\n");
printf("System memory allocated: %i\n", l_allocated );
printf("Memory in used (malloc'ed): %i\n", l_inuse );
for ( i = 0; i < MAXEXP; i++ )
{
printf("%.2i(%i): ",i, l_completePages[i] );
tag = l_freePages[ i ];
while ( tag != NULL )
{
if ( tag->split_left != NULL ) printf("*");
printf("%i", tag->real_size );
if ( tag->split_right != NULL ) printf("*");
printf(" ");
tag = tag->next;
}
printf("\n");
}
printf("'*' denotes a split to the left/right of a tag\n");
fflush( stdout );
}
#endif
static inline void insert_tag( struct boundary_tag *tag, int index )
{
int realIndex;
if ( index < 0 )
{
realIndex = getexp( tag->real_size - sizeof(struct boundary_tag) );
if ( realIndex < MINEXP ) realIndex = MINEXP;
}
else
realIndex = index;
tag->index = realIndex;
if ( l_freePages[ realIndex ] != NULL )
{
l_freePages[ realIndex ]->prev = tag;
tag->next = l_freePages[ realIndex ];
}
l_freePages[ realIndex ] = tag;
}
static inline void remove_tag( struct boundary_tag *tag )
{
if ( l_freePages[ tag->index ] == tag ) l_freePages[ tag->index ] = tag->next;
if ( tag->prev != NULL ) tag->prev->next = tag->next;
if ( tag->next != NULL ) tag->next->prev = tag->prev;
tag->next = NULL;
tag->prev = NULL;
tag->index = -1;
}
static inline struct boundary_tag* melt_left( struct boundary_tag *tag )
{
struct boundary_tag *left = tag->split_left;
left->real_size += tag->real_size;
left->split_right = tag->split_right;
if ( tag->split_right != NULL ) tag->split_right->split_left = left;
return left;
}
static inline struct boundary_tag* absorb_right( struct boundary_tag *tag )
{
struct boundary_tag *right = tag->split_right;
remove_tag( right ); // Remove right from free pages.
tag->real_size += right->real_size;
tag->split_right = right->split_right;
if ( right->split_right != NULL )
right->split_right->split_left = tag;
return tag;
}
static inline struct boundary_tag* split_tag( struct boundary_tag* tag )
{
unsigned int remainder = tag->real_size - sizeof(struct boundary_tag) - tag->size;
struct boundary_tag *new_tag =
(struct boundary_tag*)((unsigned int)tag + sizeof(struct boundary_tag) + tag->size);
new_tag->magic = LIBALLOC_MAGIC;
new_tag->real_size = remainder;
new_tag->next = NULL;
new_tag->prev = NULL;
new_tag->split_left = tag;
new_tag->split_right = tag->split_right;
if (new_tag->split_right != NULL) new_tag->split_right->split_left = new_tag;
tag->split_right = new_tag;
tag->real_size -= new_tag->real_size;
insert_tag( new_tag, -1 );
return new_tag;
}
// ***************************************************************
static struct boundary_tag* allocate_new_tag( unsigned int size )
{
unsigned int pages;
unsigned int usage;
struct boundary_tag *tag;
// This is how much space is required.
usage = size + sizeof(struct boundary_tag);
// Perfect amount of space
pages = usage / l_pageSize;
if ( (usage % l_pageSize) != 0 ) pages += 1;
// Make sure it's >= the minimum size.
if ( pages < l_pageCount ) pages = l_pageCount;
tag = (struct boundary_tag*)liballoc_alloc( pages );
if ( tag == NULL ) return NULL; // uh oh, we ran out of memory.
tag->magic = LIBALLOC_MAGIC;
tag->size = size;
tag->real_size = pages * l_pageSize;
tag->index = -1;
tag->next = NULL;
tag->prev = NULL;
tag->split_left = NULL;
tag->split_right = NULL;
#ifdef DEBUG
printf("Resource allocated %x of %i pages (%i bytes) for %i size.\n", tag, pages, pages * l_pageSize, size );
l_allocated += pages * l_pageSize;
printf("Total memory usage = %i KB\n", (int)((l_allocated / (1024))) );
#endif
return tag;
}
void *malloc(size_t size)
{
int index;
void *ptr;
struct boundary_tag *tag = NULL;
liballoc_lock();
if ( l_initialized == 0 )
{
#ifdef DEBUG
printf("%s\n","liballoc initializing.");
#endif
for ( index = 0; index < MAXEXP; index++ )
{
l_freePages[index] = NULL;
l_completePages[index] = 0;
}
l_initialized = 1;
}
index = getexp( size ) + MODE;
if ( index < MINEXP ) index = MINEXP;
// Find one big enough.
tag = l_freePages[ index ]; // Start at the front of the list.
while ( tag != NULL )
{
// If there's enough space in this tag.
if ( (tag->real_size - sizeof(struct boundary_tag))
>= (size + sizeof(struct boundary_tag) ) )
{
#ifdef DEBUG
printf("Tag search found %i >= %i\n",(tag->real_size - sizeof(struct boundary_tag)), (size + sizeof(struct boundary_tag) ) );
#endif
break;
}
tag = tag->next;
}
// No page found. Make one.
if ( tag == NULL )
{
if ( (tag = allocate_new_tag( size )) == NULL )
{
liballoc_unlock();
return NULL;
}
index = getexp( tag->real_size - sizeof(struct boundary_tag) );
}
else
{
remove_tag( tag );
if ( (tag->split_left == NULL) && (tag->split_right == NULL) )
l_completePages[ index ] -= 1;
}
// We have a free page. Remove it from the free pages list.
tag->size = size;
// Removed... see if we can re-use the excess space.
#ifdef DEBUG
printf("Found tag with %i bytes available (requested %i bytes, leaving %i), which has exponent: %i (%i bytes)\n", tag->real_size - sizeof(struct boundary_tag), size, tag->real_size - size - sizeof(struct boundary_tag), index, 1<<index );
#endif
unsigned int remainder = tag->real_size - size - sizeof( struct boundary_tag ) * 2; // Support a new tag + remainder
if ( ((int)(remainder) > 0) /*&& ( (tag->real_size - remainder) >= (1<<MINEXP))*/ )
{
int childIndex = getexp( remainder );
if ( childIndex >= 0 )
{
#ifdef DEBUG
printf("Seems to be splittable: %i >= 2^%i .. %i\n", remainder, childIndex, (1<<childIndex) );
#endif
struct boundary_tag *new_tag = split_tag( tag );
new_tag = new_tag; // Get around the compiler warning about unused variables.
#ifdef DEBUG
printf("Old tag has become %i bytes, new tag is now %i bytes (%i exp)\n", tag->real_size, new_tag->real_size, new_tag->index );
#endif
}
}
ptr = (void*)((unsigned int)tag + sizeof( struct boundary_tag ) );
#ifdef DEBUG
l_inuse += size;
printf("malloc: %x, %i, %i\n", ptr, (int)l_inuse / 1024, (int)l_allocated / 1024 );
dump_array();
#endif
liballoc_unlock();
return ptr;
}
void free(void *ptr)
{
int index;
struct boundary_tag *tag;
if ( ptr == NULL ) return;
liballoc_lock();
tag = (struct boundary_tag*)((unsigned int)ptr - sizeof( struct boundary_tag ));
if ( tag->magic != LIBALLOC_MAGIC )
{
liballoc_unlock(); // release the lock
return;
}
#ifdef DEBUG
l_inuse -= tag->size;
printf("free: %x, %i, %i\n", ptr, (int)l_inuse / 1024, (int)l_allocated / 1024 );
#endif
// MELT LEFT...
while ( (tag->split_left != NULL) && (tag->split_left->index >= 0) )
{
#ifdef DEBUG
printf("Melting tag left into available memory. Left was %i, becomes %i (%i)\n", tag->split_left->real_size, tag->split_left->real_size + tag->real_size, tag->split_left->real_size );
#endif
tag = melt_left( tag );
remove_tag( tag );
}
// MELT RIGHT...
while ( (tag->split_right != NULL) && (tag->split_right->index >= 0) )
{
#ifdef DEBUG
printf("Melting tag right into available memory. This was was %i, becomes %i (%i)\n", tag->real_size, tag->split_right->real_size + tag->real_size, tag->split_right->real_size );
#endif
tag = absorb_right( tag );
}
// Where is it going back to?
index = getexp( tag->real_size - sizeof(struct boundary_tag) );
if ( index < MINEXP ) index = MINEXP;
// A whole, empty block?
if ( (tag->split_left == NULL) && (tag->split_right == NULL) )
{
if ( l_completePages[ index ] == MAXCOMPLETE )
{
// Too many standing by to keep. Free this one.
unsigned int pages = tag->real_size / l_pageSize;
if ( (tag->real_size % l_pageSize) != 0 ) pages += 1;
if ( pages < l_pageCount ) pages = l_pageCount;
liballoc_free( tag, pages );
#ifdef DEBUG
l_allocated -= pages * l_pageSize;
printf("Resource freeing %x of %i pages\n", tag, pages );
dump_array();
#endif
liballoc_unlock();
return;
}
l_completePages[ index ] += 1; // Increase the count of complete pages.
}
// ..........
insert_tag( tag, index );
#ifdef DEBUG
printf("Returning tag with %i bytes (requested %i bytes), which has exponent: %i\n", tag->real_size, tag->size, index );
dump_array();
#endif
liballoc_unlock();
}
void* calloc(size_t nobj, size_t size)
{
int real_size;
void *p;
real_size = nobj * size;
p = malloc( real_size );
liballoc_memset( p, 0, real_size );
return p;
}
void* realloc(void *p, size_t size)
{
void *ptr;
struct boundary_tag *tag;
int real_size;
if ( size == 0 )
{
free( p );
return NULL;
}
if ( p == NULL ) return malloc( size );
if ( liballoc_lock != NULL ) liballoc_lock(); // lockit
tag = (struct boundary_tag*)((unsigned int)p - sizeof( struct boundary_tag ));
real_size = tag->size;
if ( liballoc_unlock != NULL ) liballoc_unlock();
if ( real_size > size ) real_size = size;
ptr = malloc( size );
liballoc_memcpy( ptr, p, real_size );
free( p );
return ptr;
}

99
kernel/mm/liballoc.h Normal file
View File

@@ -0,0 +1,99 @@
#ifndef _LIBALLOC_H
#define _LIBALLOC_H
// If we are told to not define our own size_t, then we
// skip the define.
#ifndef _ALLOC_SKIP_DEFINE
#ifndef _HAVE_SIZE_T
#define _HAVE_SIZE_T
typedef unsigned int size_t;
#endif
#ifndef NULL
#define NULL 0
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** This is a boundary tag which is prepended to the
* page or section of a page which we have allocated. It is
* used to identify valid memory blocks that the
* application is trying to free.
*/
struct boundary_tag
{
unsigned int magic; //< It's a kind of ...
unsigned int size; //< Requested size.
unsigned int real_size; //< Actual size.
int index; //< Location in the page table.
struct boundary_tag *split_left; //< Linked-list info for broken pages.
struct boundary_tag *split_right; //< The same.
struct boundary_tag *next; //< Linked list info.
struct boundary_tag *prev; //< Linked list info.
};
/** This function is supposed to lock the memory data structures. It
* could be as simple as disabling interrupts or acquiring a spinlock.
* It's up to you to decide.
*
* \return 0 if the lock was acquired successfully. Anything else is
* failure.
*/
extern int liballoc_lock();
/** This function unlocks what was previously locked by the liballoc_lock
* function. If it disabled interrupts, it enables interrupts. If it
* had acquiried a spinlock, it releases the spinlock. etc.
*
* \return 0 if the lock was successfully released.
*/
extern int liballoc_unlock();
/** This is the hook into the local system which allocates pages. It
* accepts an integer parameter which is the number of pages
* required. The page size was set up in the liballoc_init function.
*
* \return NULL if the pages were not allocated.
* \return A pointer to the allocated memory.
*/
extern void* liballoc_alloc(int);
/** This frees previously allocated memory. The void* parameter passed
* to the function is the exact same value returned from a previous
* liballoc_alloc call.
*
* The integer value is the number of pages to free.
*
* \return 0 if the memory was successfully freed.
*/
extern int liballoc_free(void*,int);
void *malloc(size_t); //< The standard function.
void *realloc(void *, size_t); //< The standard function.
void *calloc(size_t, size_t); //< The standard function.
void free(void *); //< The standard function.
#ifdef __cplusplus
}
#endif
#endif

9
kernel/mm/make.src Normal file
View File

@@ -0,0 +1,9 @@
dir_mm1 := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
c_files += $(dir_mm1)/pmm.c \
$(dir_mm1)/bba.c \
$(dir_mm1)/liballoc.c
o_files += $(dir_mm1)/pmm.o \
$(dir_mm1)/bba.o \
$(dir_mm1)/liballoc.o

87
kernel/mm/pmm.c Normal file
View File

@@ -0,0 +1,87 @@
/*
Copyright 2025 Kamil Kowalczyk
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <libk/types.h>
#include <libk/bitmap.h>
#include <libk/string.h>
#include <libk/util.h>
#include <sync/spinlock.h>
#include <mm/pmm.h>
#include <config.h>
static struct pmm pmm;
void pmm_init(uptr_t baseptr, uptr_t bm_baseptr, usize_t block_count, usize_t block_size) {
zero(&pmm);
pmm.block_size = block_size;
pmm.baseptr = baseptr;
bitmap_init(&pmm.bm, (byte_t *)bm_baseptr, block_count);
sl_init(&pmm.sl, "pmm");
dbgf("pmm_init(): initialized PMM %zu blocks, %zu block size, total=%zu\n",
block_count, block_size, block_count * block_size);
}
uptr_t pmm_alloc(void) {
uptr_t ret = 0;
sl_lock(&pmm.sl);
for (usize_t i = 0; i < pmm.bm.bit_count; i++) {
if (!bitmap_test(&pmm.bm, i)) {
ret = (pmm.baseptr + i * pmm.block_size);
bitmap_set(&pmm.bm, i);
goto done;
}
}
done:
sl_unlock(&pmm.sl);
return ret;
}
void pmm_free(uptr_t addr) {
uptr_t aligned_addr = ALIGN_DOWN(addr, pmm.block_size);
if (aligned_addr < pmm.baseptr)
goto done;
if (aligned_addr >= pmm.baseptr + pmm.bm.bit_count * pmm.block_size)
goto done;
usize_t bit = (usize_t)(aligned_addr - pmm.baseptr) / pmm.block_size;
sl_lock(&pmm.sl);
bitmap_clear(&pmm.bm, bit);
sl_unlock(&pmm.sl);
done:
return;
}

50
kernel/mm/pmm.h Normal file
View File

@@ -0,0 +1,50 @@
/*
Copyright 2025 Kamil Kowalczyk
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _MM_PMM_H
#define _MM_PMM_H
#include <libk/types.h>
#include <libk/bitmap.h>
#include <sync/spinlock.h>
struct pmm {
struct spinlock sl;
struct bitmap bm;
usize_t block_size;
uptr_t baseptr;
};
void pmm_init(uptr_t baseptr, uptr_t bm_baseptr, usize_t block_count, usize_t block_size);
uptr_t pmm_alloc(void);
void pmm_free(uptr_t addr);
#endif // _MM_PMM_H