Compare commits
No commits in common. "8316c9f6aeeb977eaee7a442f66d071a3e1cd617" and "6f5746a805fadf1f137c9a9e46f427ebab81d49b" have entirely different histories.
8316c9f6ae
...
6f5746a805
11 changed files with 150 additions and 198 deletions
2
Makefile
2
Makefile
|
|
@ -55,6 +55,7 @@ quickstart:
|
||||||
KERNEL_OBJ := \
|
KERNEL_OBJ := \
|
||||||
kern/entry.o \
|
kern/entry.o \
|
||||||
kern/start.o \
|
kern/start.o \
|
||||||
|
kern/kalloc.o \
|
||||||
kern/libkern/string.o \
|
kern/libkern/string.o \
|
||||||
kern/libkern/proc.o \
|
kern/libkern/proc.o \
|
||||||
kern/libkern/uart.o \
|
kern/libkern/uart.o \
|
||||||
|
|
@ -63,7 +64,6 @@ KERNEL_OBJ := \
|
||||||
kern/libkern/spinlock.o \
|
kern/libkern/spinlock.o \
|
||||||
kern/libkern/mini-printf.o \
|
kern/libkern/mini-printf.o \
|
||||||
kern/libkern/stdio.o \
|
kern/libkern/stdio.o \
|
||||||
kern/libkern/buddy.o \
|
|
||||||
kern/libkern/badrand.o
|
kern/libkern/badrand.o
|
||||||
|
|
||||||
kern/kernel.elf: $(KERNEL_OBJ)
|
kern/kernel.elf: $(KERNEL_OBJ)
|
||||||
|
|
|
||||||
77
kern/kalloc.c
Normal file
77
kern/kalloc.c
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
33
kern/kalloc.h
Normal file
33
kern/kalloc.h
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
OUTPUT_ARCH( "riscv" )
|
OUTPUT_ARCH( "riscv" )
|
||||||
ENTRY( _entry ) /* See: entry.S */
|
ENTRY( _entry ) /* See: entry.S */
|
||||||
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTIONS
|
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
|
* This section contains the code. This is, the machine language instructions
|
||||||
* that will be executed by the processor. In here we will find symbols
|
* 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. */
|
/* Define symbol etext to be the current location. */
|
||||||
PROVIDE(etext = .);
|
PROVIDE(etext = .);
|
||||||
} > RAM :text
|
} :text
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This contains any data that is marked as read only.
|
* This contains any data that is marked as read only.
|
||||||
|
|
@ -46,7 +47,7 @@ SECTIONS
|
||||||
*(.srodata*) /* do not need to distinguish this from .rodata */
|
*(.srodata*) /* do not need to distinguish this from .rodata */
|
||||||
. = ALIGN(16);
|
. = ALIGN(16);
|
||||||
*(.rodata*)
|
*(.rodata*)
|
||||||
} > RAM
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This section contains initialized global and static variables.
|
* This section contains initialized global and static variables.
|
||||||
|
|
@ -57,7 +58,7 @@ SECTIONS
|
||||||
*(.sdata*) /* do not need to distinguish this from .data */
|
*(.sdata*) /* do not need to distinguish this from .data */
|
||||||
. = ALIGN(16);
|
. = ALIGN(16);
|
||||||
*(.data*)
|
*(.data*)
|
||||||
} > RAM
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Contains all uninitialized global and static var iables. These are usually
|
* 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 */
|
*(.sbss*) /* do not need to distinguish this from .bss */
|
||||||
. = ALIGN(16);
|
. = ALIGN(16);
|
||||||
*(.bss*)
|
*(.bss*)
|
||||||
} > RAM
|
}
|
||||||
|
|
||||||
/* Define symbol end as current location, note that this is not aligned, see vm.c */
|
/* Define symbol end as current location, note that this is not aligned, see vm.c */
|
||||||
PROVIDE(kernel_end = .);
|
PROVIDE(kernel_end = .);
|
||||||
PROVIDE(__heap_start = ALIGN(32));
|
|
||||||
PROVIDE(__heap_end = ORIGIN(RAM) + LENGTH(RAM));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PHDRS {
|
PHDRS {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
#include <panic.h>
|
#include <panic.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define assert(cond) \
|
#define assert(cond) \
|
||||||
do { \
|
do { \
|
||||||
|
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#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,21 +1,28 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdlib.h>
|
#include <string.h>
|
||||||
|
#include <uart.h>
|
||||||
|
|
||||||
int memory_sweep(const uintptr_t start, const uintptr_t end) {
|
#define MAX_PROBE_SIZE (256 * 1024 * 1024) // Probe up to 256 MiB max
|
||||||
assert_msg(start < end, "Start needs to be before end.");
|
#define PROBE_STEP 0x1000 // Probe every 4 KiB page
|
||||||
uintptr_t sweeper = start;
|
|
||||||
|
|
||||||
while (sweeper < end) {
|
size_t probe_memory(void) {
|
||||||
*(uint64_t *)sweeper = 0xFAFAFAFABCBCBCBC;
|
volatile u32 *addr;
|
||||||
sweeper += sizeof(uint64_t);
|
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);
|
return detected;
|
||||||
while (sweeper != start) {
|
|
||||||
assert(*(uint64_t *)sweeper == 0xFAFAFAFABCBCBCBC);
|
|
||||||
sweeper -= sizeof(uint64_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@
|
||||||
#define PHYSTOP (KERNBASE + 128 * 1024 * 1024)
|
#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
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef KERNEL_PANIC_H
|
#ifndef KERNEL_PANIC_H
|
||||||
#define 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, ...);
|
void __panic(const char *restrict fmt, ...);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <banner.h>
|
#include <banner.h>
|
||||||
#include <buddy.h>
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <kalloc.h>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <panic.h>
|
#include <panic.h>
|
||||||
#include <proc.h>
|
#include <proc.h>
|
||||||
|
|
@ -9,7 +8,6 @@
|
||||||
#include <spinlock.h>
|
#include <spinlock.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
#include <uart.h>
|
#include <uart.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,8 +39,7 @@ void start() {
|
||||||
|
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
/* Here we will do a bunch of initialization steps */
|
/* Here we will do a bunch of initialization steps */
|
||||||
memory_sweep(heap_start, heap_end);
|
kalloc_init();
|
||||||
buddy_init(heap_start, heap_end);
|
|
||||||
spinlock_init(&sl);
|
spinlock_init(&sl);
|
||||||
for (int i = 0; i < banner_len; i++) uart_putc(banner[i]);
|
for (int i = 0; i < banner_len; i++) uart_putc(banner[i]);
|
||||||
__sync_synchronize();
|
__sync_synchronize();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue