Compare commits
6 commits
6f5746a805
...
8316c9f6ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8316c9f6ae | ||
|
|
90c63ab41e | ||
|
|
21d55031d9 | ||
|
|
e762502c33 | ||
|
|
7301205e1f | ||
|
|
57be90da84 |
11 changed files with 198 additions and 150 deletions
2
Makefile
2
Makefile
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include <panic.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define assert(cond) \
|
||||
do { \
|
||||
|
|
|
|||
139
kern/libkern/buddy.c
Normal file
139
kern/libkern/buddy.c
Normal 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
22
kern/libkern/buddy.h
Normal 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
|
||||
|
|
@ -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
|
||||
while (sweeper < end) {
|
||||
*(uint64_t *)sweeper = 0xFAFAFAFABCBCBCBC;
|
||||
sweeper += sizeof(uint64_t);
|
||||
}
|
||||
|
||||
*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 detected;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, ...);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue