Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/mem/frame.c b/mem/frame.c new file mode 100644 index 0000000..ee88fd8 --- /dev/null +++ b/mem/frame.c @@ -0,0 +1,207 @@ +/* + * This file handles allocating and freeing of page frames. It it the core of + * the physical memory manager. The Kernel heap and paging systems both sit on + * top of this. It keeps track of which frames of physical memory are free and + * hands them out as needed. To do this it keeps a simple bitmap of the frames. + */ + +#include <stdint.h> +#include "mem.h" +#include "paging.h" +#include "frame.h" +#include "../screen.h" + +#define INDEX(a) ((a)/32) +#define OFFSET(a) ((a)%32) + +/* Descriptor of a Frame Region */ +typedef struct FrameRegion FrameRegion; +struct FrameRegion { + uint32_t base; + size_t numFrames; + size_t usedFrames; + FrameRegion *next; + uint32_t bitmap[]; +}; + +size_t numFrames, usedFrames; +FrameRegion *regions; + +/* Count the number of pages a number of bytes occupies */ +static size_t +page_count(size_t bytes) +{ + size_t pages = bytes/0x1000; + if (bytes & 0xFFF) + pages++; + return pages; +} + +/* Set a bit in the frame bitset */ +static void +set_frame(FrameRegion *region, uint32_t idx) +{ + region->bitmap[INDEX(idx)] |= (1 << OFFSET(idx)); + region->usedFrames++; + usedFrames++; +} + +/* Clear a bit in the frame bitset */ +static void +clear_frame(FrameRegion *region, uint32_t idx) +{ + region->bitmap[INDEX(idx)] &= ~(1 << OFFSET(idx)); + region->usedFrames--; + usedFrames--; +} + +/* Test a bit in the frame bitset */ +static uint32_t +test_frame(FrameRegion *region, uint32_t idx) +{ + return (region->bitmap[INDEX(idx)] & (1 << OFFSET(idx))); +} + +/* Get n bits from position p */ +static inline uint32_t +get_bits(uint32_t x, uint8_t p, uint8_t n) +{ + return (x >> (p+1-n)) & ~(~0 << n); +} + +/* Find first free frame */ +static uint32_t +find_frames(FrameRegion *region, size_t frames) +{ + /* TODO: best fit */ + /* FIXME: frames can be at most 32 */ + uint32_t i, j; + for (i = 0; i < INDEX(region->numFrames); i++) { + if (!~region->bitmap[i]) + continue; + for (j = 0; j < 32; j++) { + if (get_bits(~region->bitmap[i], j, frames) == ~(~0 << + frames)) + return (i*32+j)-frames+1; + /* TODO: check across uint32_t boundaries */ + } + } + return (uint32_t) -1; +} + +/* Allocate a set of contiguous page frames */ +uint32_t +alloc_frames(size_t frames) +{ + uint32_t idx; + size_t i; + /* Walk the regions, first fit */ + FrameRegion *region; + for (region = regions; region; region = region->next) { + idx = find_frames(region, frames); + if (idx != -1) + break; + } + if (idx == -1) + return -1; + + for (i = 0; i < frames; i++) + set_frame(region, idx + i); + + return (uint32_t) region->base + (idx << 12); +} + +/* Free a page frame */ +void +free_frame(uint32_t frame) +{ + /* Walk the regions */ + FrameRegion *region = regions; + while (region) { + if ((uint32_t) region > frame) { + frame -= region->base; + clear_frame(region, frame >> 12); + break; + } + region = region->next; + } +} + +/* Setup the frame allocator */ +void +init_frames(uint32_t memMapSize, struct E820Entry *memMap) +{ + /* Relocate the memory map */ + struct E820Entry *top = (void *) 0x20000; + if (memMap < top) { + memcpy(top, memMap, memMapSize); + memMap = (void *) top; + } + + /* + * When the OS starts, the bootloader passes the Kernel a map of memory. + * This map must be read, so the memory manager can avoid bad areas of + * memory, that as mapped to hardware, ACPI, etc. + * The frame allocator should only create a bitmap for frames that it + * can actually allocate. This means setting avoiding bad areas, and + * avoiding the Kernel. + */ + uint32_t i, j; + FrameRegion *prev = 0, *head = regions; + uint32_t bumpAlloc = 0x1000; + for (i = 0; i < memMapSize / sizeof(struct E820Entry); i++) { + kprintf("MemMap: base=%#.8x%.8x, length=%#.8x%.8x, type=%d", + memMap[i].baseHigh, memMap[i].base, + memMap[i].lengthHigh, memMap[i].length, + memMap[i].type); + if (memMap[i].baseHigh > 0 + || memMap[i].base > 0xFFFFFFFF + || memMap[i].type != 1) + continue; + + /* Usable region - create bitmap */ + size_t frameSize = memMap[i].length / 0x1000; + size_t bitmapSize = (frameSize / 8); + head = (FrameRegion *) bumpAlloc; + bumpAlloc += bitmapSize + sizeof(FrameRegion); + head->base = memMap[i].base; + head->numFrames = frameSize; + head->usedFrames = 0; + memset(head->bitmap, 0, bitmapSize); + /* Set top bits to 1, so they're never allocated */ + for (j = OFFSET(head->numFrames); j < 32; j++) + set_frame(head, head->numFrames + j); + if (prev) prev->next = head; + else regions = head; + prev = head; + } + + /* Regions to be remapped */ + struct {uint32_t start, end;} remaps[] = { + {.start = 0x0000, .end = bumpAlloc}, /* PMM bitmaps */ + {.start = 0xB0000, .end = 0xC0000 }, /* VGA memory */ + {.start = 0x100000, .end = 0x180000 }, /* Kernel */ + }; + + kprintf("Bump allocator top @ %#.8x", bumpAlloc); + + /* Check bitmaps */ + usedFrames = 0; + numFrames = 0; + FrameRegion *region = regions; + uint32_t regionEnd; + while (region) { + numFrames += region->numFrames; + regionEnd = region->base + (region->numFrames * 0x1000); + /* Iterate the remaps[] to find overlapping regions */ + for (i = 0; i < sizeof(remaps)/sizeof(remaps[0]); i++) + for (j = remaps[i].start; j < remaps[i].end + && j >= region->base && j < regionEnd; + j += 0x1000) + set_frame(region, (j - region->base) >> 12); + region = region->next; + } + + if (numFrames < 1024) /* 4MB */ + panic("Not enough memory"); +}