diff --git a/Makefile b/Makefile index 9636a7f..3d29dc2 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax CFLAGS += -I. CFLAGS += -Ilib +CFLAGS += -Ikern CFLAGS += -fno-stack-protector # Prevents code that needs libc / runtime support CFLAGS += -MD # Generate header dependency files (.d) @@ -27,7 +28,7 @@ CFLAGS += -fno-omit-frame-pointer # More reliable backtraces in GDB all: kernel.elf -kernel.elf: entry.o start.o lib/string.o lib/proc.o lib/spinlock.o lib/proc.o lib/uart.o lib/panic.o +kernel.elf: entry.o start.o lib/string.o lib/proc.o lib/spinlock.o lib/proc.o lib/uart.o lib/panic.o kern/kalloc.o lib/memory.o @echo LD $@ @$(LD) $(LDFLAGS) -o $@ $^ diff --git a/kern/kalloc.c b/kern/kalloc.c new file mode 100644 index 0000000..6cd329d --- /dev/null +++ b/kern/kalloc.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include + +// Physical memory allocator, for user processes, +// kernel stacks, page-table pages, +// and pipe buffers. Allocates whole 4096-byte pages. + +/** Free list of physical pages. */ +void freerange(void *physaddr_start, void *physaddr_end); + +/** First address after kernel. Provided kernel.ld */ +extern char kernel_end[]; + +/** A run is a node in the free list. */ +struct Run { + struct Run *next; +}; + +/** Kernel memory allocator. */ +struct { + struct Spinlock lock; + struct Run *freelist; +} kmem; + +void kalloc_init() { + initlock(&kmem.lock, "kmem"); + freerange(kernel_end, (void *)PHYSTOP); +} + +void freerange(void *physaddr_start, void *physaddr_end) { + char *p; + p = (char *)PGROUNDUP((u64)physaddr_start); + for (; p + PGSIZE <= (char *)physaddr_end; p += PGSIZE) kfree(p); +} + +void kfree(void *pa) { + struct Run *r; + + // Assert that page is a ligned to a page boundary and that its correctly + // sized + if (((u64)pa % PGSIZE) != 0 || (char *)pa < kernel_end || + (u64)pa >= PHYSTOP) + panic("kfree"); + + // Fill with junk to catch dangling refs. + memset(pa, 1, PGSIZE); + + r = (struct Run *)pa; + + acquire(&kmem.lock); + r->next = kmem.freelist; + kmem.freelist = r; + release(&kmem.lock); +} + +void *kalloc(void) { + struct Run *r; + + acquire(&kmem.lock); + + r = kmem.freelist; + + if (r) + kmem.freelist = r->next; + release(&kmem.lock); + if (r) + memset((char *)r, 5, PGSIZE); // fill with junk + + return (void *)r; +} diff --git a/kern/kalloc.h b/kern/kalloc.h new file mode 100644 index 0000000..0f13e53 --- /dev/null +++ b/kern/kalloc.h @@ -0,0 +1,33 @@ +#ifndef KALLOC_KERNEL_H +#define KALLOC_KERNEL_H + +/** + * Kernel memory allocator + * + * Allocate one 4096-byte page of physical memory. + * Returns a pointer that the kernel can use. + * Returns 0 if the memory cannot be allocated. + * See: kalloc.c + */ +void *kalloc(void); + +/** + * Kernel memory allocator + * + * Free the page of physical memory pointed at by pa, + * which normally should have been returned by a + * call to kalloc(). (The exception is when + * initializing the allocator; see kinit above.) + * See: kalloc.c + */ +void kfree(void *); + +/** + * Initialize kernel memory allocator + * + * Called by main() on the way to the kernel's main loop. + * See: kalloc.c + */ +void kalloc_init(void); + +#endif diff --git a/lib/memory.c b/lib/memory.c new file mode 100644 index 0000000..a1cb090 --- /dev/null +++ b/lib/memory.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#define MAX_PROBE_SIZE (256 * 1024 * 1024) // Probe up to 256 MiB max +#define PROBE_STEP 0x1000 // Probe every 4 KiB page + +size_t probe_memory(void) { + volatile u32 *addr; + u32 test_pattern = 0xA5A5A5A5; + size_t detected = 0; + + for (size_t offset = 4096 * 16; offset < MAX_PROBE_SIZE; + offset += PROBE_STEP) { + addr = (volatile u32 *)(KERNBASE + offset); + + u32 old = *addr; + *addr = test_pattern; + + if (*addr != test_pattern) { + break; // Memory not readable/writable here, stop probing + } + + *addr = old; // restore original data + detected = offset + PROBE_STEP; + } + + return detected; +} diff --git a/lib/memory.h b/lib/memory.h new file mode 100644 index 0000000..8a012a2 --- /dev/null +++ b/lib/memory.h @@ -0,0 +1,16 @@ +#ifndef MEMORY_KERNEL_H +#define MEMORY_KERNEL_H + +#include + +/* These are hardcoded for now */ +#define KERNBASE 0x80000000L +#define PHYSTOP (KERNBASE + 128 * 1024 * 1024) + +/** + * Returns size in bytes of detected RAM In qemu, it requires a trap handler to + * handle the interrupt when accessing unavailable memory. + */ +size_t probe_memory(void); + +#endif