lucicos/kernel/pmm/src/pmm.c

176 lines
3.5 KiB
C

#include "kernel/pmm.h"
#include "pmm_internals.h"
#include "lib/bitmap.h"
#include <string.h>
#include <stddef.h>
/*
* pmm_init:
* ---------
*
* Set the internal variables and initialize the pmmap data type.
* It initializes all memory zones as used by settings all bits in pmmap.
* At the moment, pmmap is a bitmap.
*
*/
void pmm_init(uint32_t mmap_addr, size_t size) {
mem_size = size;
max_blocks = (mem_size * 1024) / BLOCK_SIZE;
used_blocks = max_blocks;
pmmap = (uint32_t *) mmap_addr;
pmmap_size = mem_size / BLOCKS_PER_BYTE;
if (mem_size % BLOCKS_PER_BYTE)
pmmap_size++;
memset(pmmap, 0xFF, pmmap_size);
}
/*
* pmm_init_available_regions:
* ---------------------------
*
* Using multiboot info, it initializes all available memory zones.
*
*/
void pmm_init_available_regions(uint32_t mmap_, uint32_t mmap_end_) {
multiboot_memory_map_t *mmap = (multiboot_memory_map_t *) mmap_;
multiboot_memory_map_t *mmap_end = (multiboot_memory_map_t *) mmap_end_;
for (int i = 0; mmap < mmap_end; mmap++, i++)
if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE)
pmm_init_region((uint32_t) mmap->addr, (size_t) mmap->len);
}
/*
* pmm_init_region:
* ----------------
*
* @var blocks - Number of blocks to be unset starting from @align
* @var align - Starting index in bitmap
*
*/
void pmm_init_region(uint32_t base, size_t size) {
size_t blocks = size / BLOCK_SIZE;
uint32_t align = base / BLOCK_SIZE;
for (size_t i = 0; i < blocks; i++) {
bitmap_unset(pmmap, align++);
used_blocks--;
}
bitmap_set(pmmap, 0);
}
/*
* pmm_deinit_region:
* ------------------
*
* Sets the corresponding bit in pmmap and increments the number of
* used blocks.
*
*/
void pmm_deinit_region(uint32_t base, size_t size) {
size_t blocks = size / BLOCK_SIZE;
uint32_t align = base / BLOCK_SIZE;
for (size_t i = 0; i < blocks; i++) {
bitmap_set(pmmap, align++);
used_blocks++;
}
}
/*
* pmm_deinit_kernel:
* ------------------
*
* Deinitializes the blocks containing the kernel and the memory map. There
* is an align operation because the routine tries to deinitialize all
* blocks where pmmap may sit in memory.
*
*/
void pmm_deinit_kernel(void) {
extern uint8_t *_kernel_start;
extern uint8_t *_kernel_end;
size_t kernel_size = (size_t) &_kernel_end - (size_t) &_kernel_start;
uint32_t pmmap_size_aligned = pmmap_size;
if (!IS_ALIGNED(pmmap_size_aligned, BLOCK_SIZE))
pmmap_size_aligned = ALIGN(pmmap_size_aligned, BLOCK_SIZE);
pmm_deinit_region((uint32_t) &_kernel_start, kernel_size);
pmm_deinit_region((uint32_t) &_kernel_end, pmmap_size_aligned);
}
/*
* pmm_alloc_block:
* ----------------
*
* Searches for a free block and returns a pointer to its physical memory
* location.
*
*/
void *pmm_alloc_block(void) {
if (used_blocks - max_blocks <= 0)
return NULL;
int p_index = bitmap_first_unset(pmmap, max_blocks);
if (p_index == -1)
return NULL;
bitmap_set(pmmap, p_index);
used_blocks++;
return (void *) (BLOCK_SIZE * p_index);
}
/*
* pmm_free_block:
* ---------------
*
* Checks if p is a valid pointer to free (is not first memory block)
* and frees its entry it pmmap.
*
*/
void pmm_free_block(void *p) {
if (p == NULL)
return;
uint32_t p_addr = (uint32_t) p;
int index = p_addr / BLOCK_SIZE;
bitmap_unset(pmmap, index);
used_blocks--;
}