diff --git a/Makefile b/Makefile index 3fbba85..f1c4355 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,7 @@ quickstart: KERNEL_OBJ := \ kern/entry.o \ kern/start.o \ + kern/kalloc.o \ kern/libkern/string.o \ kern/libkern/proc.o \ kern/libkern/uart.o \ @@ -63,7 +64,6 @@ KERNEL_OBJ := \ kern/libkern/spinlock.o \ kern/libkern/mini-printf.o \ kern/libkern/stdio.o \ - kern/libkern/buddy.o \ kern/libkern/badrand.o kern/kernel.elf: $(KERNEL_OBJ) diff --git a/kern/kalloc.c b/kern/kalloc.c new file mode 100644 index 0000000..dead607 --- /dev/null +++ b/kern/kalloc.c @@ -0,0 +1,77 @@ +#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 { + spinlock_t lock; + struct Run *freelist; +} kmem; + +void kalloc_init() { + spinlock_init(&kmem.lock); + 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"); + + // TODO: Kconfig this + // Fill with junk to catch dangling refs. + memset(pa, 1, PGSIZE); + + r = (struct Run *)pa; + + spin_lock(&kmem.lock); + r->next = kmem.freelist; + kmem.freelist = r; + spin_unlock(&kmem.lock); +} + +void *kalloc(void) { + struct Run *r; + + spin_lock(&kmem.lock); + + r = kmem.freelist; + + if (r) + kmem.freelist = r->next; + + spin_unlock(&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..592d814 --- /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 // KALLOC_KERNEL_H diff --git a/kern/kernel.ld b/kern/kernel.ld index 17e1ee2..af3fd7c 100644 --- a/kern/kernel.ld +++ b/kern/kernel.ld @@ -1,13 +1,14 @@ OUTPUT_ARCH( "riscv" ) ENTRY( _entry ) /* See: entry.S */ -MEMORY -{ - RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M -} - SECTIONS { + /* + * ensure that entry.S / _entry is at 0x80000000, + * where qemu's -kernel jumps. + */ + . = 0x80000000; + /* * This section contains the code. This is, the machine language instructions * that will be executed by the processor. In here we will find symbols @@ -34,7 +35,7 @@ SECTIONS /* Define symbol etext to be the current location. */ PROVIDE(etext = .); - } > RAM :text + } :text /* * This contains any data that is marked as read only. @@ -46,7 +47,7 @@ SECTIONS *(.srodata*) /* do not need to distinguish this from .rodata */ . = ALIGN(16); *(.rodata*) - } > RAM + } /* * This section contains initialized global and static variables. @@ -57,7 +58,7 @@ SECTIONS *(.sdata*) /* do not need to distinguish this from .data */ . = ALIGN(16); *(.data*) - } > RAM + } /* * Contains all uninitialized global and static var iables. These are usually @@ -70,12 +71,10 @@ SECTIONS *(.sbss*) /* do not need to distinguish this from .bss */ . = ALIGN(16); *(.bss*) - } > RAM + } /* Define symbol end as current location, note that this is not aligned, see vm.c */ PROVIDE(kernel_end = .); - PROVIDE(__heap_start = ALIGN(32)); - PROVIDE(__heap_end = ORIGIN(RAM) + LENGTH(RAM)); } PHDRS { diff --git a/kern/libkern/assert.h b/kern/libkern/assert.h index 3489342..7cb7a27 100644 --- a/kern/libkern/assert.h +++ b/kern/libkern/assert.h @@ -1,5 +1,4 @@ #include -#include #define assert(cond) \ do { \ diff --git a/kern/libkern/buddy.c b/kern/libkern/buddy.c deleted file mode 100644 index 3b49bb0..0000000 --- a/kern/libkern/buddy.c +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include -#include - -#define MAX_ORDER 22 // 2^22 * min_block_size = max allocatable block -#define MIN_BLOCK_SIZE 32 // Smallest allocatable block - -typedef struct free_block { - struct free_block *next; -} free_block_t; - -typedef struct { - uint8_t order; -} block_header_t; - -static free_block_t *free_lists[MAX_ORDER + 1]; - -uintptr_t heap_start = (uintptr_t)&__heap_start; -uintptr_t heap_end = (uintptr_t)&__heap_end; - -// Utility: round up to next power of two -static size_t next_pow2(size_t x) { - if (x <= 1) - return 1; - x--; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; -#if UINTPTR_MAX > 0xffffffff - x |= x >> 32; -#endif - return x + 1; -} - -// Find order for size -static int size_to_order(size_t size) { - size_t needed = (size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : size; - size_t block_size = next_pow2(needed); - int order = 0; - while ((MIN_BLOCK_SIZE << order) < block_size) { - order++; - } - return order; -} - -// Initialize allocator -void buddy_init(uintptr_t start, uintptr_t end) { - heap_start = (start + MIN_BLOCK_SIZE - 1) & ~(MIN_BLOCK_SIZE - 1); - heap_end = end & ~(MIN_BLOCK_SIZE - 1); - - // Clear free lists - for (int i = 0; i <= MAX_ORDER; i++) free_lists[i] = NULL; - - // Put the whole region as one big block - size_t total_size = heap_end - heap_start; - int order = MAX_ORDER; - while ((MIN_BLOCK_SIZE << order) > total_size && order > 0) order--; - - free_block_t *block = (free_block_t *)heap_start; - block->next = NULL; - free_lists[order] = block; -} - -// Allocate memory -void *buddy_alloc(size_t size) { - int order = size_to_order(size); - - for (int i = order; i <= MAX_ORDER; i++) { - if (free_lists[i]) { - // Remove block from this list - free_block_t *block = free_lists[i]; - free_lists[i] = block->next; - - // Split down to requested order - while (i > order) { - i--; - uintptr_t buddy_addr = (uintptr_t)block + (MIN_BLOCK_SIZE << i); - free_block_t *buddy = (free_block_t *)buddy_addr; - buddy->next = free_lists[i]; - free_lists[i] = buddy; - } - - /* - * Here, we install a header into the block. This will allow us to use free, without - * specifying size, since it can read the order from one byte before the block start. - */ - { - block_header_t *hdr = (block_header_t *)block; - hdr->order = order; - return (void *)(hdr + 1); // return pointer after header - // return (void *)block; // Regular block return for reference - } - } - } - return NULL; // Out of memory -} - -// Free memory -int buddy_free(void *ptr) { - block_header_t *hdr = (block_header_t *)ptr - 1; - int order = hdr->order; - - assert_msg(order != MAX_ORDER, "The buddy freelist header seems to have been corrupted."); - - uintptr_t addr = (uintptr_t)ptr; - - while (order < MAX_ORDER) { - uintptr_t buddy_addr = addr ^ (MIN_BLOCK_SIZE << order); - - // Check if buddy is in free list - free_block_t **prev = &free_lists[order]; - free_block_t *cur = free_lists[order]; - while (cur) { - if ((uintptr_t)cur == buddy_addr) { - // Remove buddy from list - *prev = cur->next; - // Merge - if (buddy_addr < addr) - addr = buddy_addr; - order++; - goto try_merge; - } - prev = &cur->next; - cur = cur->next; - } - break; // Buddy not free, stop - try_merge:; - } - - free_block_t *block = (free_block_t *)addr; - block->next = free_lists[order]; - free_lists[order] = block; - - return order; -} diff --git a/kern/libkern/buddy.h b/kern/libkern/buddy.h deleted file mode 100644 index a6cf8a4..0000000 --- a/kern/libkern/buddy.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef BUDDY_H -#define BUDDY_H - -#include -#include - -extern uintptr_t heap_start; -extern uintptr_t heap_end; - -extern char __heap_start; -extern char __heap_end; - -void buddy_init(uintptr_t start, uintptr_t end); -void *buddy_alloc(size_t size); -int buddy_free(void *ptr); - -/* Returns total heap memory managed by buddy allocator in bytes */ -static inline size_t buddy_total_bytes(void) { - return (uintptr_t)&__heap_end - (uintptr_t)&__heap_start; -} - -#endif // BUDDY_H diff --git a/kern/libkern/memory.c b/kern/libkern/memory.c index 72871fa..47ac873 100644 --- a/kern/libkern/memory.c +++ b/kern/libkern/memory.c @@ -1,21 +1,28 @@ -#include #include -#include +#include +#include -int memory_sweep(const uintptr_t start, const uintptr_t end) { - assert_msg(start < end, "Start needs to be before end."); - uintptr_t sweeper = start; +#define MAX_PROBE_SIZE (256 * 1024 * 1024) // Probe up to 256 MiB max +#define PROBE_STEP 0x1000 // Probe every 4 KiB page - while (sweeper < end) { - *(uint64_t *)sweeper = 0xFAFAFAFABCBCBCBC; - sweeper += sizeof(uint64_t); +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; } - sweeper -= sizeof(uint64_t); - while (sweeper != start) { - assert(*(uint64_t *)sweeper == 0xFAFAFAFABCBCBCBC); - sweeper -= sizeof(uint64_t); - } - - return EXIT_SUCCESS; + return detected; } diff --git a/kern/libkern/memory.h b/kern/libkern/memory.h index a61d701..c8d7d36 100644 --- a/kern/libkern/memory.h +++ b/kern/libkern/memory.h @@ -8,8 +8,9 @@ #define PHYSTOP (KERNBASE + 128 * 1024 * 1024) /** - * Set and fetch routine for checking memory. + * Returns size in bytes of detected RAM In qemu, it requires a trap handler to + * handle the interrupt when accessing unavailable memory. */ -int memory_sweep(const uintptr_t start, const uintptr_t end); +size_t probe_memory(void); -#endif // MEMORY_KERNEL_H +#endif // MEMORY_KARNEL_H diff --git a/kern/libkern/panic.h b/kern/libkern/panic.h index 3eb3b32..4f2e3f4 100644 --- a/kern/libkern/panic.h +++ b/kern/libkern/panic.h @@ -1,7 +1,7 @@ #ifndef KERNEL_PANIC_H #define KERNEL_PANIC_H -#define PANIC(fmt, ...) __panic("[Panic @ %s:%d %s] " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__) +#define PANIC(fmt, ...) __panic("[Panic @ %s:%d %s] " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__) void __panic(const char *restrict fmt, ...); diff --git a/kern/start.c b/kern/start.c index c892765..0e4b264 100644 --- a/kern/start.c +++ b/kern/start.c @@ -1,7 +1,6 @@ -#include #include -#include #include +#include #include #include #include @@ -9,7 +8,6 @@ #include #include #include -#include #include /** @@ -41,8 +39,7 @@ void start() { if (id == 0) { /* Here we will do a bunch of initialization steps */ - memory_sweep(heap_start, heap_end); - buddy_init(heap_start, heap_end); + kalloc_init(); spinlock_init(&sl); for (int i = 0; i < banner_len; i++) uart_putc(banner[i]); __sync_synchronize();