diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | Makefile | 58 | ||||
| -rw-r--r-- | include/fbos/compiler.h | 12 | ||||
| -rw-r--r-- | include/fbos/dt.h | 9 | ||||
| -rw-r--r-- | include/fbos/init.h | 2 | ||||
| -rw-r--r-- | include/fbos/printk.h | 17 | ||||
| -rw-r--r-- | include/fbos/string.h | 1 | ||||
| -rw-r--r-- | kernel/dt.c | 6 | ||||
| -rw-r--r-- | kernel/main.c | 5 | ||||
| -rw-r--r-- | kernel/string.S | 46 | ||||
| -rw-r--r-- | kernel/strlen.S | 22 | ||||
| -rw-r--r-- | lib/dt.c | 126 | ||||
| -rw-r--r-- | test/test_dt.c | 29 | ||||
| -rw-r--r-- | test/testdata/qemu.dtb | bin | 0 -> 4864 bytes |
14 files changed, 285 insertions, 52 deletions
@@ -2,8 +2,12 @@ kernel/fbos.ld /fbos .cache + usr/bin/* usr/initramfs.cpio +test/*.o +test/test_dt + # You can generate it with Bear: `$ bear -- make`. compile_commands.json @@ -17,19 +17,20 @@ endif # I did not go too much into the rabbit hole of platform-specific flags. Hence # no `-mcpu`, no `-mtune`, no funny business. -CC = $(CROSS_COMPILE)gcc$(CC_SUFFIX) -LD = $(CROSS_COMPILE)ld +CC = $(CROSS_COMPILE)gcc$(CC_SUFFIX) +LD = $(CROSS_COMPILE)ld +HOSTCC = gcc QEMU ?= qemu-system-riscv64 -ISA ?= rv64imafdc_zicntr_zicsr_zifencei_zihpm_zca_zcd_zba_zbb -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 \ - -nostdinc -nostdlib -std=gnu17 -LDFLAGS = -Iinclude/ -static -melf64lriscv -z noexecstack -USRFLAGS = -static -melf64lriscv +ISA ?= rv64imafdc_zicntr_zicsr_zifencei_zihpm_zca_zcd_zba_zbb +ASFLAGS = -march=$(ISA) -mabi=lp64d -mcmodel=medany +CCFLAGS = $(ASFLAGS) -Iinclude/ -D__KERNEL__ -std=gnu17 -nostdinc -nostdlib +WARNINGS = -Werror -Wpedantic -Wall -Wextra -Wcast-align -Wcast-qual -Winit-self \ + -Wmissing-include-dirs -Wredundant-decls -Wshadow -Wsign-conversion \ + -Wswitch-default -Wundef -Wunreachable-code +CCFLAGS += $(WARNINGS) +LDFLAGS = -Iinclude/ -static -melf64lriscv -z noexecstack +USRFLAGS = -static -melf64lriscv ## # Optional parameters for QEMU and gdb. @@ -55,12 +56,13 @@ endif ## # Paths -SRC = $(filter-out kernel/fbos.ld.S, $(wildcard kernel/*.S kernel/*.c)) +SRC = $(filter-out kernel/fbos.ld.S, $(wildcard kernel/*.S kernel/*.c lib/*.c)) OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SRC))) LINKER = kernel/fbos.ld KRNL = fbos USR = usr/bin/foo INIT = usr/initramfs.cpio +TESTS = test/test_dt LDFLAGS += -T $(LINKER) @@ -68,7 +70,7 @@ LDFLAGS += -T $(LINKER) # Kernel .PHONY: all -all: clean $(KRNL) usr +all: clean $(KRNL) usr test .PHONY: $(KRNL) $(KRNL): $(OBJ) $(LINKER).S @@ -78,12 +80,12 @@ $(KRNL): $(OBJ) $(LINKER).S $(Q) $(LD) $(LDFLAGS) $(OBJ) -o $(KRNL) .c.o: - $(E) " CC " $(*F) - $(Q) $(CC) $(CCFLAGS) -c $< -o $@ + $(E) " CC " $(basename $@) + $(Q) $(CC) $(CCFLAGS) $(KRNLFLAGS) -c $< -o $@ .S.o: - $(E) " CC " $(*F) - $(Q) $(CC) $(CCFLAGS) -D__ASSEMBLY__ -c $< -o $@ + $(E) " CC " $(basename $@) + $(Q) $(CC) $(CCFLAGS) -D__ASSEMBLY__ -D__KERNEL__ -c $< -o $@ ## # User space @@ -95,7 +97,7 @@ usr: $(USR) usr/src/%.o: usr/src/%.S $(E) " CC " $(basename $@) - $(Q) $(CC) $(ASFLAGS) -D__ASSEMBLY__ -c $< -o $@ + $(Q) $(CC) $(ASFLAGS) -D__ASSEMBLY__ -D__KERNEL__ -c $< -o $@ usr/bin/%: usr/src/%.o $(Q) mkdir -p usr/bin/ @@ -106,6 +108,24 @@ usr/bin/%: usr/src/%.o .SECONDARY: ## +# Tests + +.PHONY: test +test: host_lib $(TESTS) + $(Q) ./test/test_dt + +host_lib: + $(Q) $(HOSTCC) $(WARNINGS) -Iinclude/ -g -c lib/dt.c -o lib/dt.o + +test/%.o: test/%.c + $(E) " HOSTCC " $(basename $@) + $(Q) $(HOSTCC) $(WARNINGS) -g -Iinclude/ -c $< -o $@ + +test/%: test/%.o + $(E) " HOSTLD " $@ + $(Q) $(HOSTCC) -Iinclude/ $< lib/dt.o -o $@ + +## # Hacking .PHONY: qemu @@ -122,7 +142,7 @@ gdb: .PHONY: clean clean: - $(Q) rm -f $(OBJ) $(KRNL) $(LINKER) $(USR) usr/src/*.o $(INIT) + $(Q) rm -f $(OBJ) $(KRNL) $(LINKER) $(USR) usr/src/*.o $(INIT) test/*.o $(TESTS) .PHONY: lint lint: diff --git a/include/fbos/compiler.h b/include/fbos/compiler.h index 35091c0..b88bb0b 100644 --- a/include/fbos/compiler.h +++ b/include/fbos/compiler.h @@ -11,16 +11,24 @@ * Compiler attributes specific to linker sections. */ +#ifdef __KERNEL__ #define __section(s) __attribute__((__section__(s))) #define __kernel __section(".kernel.text") +#else +#define __kernel +#endif /* __KERNEL__ */ /* - * Multiple aliases for 64-bit integers which have their definition on the + * Multiple aliases for 32/64-bit integers which have their definition on the * standard library. */ +typedef int int32_t; typedef long ssize_t; +typedef long int64_t; + typedef unsigned long size_t; +typedef unsigned int uint32_t; typedef unsigned long uint64_t; typedef unsigned long uintptr_t; @@ -28,8 +36,10 @@ typedef unsigned long uintptr_t; * NULL */ +#ifdef __KERNEL__ #define NULL (void *)0 #define nullptr NULL +#endif /* __KERNEL__ */ // Helpful macro when prototyping. #define __unused(x) (void)x diff --git a/include/fbos/dt.h b/include/fbos/dt.h index 39d8381..fd77c35 100644 --- a/include/fbos/dt.h +++ b/include/fbos/dt.h @@ -3,6 +3,13 @@ #include <fbos/compiler.h> -void parse_dtb(uint64_t *dtb); +// Pair of addresses where the initrd is located in memory. +struct initrd_addr { + uintptr_t start; + uintptr_t end; +}; + +// Returns the `initrd` addresses as parsed from the given DTB blob. +struct initrd_addr find_dt_initrd_addr(uint32_t *dtb); #endif // __FBOS_DT_H_ diff --git a/include/fbos/init.h b/include/fbos/init.h index a919405..eb72508 100644 --- a/include/fbos/init.h +++ b/include/fbos/init.h @@ -6,6 +6,6 @@ extern struct task_struct init_task; // The entry point for the kernel. -__noreturn __kernel void start_kernel(uintptr_t *dtb); +__noreturn __kernel void start_kernel(void *dtb); #endif /* __FBOS_INIT_H */ diff --git a/include/fbos/printk.h b/include/fbos/printk.h index 2c82eda..6b32555 100644 --- a/include/fbos/printk.h +++ b/include/fbos/printk.h @@ -1,7 +1,24 @@ #ifndef __FBOS_PRINTK_H_ #define __FBOS_PRINTK_H_ +/* + * This file might be pulled from user space tests. Hence, define alternatives + * for this functions from glibc. + */ + +#ifdef __KERNEL__ extern void die(const char *const message); extern void printk(const char *const message); +#else +#include <stdio.h> +#include <stdlib.h> + +#define die(x) \ + do { \ + printf(x); \ + exit(1); \ + } while (0) +#define printk(x) printf(x) +#endif /* __KERNEL */ #endif // __FBOS_PRINTK_H_ diff --git a/include/fbos/string.h b/include/fbos/string.h index c3da266..af7e2ef 100644 --- a/include/fbos/string.h +++ b/include/fbos/string.h @@ -4,5 +4,6 @@ #include <fbos/compiler.h> extern size_t strlen(const char *); +extern int strcmp(const char *, const char *); #endif // __FBOS_STRING_H_ diff --git a/kernel/dt.c b/kernel/dt.c deleted file mode 100644 index 55fa970..0000000 --- a/kernel/dt.c +++ /dev/null @@ -1,6 +0,0 @@ -#include <fbos/dt.h> - -__kernel void parse_dtb(uint64_t *dtb) -{ - __unused(dtb); -} diff --git a/kernel/main.c b/kernel/main.c index 07188b9..11f2721 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -13,13 +13,14 @@ struct task_struct init_task = { .stack = init_stack }; * function can (and will) assume that everything has been reset and that we can * start the whole thing. */ -__noreturn __kernel void start_kernel(uintptr_t *dtb) +__noreturn __kernel void start_kernel(void *dtb) { // TODO: disable irqs, etc. printk("Welcome to FizzBuzz OS!\n"); - parse_dtb(dtb); + struct initrd_addr addr = find_dt_initrd_addr(dtb); + __unused(addr); // TODO // TODO: reenable stuff diff --git a/kernel/string.S b/kernel/string.S new file mode 100644 index 0000000..d09bbac --- /dev/null +++ b/kernel/string.S @@ -0,0 +1,46 @@ +/* + * Defined in include/fbos/string.h + * + * size_t strlen(const char *str) + * + * Returns (a0): string length. + * Parameter (a0): string to measure. + * Clobbers: t0, t1. + */ +.globl strlen +.type strlen, @function +strlen: + mv t1, a0 +1: + lbu t0, 0(t1) + beqz t0, 2f + addi t1, t1, 1 + j 1b +2: + sub a0, t1, a0 + ret + +/* + * Defined in include/fbos/string.h + * + * int strcmp(const char *s1, const char *s2) + * + * Returns (a0): comparison result as in stdlib. + * Parameter (a0, a1): strings to compare. + * Clobbers: t0, t1. + */ +.globl strcmp +.type strcmp, @function +strcmp: +1: + lbu t0, 0(a0) + lbu t1, 0(a1) + bne t0, t1, 2f + addi a0, a0, 1 + addi a1, a1, 1 + bnez t0, 1b + li a0, 0 + ret +2: + sub a0, t0, t1 + ret diff --git a/kernel/strlen.S b/kernel/strlen.S deleted file mode 100644 index 6185b94..0000000 --- a/kernel/strlen.S +++ /dev/null @@ -1,22 +0,0 @@ -.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 diff --git a/lib/dt.c b/lib/dt.c new file mode 100644 index 0000000..3aab330 --- /dev/null +++ b/lib/dt.c @@ -0,0 +1,126 @@ +#include <fbos/dt.h> +#include <fbos/printk.h> +#include <fbos/string.h> + +/* + * Swap bytes in 32-bit value. Taken from glibc. + */ +#define __bswap_constant_32(x) \ + ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | (((x) & 0x0000ff00u) << 8) | \ + (((x) & 0x000000ffu) << 24)) + +// Magic number in little-endian format identifying a valid FDT structure. +#define FDT_MAGIC_LE 0xedfe0dd0 + +// Token representing the beginning of node on the structure block in +// little-endian format. +#define FDT_BEGIN_NODE_LE 0x01000000 + +// Token representing the beginning of a property on the structure block in +// little-endian format. +#define FDT_PROP_LE 0x03000000 + +// All the relevant information we need from the FDT header. +struct fdt_header { + uint32_t off_dt_struct; + uint32_t off_dt_string; + uint32_t size_dt_struct; +}; + +/* + * Find the device tree property by "name" starting at the given index "idx". + * This function assumes that the property is exactly 8 bytes long (hey, it's + * not so general purpose after all :D). + * + * Returns -1 if the given property could not be found. + */ +__kernel int64_t find_dt_property_from(uint32_t *dtb, struct fdt_header *header, uint32_t idx, + const char *const name) +{ + char *base_dt_string = ((char *)dtb) + header->off_dt_string; + uint32_t len, nameoff; + int64_t ret; + + while (dtb[idx] == FDT_PROP_LE) { + len = __bswap_constant_32(dtb[idx + 1]); + nameoff = __bswap_constant_32(dtb[idx + 2]); + + if (len == 8 && strcmp(&base_dt_string[nameoff], name) == 0) { + ret = (int64_t)__bswap_constant_32(dtb[idx + 3]) << 32; + ret += (int64_t)__bswap_constant_32(dtb[idx + 4]); + return ret; + } + + // Length of the data + original FDT_PROP_LE + len + nameoff. + idx += (len / sizeof(uint32_t)) + 3; + } + return -1; +} + +// Find the "initrd" values from the given DTB blob. Returns an empty +// `initrd_addr` if these values could not be found. +__kernel struct initrd_addr __find_dt_initrd_addr(uint32_t *dtb, struct fdt_header *header) +{ + uint32_t idx; + struct initrd_addr ret = { + .start = 0, + .end = 0, + }; + + // Try to find out the 32-bit offset of the "chosen" property inside of the + // FDT structure block. + for (idx = header->off_dt_struct; idx < header->size_dt_struct; idx++) { + /* + * We only care about beginning of nodes, and then that the block is + * literally named "chosen". After that, our offset will be that + 3 + * (skipping FDT_BEGIN_NODE + 2 that takes "chosen" with padding for + * alignment). + */ + if (dtb[idx] == FDT_BEGIN_NODE_LE) { + if (strcmp((char *)&dtb[idx + 1], "chosen") == 0) { + idx += 3; + break; + } + } + } + + // "chosen" property could not be found. Leave early with an empty result. + if (idx == header->size_dt_struct || dtb[idx] != FDT_PROP_LE) { + return ret; + } + + /* + * Now that we have the index of the "chosen" property, fetch the "initrd-*" + * values and return that. + */ + + int64_t i = find_dt_property_from(dtb, header, idx, "linux,initrd-start"); + if (i < 0) { + return ret; + } + ret.start = (uintptr_t)i; + + i = find_dt_property_from(dtb, header, idx, "linux,initrd-end"); + if (i < 0) { + ret.start = 0; + return ret; + } + ret.end = (uintptr_t)i; + + return ret; +} + +__kernel struct initrd_addr find_dt_initrd_addr(uint32_t *dtb) +{ + if (dtb[0] != FDT_MAGIC_LE) { + die("FDT structure does not have a valid magic identifier\n"); + } + + struct fdt_header header = { + .off_dt_struct = __bswap_constant_32(dtb[2]) / sizeof(uint32_t), + .off_dt_string = __bswap_constant_32(dtb[3]), + .size_dt_struct = __bswap_constant_32(dtb[9]) / sizeof(uint32_t), + }; + + return __find_dt_initrd_addr(dtb, &header); +} diff --git a/test/test_dt.c b/test/test_dt.c new file mode 100644 index 0000000..31f729f --- /dev/null +++ b/test/test_dt.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include <fbos/dt.h> + +int main(void) +{ + FILE *fh = fopen("./test/testdata/qemu.dtb", "rb"); + assert(fh); + + fseek(fh, 0, SEEK_END); + long fsize = ftell(fh); + rewind(fh); + + uint32_t *contents = malloc((unsigned long)fsize + 1); + fread(contents, (unsigned long)fsize, sizeof(uint32_t *), fh); + fclose(fh); + + contents[fsize] = 0; + + struct initrd_addr addr = find_dt_initrd_addr(contents); + free(contents); + + assert(addr.start == 0x84200000); + assert(addr.end == 0x84200c00); + + exit(0); +} diff --git a/test/testdata/qemu.dtb b/test/testdata/qemu.dtb Binary files differnew file mode 100644 index 0000000..cd3d066 --- /dev/null +++ b/test/testdata/qemu.dtb |
