Compare commits
13 commits
5dbcb33726
...
2aa06778b3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2aa06778b3 | ||
![]() |
b8474a12fc | ||
![]() |
00bc2aa300 | ||
![]() |
bd7959cc6c | ||
![]() |
58ed873401 | ||
![]() |
f2685ce7df | ||
![]() |
e82b75f8f0 | ||
![]() |
71ff137192 | ||
![]() |
521217f2b5 | ||
![]() |
be50854251 | ||
![]() |
697a84b370 | ||
![]() |
4e73eee53e | ||
![]() |
952eeebb6d |
19 changed files with 436 additions and 62 deletions
|
@ -13,3 +13,4 @@ AlignConsecutiveDeclarations:
|
|||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros: true
|
||||
|
|
3
Makefile
3
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 $@ $^
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Neptune Kernel
|
||||
|
||||
Inspired by xv6
|
||||
|
||||
For a quick reference on RISC-V assembly:
|
||||
- https://risc-v.guru/instructions/
|
||||
|
||||
|
@ -9,6 +11,10 @@ Toolchains:
|
|||
|
||||
---
|
||||
|
||||
> A word on terminology: Although the official x86 term is exception, xv6 uses the
|
||||
> term trap, largely because it was the term used by the PDP11/40 and therefore is the
|
||||
> conventional Unix term.
|
||||
|
||||
| Register | Name | Privilege Level | Description |
|
||||
|-------------|----------------------------|------------------|-----------------------------------------------------------------------------|
|
||||
| `mstatus` | Machine Status Register | Machine | Holds global interrupt enable, previous privilege mode, etc. |
|
||||
|
|
75
kern/kalloc.c
Normal file
75
kern/kalloc.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include <kalloc.h>
|
||||
#include <memory.h>
|
||||
#include <panic.h>
|
||||
#include <riscv.h>
|
||||
#include <spinlock.h>
|
||||
#include <string.h>
|
||||
#include <types.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 {
|
||||
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;
|
||||
}
|
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
|
|
@ -74,7 +74,7 @@ SECTIONS
|
|||
}
|
||||
|
||||
/* Define symbol end as current location, note that this is not aligned, see vm.c */
|
||||
PROVIDE(end = .);
|
||||
PROVIDE(kernel_end = .);
|
||||
}
|
||||
|
||||
PHDRS {
|
||||
|
|
92
lib/endian.h
Normal file
92
lib/endian.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
#ifndef ENDIAN_KERNEL_H
|
||||
#define ENDIAN_KERNEL_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/** Swap byte order of 16-bit value */
|
||||
static inline u16 swap16(u16 x) { return (x >> 8) | (x << 8); }
|
||||
|
||||
/** Swap byte order of 32-bit value */
|
||||
static inline u32 swap32(u32 x) {
|
||||
return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00) |
|
||||
((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000);
|
||||
}
|
||||
|
||||
/** Swap byte order of 64-bit value */
|
||||
static inline u64 swap64(u64 x) {
|
||||
return ((x >> 56) & 0x00000000000000ffULL) |
|
||||
((x >> 40) & 0x000000000000ff00ULL) |
|
||||
((x >> 24) & 0x0000000000ff0000ULL) |
|
||||
((x >> 8) & 0x00000000ff000000ULL) |
|
||||
((x << 8) & 0x000000ff00000000ULL) |
|
||||
((x << 24) & 0x0000ff0000000000ULL) |
|
||||
((x << 40) & 0x00ff000000000000ULL) |
|
||||
((x << 56) & 0xff00000000000000ULL);
|
||||
}
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
/** Convert 16-bit value to little-endian */
|
||||
static inline u16 to_le16(u16 x) { return x; }
|
||||
/** Convert 16-bit little-endian value to host */
|
||||
static inline u16 from_le16(u16 x) { return x; }
|
||||
|
||||
/** Convert 32-bit value to little-endian */
|
||||
static inline u32 to_le32(u32 x) { return x; }
|
||||
/** Convert 32-bit little-endian value to host */
|
||||
static inline u32 from_le32(u32 x) { return x; }
|
||||
|
||||
/** Convert 64-bit value to little-endian */
|
||||
static inline u64 to_le64(u64 x) { return x; }
|
||||
/** Convert 64-bit little-endian value to host */
|
||||
static inline u64 from_le64(u64 x) { return x; }
|
||||
|
||||
/** Convert 16-bit value to big-endian */
|
||||
static inline u16 to_be16(u16 x) { return swap16(x); }
|
||||
/** Convert 16-bit big-endian value to host */
|
||||
static inline u16 from_be16(u16 x) { return swap16(x); }
|
||||
|
||||
/** Convert 32-bit value to big-endian */
|
||||
static inline u32 to_be32(u32 x) { return swap32(x); }
|
||||
/** Convert 32-bit big-endian value to host */
|
||||
static inline u32 from_be32(u32 x) { return swap32(x); }
|
||||
|
||||
/** Convert 64-bit value to big-endian */
|
||||
static inline u64 to_be64(u64 x) { return swap64(x); }
|
||||
/** Convert 64-bit big-endian value to host */
|
||||
static inline u64 from_be64(u64 x) { return swap64(x); }
|
||||
|
||||
#else // Big-endian
|
||||
|
||||
/** Convert 16-bit value to little-endian */
|
||||
static inline u16 to_le16(u16 x) { return swap16(x); }
|
||||
/** Convert 16-bit little-endian value to host */
|
||||
static inline u16 from_le16(u16 x) { return swap16(x); }
|
||||
|
||||
/** Convert 32-bit value to little-endian */
|
||||
static inline u32 to_le32(u32 x) { return swap32(x); }
|
||||
/** Convert 32-bit little-endian value to host */
|
||||
static inline u32 from_le32(u32 x) { return swap32(x); }
|
||||
|
||||
/** Convert 64-bit value to little-endian */
|
||||
static inline u64 to_le64(u64 x) { return swap64(x); }
|
||||
/** Convert 64-bit little-endian value to host */
|
||||
static inline u64 from_le64(u64 x) { return swap64(x); }
|
||||
|
||||
/** Convert 16-bit value to big-endian */
|
||||
static inline u16 to_be16(u16 x) { return x; }
|
||||
/** Convert 16-bit big-endian value to host */
|
||||
static inline u16 from_be16(u16 x) { return x; }
|
||||
|
||||
/** Convert 32-bit value to big-endian */
|
||||
static inline u32 to_be32(u32 x) { return x; }
|
||||
/** Convert 32-bit big-endian value to host */
|
||||
static inline u32 from_be32(u32 x) { return x; }
|
||||
|
||||
/** Convert 64-bit value to big-endian */
|
||||
static inline u64 to_be64(u64 x) { return x; }
|
||||
/** Convert 64-bit big-endian value to host */
|
||||
static inline u64 from_be64(u64 x) { return x; }
|
||||
|
||||
#endif // __LITTLE_ENDIAN__
|
||||
|
||||
#endif // ENDIAN_KERNEL_H
|
29
lib/memory.c
Normal file
29
lib/memory.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <uart.h>
|
||||
|
||||
#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;
|
||||
}
|
16
lib/memory.h
Normal file
16
lib/memory.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef MEMORY_KERNEL_H
|
||||
#define MEMORY_KERNEL_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/* 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
|
19
lib/proc.c
19
lib/proc.c
|
@ -2,18 +2,7 @@
|
|||
|
||||
struct Cpu cpus[NCPU];
|
||||
|
||||
// Must be called with interrupts disabled,
|
||||
// to prevent race with process being moved
|
||||
// to a different CPU.
|
||||
int cpuid() {
|
||||
int id = r_tp();
|
||||
return id;
|
||||
}
|
||||
|
||||
// Return this CPU's cpu struct.
|
||||
// Interrupts must be disabled.
|
||||
struct Cpu *mycpu(void) {
|
||||
int id = cpuid();
|
||||
struct Cpu *c = &cpus[id];
|
||||
return c;
|
||||
}
|
||||
/**
|
||||
* Return this CPU's cpu struct. Interrupts must be disabled.
|
||||
*/
|
||||
inline struct Cpu *mycpu(void) { return &cpus[read_tp()]; }
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <riscv.h>
|
||||
#include <types.h>
|
||||
|
||||
int cpuid(void);
|
||||
struct Cpu *mycpu(void);
|
||||
|
||||
/** Saved registers for kernel context switches. */
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
* On RISC-V, this emits a fence instruction.
|
||||
*/
|
||||
|
||||
/** Initialize spinlock */
|
||||
void initlock(struct spinlock *lk, char *name) {
|
||||
/** Initialize Spinlock */
|
||||
void initlock(struct Spinlock *lk, char *name) {
|
||||
lk->name = name;
|
||||
lk->locked = 0;
|
||||
lk->cpu = 0;
|
||||
|
@ -51,7 +51,7 @@ void initlock(struct spinlock *lk, char *name) {
|
|||
* Loops (spins) until the lock is acquired.
|
||||
* Panics if the lock is already held by this cpu.
|
||||
*/
|
||||
void acquire(struct spinlock *lk) {
|
||||
void acquire(struct Spinlock *lk) {
|
||||
push_off(); // disable interrupts to avoid deadlock.
|
||||
|
||||
if (holding(lk)) // If the lock is already held, panic.
|
||||
|
@ -70,7 +70,7 @@ void acquire(struct spinlock *lk) {
|
|||
* Release the lock.
|
||||
* Panics if the lock is not held.
|
||||
*/
|
||||
void release(struct spinlock *lk) {
|
||||
void release(struct Spinlock *lk) {
|
||||
if (!holding(lk)) // If the lock is not held, panic.
|
||||
panic("release");
|
||||
|
||||
|
@ -83,7 +83,7 @@ void release(struct spinlock *lk) {
|
|||
|
||||
// Check whether this cpu is holding the lock.
|
||||
// Interrupts must be off.
|
||||
int holding(struct spinlock *lk) {
|
||||
int holding(struct Spinlock *lk) {
|
||||
int r;
|
||||
r = (lk->locked && lk->cpu == mycpu());
|
||||
return r;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#ifndef KERNEL_SPINLOCK_H
|
||||
#define KERNEL_SPINLOCK_H
|
||||
#ifndef KERNEL_Spinlock_H
|
||||
#define KERNEL_Spinlock_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/** Mutual exclusion spin lock */
|
||||
struct spinlock {
|
||||
struct Spinlock {
|
||||
u32 locked; // Is the lock held?
|
||||
|
||||
// NOTE: Perhaps feature gate this?
|
||||
|
@ -19,24 +19,24 @@ struct spinlock {
|
|||
* Loops (spins) until the lock is acquired.
|
||||
* Panics if the lock is already held by this cpu.
|
||||
*/
|
||||
void acquire(struct spinlock *);
|
||||
void acquire(struct Spinlock *);
|
||||
|
||||
/**
|
||||
* Check whether this cpu is holding the lock.
|
||||
* Interrupts must be off.
|
||||
*/
|
||||
int holding(struct spinlock *);
|
||||
int holding(struct Spinlock *);
|
||||
|
||||
/**
|
||||
* Initialize spinlock
|
||||
* Initialize Spinlock
|
||||
*/
|
||||
void initlock(struct spinlock *, char *);
|
||||
void initlock(struct Spinlock *, char *);
|
||||
|
||||
/**
|
||||
* Release the lock.
|
||||
* Panics if the lock is not held.
|
||||
*/
|
||||
void release(struct spinlock *);
|
||||
void release(struct Spinlock *);
|
||||
|
||||
/**
|
||||
* @brief push_off/pop_off are like intr_off()/intr_on() except that they are
|
||||
|
|
66
lib/string.c
66
lib/string.c
|
@ -1,8 +1,10 @@
|
|||
#include <string.h>
|
||||
|
||||
char *itoa(int value, char *str, int base) {
|
||||
char *p = str;
|
||||
char *p1, *p2;
|
||||
char *p = str;
|
||||
char *p1, *p2;
|
||||
unsigned int uvalue = value;
|
||||
int negative = 0;
|
||||
int negative = 0;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
*str = '\0';
|
||||
|
@ -37,3 +39,61 @@ char *itoa(int value, char *str, int base) {
|
|||
|
||||
return str;
|
||||
}
|
||||
|
||||
void *memset(void *dst, int c, size_t length) {
|
||||
u8 *ptr = (u8 *)dst;
|
||||
const u8 value = (u8)c;
|
||||
|
||||
while (length--) *(ptr++) = value;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void *memcpy(void *dst, const void *src, size_t len) {
|
||||
u8 *d = (u8 *)dst;
|
||||
const u8 *s = (const u8 *)src;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
void *memmove(void *dst, const void *src, size_t len) {
|
||||
u8 *d = (u8 *)dst;
|
||||
const u8 *s = (const u8 *)src;
|
||||
if (d < s) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
} else if (d > s) {
|
||||
for (size_t i = len; i > 0; i--) {
|
||||
d[i - 1] = s[i - 1];
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
int memcmp(const void *s1, const void *s2, size_t len) {
|
||||
const u8 *a = (const u8 *)s1;
|
||||
const u8 *b = (const u8 *)s2;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (a[i] != b[i]) {
|
||||
return (int)a[i] - (int)b[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t strlen(const char *s) {
|
||||
const char *p = s;
|
||||
while (*p) ++p;
|
||||
return (size_t)(p - s);
|
||||
}
|
||||
|
||||
size_t strnlen(const char *s, size_t maxlen) {
|
||||
size_t len = 0;
|
||||
while (len < maxlen && s[len] != '\0') {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
|
33
lib/string.h
33
lib/string.h
|
@ -1,7 +1,40 @@
|
|||
#ifndef KERNEL_STRING_H
|
||||
#define KERNEL_STRING_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/** Integer to ascii */
|
||||
char *itoa(int value, char *str, int base);
|
||||
|
||||
/** Fill memory with constant byte */
|
||||
void *memset(void *dst, int c, size_t len);
|
||||
|
||||
/** Copy `len` bytes from `src` to `dst`. Undefined if regions overlap. */
|
||||
void *memcpy(void *dst, const void *src, size_t len);
|
||||
|
||||
/** Copy `len` bytes from `src` to `dst`, safe for overlapping regions. */
|
||||
void *memmove(void *dst, const void *src, size_t len);
|
||||
|
||||
/** Compare `len` bytes of `s1` and `s2`.
|
||||
* Returns 0 if equal, <0 if s1 < s2, >0 if s1 > s2. */
|
||||
int memcmp(const void *s1, const void *s2, size_t len);
|
||||
|
||||
/** Returns the length of a null-terminated string */
|
||||
size_t strlen(const char *s);
|
||||
|
||||
/** Return length of string `s`, up to a max of `maxlen` bytes */
|
||||
size_t strnlen(const char *s, size_t maxlen);
|
||||
|
||||
// TODO: These:
|
||||
/*
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
int strncmp(const char *s1, const char *s2, size_t n);
|
||||
|
||||
char *strcpy(char *dst, const char *src);
|
||||
char *strncpy(char *dst, const char *src, size_t n);
|
||||
|
||||
char *strchr(const char *s, int c);
|
||||
char *strrchr(const char *s, int c);
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,4 +6,3 @@ void uart_putc(char c) { *UART_BASE = c; }
|
|||
void uart_puts(const char *s) {
|
||||
while (*s) uart_putc(*s++);
|
||||
}
|
||||
|
||||
|
|
80
riscv.h
80
riscv.h
|
@ -3,22 +3,20 @@
|
|||
|
||||
#include <types.h>
|
||||
|
||||
/** Page Size */
|
||||
#define PGSIZE 4096 // bytes per page
|
||||
|
||||
// /** Page Shift, bits of offset within a page */
|
||||
#define PGSHIFT 12
|
||||
#define PGROUNDUP(sz) (((sz) + PGSIZE - 1) & ~(PGSIZE - 1))
|
||||
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE - 1))
|
||||
|
||||
// Supervisor Status Register, sstatus
|
||||
|
||||
/** Supervisor Previous Privilege */
|
||||
#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User
|
||||
|
||||
/** Supervisor Previous Interrupt Enable */
|
||||
#define SSTATUS_SPIE (1L << 5)
|
||||
|
||||
/** User Previous Interrupt Enable */
|
||||
#define SSTATUS_UPIE (1L << 4)
|
||||
|
||||
/** Supervisor Interrupt Enable */
|
||||
#define SSTATUS_SIE (1L << 1)
|
||||
|
||||
/** User Interrupt Enable */
|
||||
#define SSTATUS_UIE (1L << 0)
|
||||
#define SSTATUS_SPP (1L << 8) /** Supervisor Previous Privilege 1=S, 0=U */
|
||||
#define SSTATUS_SPIE (1L << 5) /** Supervisor Previous Interrupt Enable */
|
||||
#define SSTATUS_UPIE (1L << 4) /** User Previous Interrupt Enable */
|
||||
#define SSTATUS_SIE (1L << 1) /** Supervisor Interrupt Enable */
|
||||
#define SSTATUS_UIE (1L << 0) /** User Interrupt Enable */
|
||||
|
||||
/** Page Table Entry Type */
|
||||
typedef u64 pte_t;
|
||||
|
@ -26,22 +24,60 @@ typedef u64 pte_t;
|
|||
/** Page Table Type */
|
||||
typedef u64 *pagetable_t; // 512 PTEs
|
||||
|
||||
/** Returns the current hart id */
|
||||
static inline u64 r_mhartid() {
|
||||
u64 x;
|
||||
asm volatile("csrr %0, mhartid" : "=r"(x));
|
||||
return x;
|
||||
// CSR numeric addresses
|
||||
#define CSR_MSTATUS 0x300
|
||||
#define CSR_MISA 0x301
|
||||
#define CSR_MIE 0x304
|
||||
#define CSR_MTVEC 0x305
|
||||
#define CSR_MCOUNTEREN 0x306
|
||||
|
||||
#define CSR_MSCRATCH 0x340
|
||||
#define CSR_MEPC 0x341
|
||||
#define CSR_MCAUSE 0x342
|
||||
#define CSR_MTVAL 0x343
|
||||
#define CSR_MIP 0x344
|
||||
|
||||
#define CSR_MHARTID 0xF14
|
||||
|
||||
static inline u64 read_csr(u32 csr) {
|
||||
u64 value;
|
||||
asm volatile("csrr %0, %1" : "=r"(value) : "i"(csr));
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void write_csr(u32 csr, u64 value) {
|
||||
asm volatile("csrw %0, %1" ::"i"(csr), "r"(value));
|
||||
}
|
||||
|
||||
static inline u64 read_mstatus(void) { return read_csr(CSR_MSTATUS); }
|
||||
static inline void write_mstatus(u64 val) { write_csr(CSR_MSTATUS, val); }
|
||||
|
||||
static inline u64 read_mcause(void) { return read_csr(CSR_MCAUSE); }
|
||||
static inline u64 read_mtval(void) { return read_csr(CSR_MTVAL); }
|
||||
static inline u64 read_mepc(void) { return read_csr(CSR_MEPC); }
|
||||
static inline void write_mepc(u64 val) { write_csr(CSR_MEPC, val); }
|
||||
|
||||
static inline void write_mtvec(u64 val) { write_csr(CSR_MTVEC, val); }
|
||||
static inline u64 read_mtvec(void) { return read_csr(CSR_MTVEC); }
|
||||
|
||||
/** Returns the current hart id */
|
||||
static inline u64 read_mhartid(void) { return read_csr(CSR_MHARTID); }
|
||||
|
||||
// static inline u64 r_mhartid() {
|
||||
// u64 x;
|
||||
// asm volatile("csrr %0, mhartid" : "=r"(x));
|
||||
// return x;
|
||||
// }
|
||||
|
||||
/** Read thread pointer */
|
||||
static inline u64 r_tp() {
|
||||
static inline u64 read_tp() {
|
||||
u64 x;
|
||||
asm volatile("mv %0, tp" : "=r"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
/** Write thread pointer */
|
||||
static inline void w_tp(u64 x) { asm volatile("mv tp, %0" : : "r"(x)); }
|
||||
static inline void write_tp(u64 x) { asm volatile("mv tp, %0" : : "r"(x)); }
|
||||
|
||||
/**
|
||||
* Read the value of the sstatus register.
|
||||
|
|
14
start.c
14
start.c
|
@ -1,8 +1,9 @@
|
|||
#include <config.h>
|
||||
#include <kalloc.h>
|
||||
#include <memory.h>
|
||||
#include <proc.h>
|
||||
#include <riscv.h>
|
||||
#include <spinlock.h>
|
||||
#include <string.h>
|
||||
#include <types.h>
|
||||
#include <uart.h>
|
||||
|
||||
|
@ -15,18 +16,18 @@
|
|||
char stack0[4096 * NCPU] __attribute__((aligned(16)));
|
||||
|
||||
/* Keep this here and sync on it until we have synchronized printf */
|
||||
struct spinlock sl = {0};
|
||||
struct Spinlock sl = {0};
|
||||
volatile int greeted = 0;
|
||||
|
||||
/* This is where entry.S drops us of. All cores land here */
|
||||
void start() {
|
||||
u64 id = r_mhartid();
|
||||
u64 id = read_mhartid();
|
||||
|
||||
// Keep each CPU's hartid in its tp (thread pointer) register, for cpuid().
|
||||
// This can then be retrieved with r_wp or cpuid(). It is used to index the
|
||||
// cpus[] array in mycpu(), which in turn holds state for each individual
|
||||
// cpu (struct Cpu).
|
||||
w_tp(id);
|
||||
write_tp(id);
|
||||
|
||||
acquire(&sl);
|
||||
|
||||
|
@ -41,7 +42,10 @@ void start() {
|
|||
|
||||
release(&sl);
|
||||
|
||||
/* Here we will do a bunch of initialization steps */
|
||||
if (id == 0) {
|
||||
/* Here we will do a bunch of initialization steps */
|
||||
kalloc_init();
|
||||
}
|
||||
|
||||
// We should not arrive here, but if we do, hang in a while on wfi.
|
||||
while (1) __asm__ volatile("wfi"); // (Wait For Interrupt)
|
||||
|
|
1
types.h
1
types.h
|
@ -4,3 +4,4 @@ typedef unsigned char u8;
|
|||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long u64;
|
||||
typedef u64 size_t;
|
||||
|
|
Loading…
Add table
Reference in a new issue