BarryServer : Git

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

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)
/*
 * 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");
}