diff options
| -rw-r--r-- | Makefile | 14 | ||||
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | include/fbos/compiler.h | 23 | ||||
| -rw-r--r-- | include/fbos/dt.h | 8 | ||||
| -rw-r--r-- | include/fbos/init.h | 11 | ||||
| -rw-r--r-- | include/fbos/mm.h | 13 | ||||
| -rw-r--r-- | include/fbos/printk.h | 7 | ||||
| -rw-r--r-- | include/fbos/sbi.h | 35 | ||||
| -rw-r--r-- | include/fbos/sched.h | 8 | ||||
| -rw-r--r-- | include/fbos/string.h | 8 | ||||
| -rw-r--r-- | kernel/dt.c | 6 | ||||
| -rw-r--r-- | kernel/fbos.ld.S | 12 | ||||
| -rw-r--r-- | kernel/head.S | 55 | ||||
| -rw-r--r-- | kernel/main.c | 18 | ||||
| -rw-r--r-- | kernel/printk.c | 23 | ||||
| -rw-r--r-- | kernel/sbi.c | 29 | ||||
| -rw-r--r-- | kernel/strlen.S | 22 |
17 files changed, 281 insertions, 24 deletions
@@ -18,16 +18,15 @@ endif # no `-mcpu`, no `-mtune`, no funny business. CC = $(CROSS_COMPILE)gcc$(CC_SUFFIX) -AS = $(CROSS_COMPILE)as$(CC_SUFFIX) LD = $(CROSS_COMPILE)ld QEMU ?= qemu-system-riscv64 ISA ?= rv64imafdc_zicntr_zicsr_zifencei_zihpm_zca_zcd_zba_zbb -ASFLAGS = -march=$(ISA) -mabi=lp64d +ASFLAGS = -march=$(ISA) -mabi=lp64d -mcmodel=medany CCFLAGS = $(ASFLAGS) -Iinclude/ CCFLAGS += -Werror -Wpedantic -Wall -Wextra -Wcast-align -Wcast-qual -Winit-self \ -Wmissing-include-dirs -Wredundant-decls -Wshadow -Wsign-conversion \ - -Wswitch-default -Wundef -Wunreachable-code -Wmissing-noreturn \ + -Wswitch-default -Wundef -Wunreachable-code \ -nostdinc -nostdlib -std=gnu17 LDFLAGS = -Iinclude/ -static -melf64lriscv -z noexecstack USRFLAGS = -static -melf64lriscv @@ -48,6 +47,7 @@ ifeq ($(strip $(DEBUG)),) CCFLAGS += -O3 QEMU_FLAGS += -nographic else + ASFLAGS += -g CCFLAGS += -g QEMU_FLAGS += -s -S endif @@ -55,7 +55,7 @@ endif ## # Paths -SRC = $(wildcard kernel/head.S kernel/*.c) +SRC = $(filter-out kernel/fbos.ld.S, $(wildcard kernel/*.S kernel/*.c)) OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SRC))) LINKER = kernel/fbos.ld KRNL = fbos @@ -83,7 +83,7 @@ $(KRNL): $(OBJ) $(LINKER).S .S.o: $(E) " CC " $(*F) - $(Q) $(CC) $(CCFLAGS) -c $< -o $@ + $(Q) $(CC) $(CCFLAGS) -D__ASSEMBLY__ -c $< -o $@ ## # User space @@ -94,8 +94,8 @@ usr: $(USR) $(Q) find usr/bin/ -type f -executable | cpio -o --quiet -H newc > $(INIT) usr/src/%.o: usr/src/%.S - $(E) " AS " $(basename $@) - $(Q) $(AS) $(ASFLAGS) -c $< -o $@ + $(E) " CC " $(basename $@) + $(Q) $(CC) $(ASFLAGS) -D__ASSEMBLY__ -c $< -o $@ usr/bin/%: usr/src/%.o $(Q) mkdir -p usr/bin/ @@ -104,6 +104,19 @@ $ make gdb GDB_EXTRA_FLAGS="-tui" And now you have started a GDB session with a nice TUI interface. +## Requirements + +We do not want to support a myriad of different scenarios, but we want to keep +things simple. Hence, here there are some limitations/requirements: + +- You need a recent enough OpenSBI running on your firmware. I have tested this + on a QEMU which has OpenSBI v1.5, but any firmware that implements a Runtime + SBI version of 2.0 should be fine. +- You are supposed to pass an `initrd` always. This kernel will not try to + magically come up with a made up file system or try to fetch something from an + existing one. An `initrd` is already provided for you on the default `make` + target, and that's what you are supposed to be passing to the kernel. + ## Special thanks to SUSE for organizing [Hack Week 24](https://hackweek.opensuse.org/24/projects). diff --git a/include/fbos/compiler.h b/include/fbos/compiler.h index edf24d1..5e61ebd 100644 --- a/include/fbos/compiler.h +++ b/include/fbos/compiler.h @@ -1,9 +1,30 @@ #ifndef __FBOS_COMPILER_H #define __FBOS_COMPILER_H +/* + * Nicer looking versions of compiler attributes. + */ + #define __noreturn __attribute__((__noreturn__)) +/* + * Compiler attributes specific to linker sections. + */ + #define __section(s) __attribute__((__section__(s))) -#define __kernel __section(".text.kernel") +#define __kernel __section(".kernel.text") + +/* + * Multiple aliases for 64-bit integers which have their definition on the + * standard library. + */ + +typedef long ssize_t; +typedef unsigned long size_t; +typedef unsigned long uint64_t; +typedef unsigned long uintptr_t; + +// Helpful macro when prototyping. +#define __unused(x) (void)x #endif /* __FBOS_COMPILER_H */ diff --git a/include/fbos/dt.h b/include/fbos/dt.h new file mode 100644 index 0000000..39d8381 --- /dev/null +++ b/include/fbos/dt.h @@ -0,0 +1,8 @@ +#ifndef __FBOS_DT_H_ +#define __FBOS_DT_H_ + +#include <fbos/compiler.h> + +void parse_dtb(uint64_t *dtb); + +#endif // __FBOS_DT_H_ diff --git a/include/fbos/init.h b/include/fbos/init.h index 1eeb33a..a919405 100644 --- a/include/fbos/init.h +++ b/include/fbos/init.h @@ -1,8 +1,11 @@ -#ifndef FBOS_INIT_H -#define FBOS_INIT_H +#ifndef __FBOS_INIT_H +#define __FBOS_INIT_H #include <fbos/compiler.h> -extern __noreturn __kernel void start_kernel(void); +extern struct task_struct init_task; -#endif /* FBOS_INIT_H */ +// The entry point for the kernel. +__noreturn __kernel void start_kernel(uintptr_t *dtb); + +#endif /* __FBOS_INIT_H */ diff --git a/include/fbos/mm.h b/include/fbos/mm.h index 2b80076..25d18ce 100644 --- a/include/fbos/mm.h +++ b/include/fbos/mm.h @@ -1,5 +1,5 @@ -#ifndef FBOS_MM_H -#define FBOS_MM_H +#ifndef __FBOS_MM_H +#define __FBOS_MM_H /* * Page = 4KB. @@ -7,10 +7,17 @@ #define PAGE_SIZE 0x1000 /* + * Initial size of the thread, which coincides with the size of the stack for a + * given thread. + */ +#define THREAD_SIZE_ORDER 2 +#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) + +/* * The code will be linked to start at the first page, which will have a given * offset. */ #define PAGE_OFFSET 0x80200000 #define LINK_ADDR PAGE_OFFSET -#endif /* FBOS_MM_H */ +#endif /* __FBOS_MM_H */ diff --git a/include/fbos/printk.h b/include/fbos/printk.h new file mode 100644 index 0000000..2c82eda --- /dev/null +++ b/include/fbos/printk.h @@ -0,0 +1,7 @@ +#ifndef __FBOS_PRINTK_H_ +#define __FBOS_PRINTK_H_ + +extern void die(const char *const message); +extern void printk(const char *const message); + +#endif // __FBOS_PRINTK_H_ diff --git a/include/fbos/sbi.h b/include/fbos/sbi.h new file mode 100644 index 0000000..a6b893d --- /dev/null +++ b/include/fbos/sbi.h @@ -0,0 +1,35 @@ +#ifndef __FBOS_SBI_H_ +#define __FBOS_SBI_H_ + +// TODO +/* SBI_SUCCESS 0 Completed successfully */ +/* SBI_ERR_FAILED -1 Failed */ +/* SBI_ERR_NOT_SUPPORTED -2 Not supported */ +/* SBI_ERR_INVALID_PARAM -3 Invalid parameter(s) */ +/* SBI_ERR_DENIED -4 Denied or not allowed */ +/* SBI_ERR_INVALID_ADDRESS -5 Invalid address(s) */ +/* SBI_ERR_ALREADY_AVAILABLE -6 Already available */ +/* SBI_ERR_ALREADY_STARTED -7 Already started */ +/* SBI_ERR_ALREADY_STOPPED -8 Already stopped */ +/* SBI_ERR_NO_SHMEM -9 Shared memory not available */ + +enum sbi_ext { + DBCN_EXT = 0x4442434E, +}; + +enum dbcn_actions { + DBCN_WRITE = 0x00, +}; + +struct sbi_ret { + long error; + long value; +}; + +extern struct sbi_ret __sbi_ecall(unsigned long arg0, unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, unsigned long arg5, + int fid, int ext); + +#define sbi_ecall2(ext, fid, arg0, arg1) __sbi_ecall(arg0, arg1, 0, 0, 0, 0, fid, ext) + +#endif // __FBOS_SBI_H_ diff --git a/include/fbos/sched.h b/include/fbos/sched.h new file mode 100644 index 0000000..626f72f --- /dev/null +++ b/include/fbos/sched.h @@ -0,0 +1,8 @@ +#ifndef __FBOS_SCHED_H_ +#define __FBOS_SCHED_H_ + +struct task_struct { + void *stack; +}; + +#endif // __FBOS_SCHED_H_ diff --git a/include/fbos/string.h b/include/fbos/string.h new file mode 100644 index 0000000..c3da266 --- /dev/null +++ b/include/fbos/string.h @@ -0,0 +1,8 @@ +#ifndef __FBOS_STRING_H_ +#define __FBOS_STRING_H_ + +#include <fbos/compiler.h> + +extern size_t strlen(const char *); + +#endif // __FBOS_STRING_H_ diff --git a/kernel/dt.c b/kernel/dt.c new file mode 100644 index 0000000..55fa970 --- /dev/null +++ b/kernel/dt.c @@ -0,0 +1,6 @@ +#include <fbos/dt.h> + +__kernel void parse_dtb(uint64_t *dtb) +{ + __unused(dtb); +} diff --git a/kernel/fbos.ld.S b/kernel/fbos.ld.S index f288161..f74d90f 100644 --- a/kernel/fbos.ld.S +++ b/kernel/fbos.ld.S @@ -3,15 +3,15 @@ SECTIONS { // Ensure that the image starts at the very exact address SBI expects it to. . = LINK_ADDR; + _start = .; + . = ALIGN(PAGE_SIZE); // You would usually want to separate the head section into its own thing // instead of clumping it into the main `.text` one. Well, I'm no expert on // linker configuration, so patches are welcome :) .text : { - // The very first thing has to be the `_start` function, which is where - // SBI will jump into. Afterwards comes the rest of `.text.head`. - _start = .; - *(.text.head) + _text = .; + *(.head.text) // Aligning it to a full page is maybe a bit too much considering how // small `.text.head` really is. I just saw this same thing on the Linux @@ -23,7 +23,7 @@ SECTIONS { // that large. That is, we put first the very core of the kernel, and // the rest can go wherever. __kernel_text_start = .; - *(.text.kernel) + *(.kernel.text) __kernel_text_end = .; // And the rest. @@ -49,4 +49,6 @@ SECTIONS { .bss : { *(.bss) } + + _end = .; } diff --git a/kernel/head.S b/kernel/head.S index ba155cb..de7852b 100644 --- a/kernel/head.S +++ b/kernel/head.S @@ -1,6 +1,55 @@ +#include <fbos/mm.h> + .global _start -.section .text.head +.section .head.text _start: - // TODO - call start_kernel + // Mask all interrupts + csrw sie, zero + csrw sip, zero + + // Flush the instruction cache + fence.i + + // Reset all registers except ra, a0, a1. + li sp, 0 + li gp, 0 + li tp, 0 + li t0, 0 + li t1, 0 + li t2, 0 + li s0, 0 + li s1, 0 + li a2, 0 + li a3, 0 + li a4, 0 + li a5, 0 + li a6, 0 + li a7, 0 + li s2, 0 + li s3, 0 + li s4, 0 + li s5, 0 + li s6, 0 + li s7, 0 + li s8, 0 + li s9, 0 + li s10, 0 + li s11, 0 + li t3, 0 + li t4, 0 + li t5, 0 + li t6, 0 + csrw sscratch, 0 + + // Point tp and sp to the init task. + la tp, init_task + la sp, init_task + THREAD_SIZE + + // The `start_kernel` function requires an argument to be passed, which is + // the pointer to the `fdt` blob. The bootloader puts this on the `a1` + // register, so let's move it now to `a0`. + mv a0, a1 + + // Start the kernel. + tail start_kernel diff --git a/kernel/main.c b/kernel/main.c index 325f858..07188b9 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -1,12 +1,28 @@ #include <fbos/init.h> +#include <fbos/printk.h> +#include <fbos/mm.h> +#include <fbos/sched.h> +#include <fbos/dt.h> + +unsigned long init_stack[THREAD_SIZE / sizeof(unsigned long)]; + +struct task_struct init_task = { .stack = init_stack }; /* * This is the main entry point of the kernel after head.S is done. This * function can (and will) assume that everything has been reset and that we can * start the whole thing. */ -__noreturn __kernel void start_kernel(void) +__noreturn __kernel void start_kernel(uintptr_t *dtb) { + // TODO: disable irqs, etc. + + printk("Welcome to FizzBuzz OS!\n"); + + parse_dtb(dtb); + + // TODO: reenable stuff + for (;;) ; } diff --git a/kernel/printk.c b/kernel/printk.c new file mode 100644 index 0000000..c0eedd9 --- /dev/null +++ b/kernel/printk.c @@ -0,0 +1,23 @@ +#include <fbos/printk.h> +#include <fbos/string.h> +#include <fbos/sbi.h> + +void __noreturn __kernel die(const char *const message) +{ + if (message) { + printk(message); + } + + for (;;) + ; +} + +void __kernel printk(const char *const message) +{ + size_t len = strlen(message); + if (!len) { + return; + } + + sbi_ecall2(DBCN_EXT, DBCN_WRITE, len, (unsigned long)message); +} diff --git a/kernel/sbi.c b/kernel/sbi.c new file mode 100644 index 0000000..ff7556c --- /dev/null +++ b/kernel/sbi.c @@ -0,0 +1,29 @@ +#include <fbos/compiler.h> +#include <fbos/sbi.h> + +/* Implementation taken from the Linux kernel (6.12) */ +__kernel struct sbi_ret __sbi_ecall(unsigned long arg0, unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, unsigned long arg5, + int fid, int ext) +{ + struct sbi_ret ret; + + register uintptr_t a0 asm("a0") = (uintptr_t)(arg0); + register uintptr_t a1 asm("a1") = (uintptr_t)(arg1); + register uintptr_t a2 asm("a2") = (uintptr_t)(arg2); + register uintptr_t a3 asm("a3") = (uintptr_t)(arg3); + register uintptr_t a4 asm("a4") = (uintptr_t)(arg4); + register uintptr_t a5 asm("a5") = (uintptr_t)(arg5); + register uintptr_t a6 asm("a6") = (uintptr_t)(fid); + register uintptr_t a7 asm("a7") = (uintptr_t)(ext); + + asm volatile("ecall" + : "+r"(a0), "+r"(a1) + : "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6), "r"(a7) + : "memory"); + + ret.error = (long)a0; + ret.value = (long)a1; + + return ret; +} diff --git a/kernel/strlen.S b/kernel/strlen.S new file mode 100644 index 0000000..6185b94 --- /dev/null +++ b/kernel/strlen.S @@ -0,0 +1,22 @@ +.globl strlen +.type strlen, @function + +/* + * Defined in include/fbos/string.h + * + * size_t strlen(const char *str) + * + * Returns (a0): string length. + * Parameter (a0): string to measure. + * Clobbers: t0, t1. + */ +strlen: + mv t1, a0 +1: + lbu t0, 0(t1) + beqz t0, 2f + addi t1, t1, 1 + j 1b +2: + sub a0, t1, a0 + ret |
