Compare commits

...

6 commits

11 changed files with 198 additions and 150 deletions

View file

@ -55,7 +55,6 @@ quickstart:
KERNEL_OBJ := \
kern/entry.o \
kern/start.o \
kern/kalloc.o \
kern/libkern/string.o \
kern/libkern/proc.o \
kern/libkern/uart.o \
@ -64,6 +63,7 @@ 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)

View file

@ -1,77 +0,0 @@
#include <kalloc.h>
#include <memory.h>
#include <panic.h>
#include <riscv.h>
#include <spinlock.h>
#include <stdint.h>
#include <string.h>
// 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;
}

View file

@ -1,33 +0,0 @@
#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

View file

@ -1,14 +1,13 @@
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
@ -35,7 +34,7 @@ SECTIONS
/* Define symbol etext to be the current location. */
PROVIDE(etext = .);
} :text
} > RAM :text
/*
* This contains any data that is marked as read only.
@ -47,7 +46,7 @@ SECTIONS
*(.srodata*) /* do not need to distinguish this from .rodata */
. = ALIGN(16);
*(.rodata*)
}
} > RAM
/*
* This section contains initialized global and static variables.
@ -58,7 +57,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
@ -71,10 +70,12 @@ 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 {

View file

@ -1,4 +1,5 @@
#include <panic.h>
#include <stdio.h>
#define assert(cond) \
do { \

139
kern/libkern/buddy.c Normal file
View file

@ -0,0 +1,139 @@
#include <assert.h>
#include <buddy.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#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;
}

22
kern/libkern/buddy.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef BUDDY_H
#define BUDDY_H
#include <stddef.h>
#include <stdint.h>
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

View file

@ -1,28 +1,21 @@
#include <assert.h>
#include <memory.h>
#include <string.h>
#include <uart.h>
#include <stdlib.h>
#define MAX_PROBE_SIZE (256 * 1024 * 1024) // Probe up to 256 MiB max
#define PROBE_STEP 0x1000 // Probe every 4 KiB page
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;
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;
while (sweeper < end) {
*(uint64_t *)sweeper = 0xFAFAFAFABCBCBCBC;
sweeper += sizeof(uint64_t);
}
return detected;
sweeper -= sizeof(uint64_t);
while (sweeper != start) {
assert(*(uint64_t *)sweeper == 0xFAFAFAFABCBCBCBC);
sweeper -= sizeof(uint64_t);
}
return EXIT_SUCCESS;
}

View file

@ -8,9 +8,8 @@
#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.
* Set and fetch routine for checking memory.
*/
size_t probe_memory(void);
int memory_sweep(const uintptr_t start, const uintptr_t end);
#endif // MEMORY_KARNEL_H
#endif // MEMORY_KERNEL_H

View file

@ -1,7 +1,7 @@
#ifndef KERNEL_PANIC_H
#define KERNEL_PANIC_H
#define PANIC(fmt, ...) __panic("[Panic @ %s:%d %s] " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define PANIC(fmt, ...) __panic("[Panic @ %s:%d %s] " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
void __panic(const char *restrict fmt, ...);

View file

@ -1,6 +1,7 @@
#include <assert.h>
#include <banner.h>
#include <buddy.h>
#include <config.h>
#include <kalloc.h>
#include <memory.h>
#include <panic.h>
#include <proc.h>
@ -8,6 +9,7 @@
#include <spinlock.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <uart.h>
/**
@ -39,7 +41,8 @@ void start() {
if (id == 0) {
/* Here we will do a bunch of initialization steps */
kalloc_init();
memory_sweep(heap_start, heap_end);
buddy_init(heap_start, heap_end);
spinlock_init(&sl);
for (int i = 0; i < banner_len; i++) uart_putc(banner[i]);
__sync_synchronize();