BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / commit / d41a53cbc7d055b1c00cf0a339dbed6925f4f02c / mem / frame.c

// Related

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");
+}