From fb9627cc985912a57de8b4bedc98991e290e4075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miquel=20Sabat=C3=A9=20Sol=C3=A0?= Date: Wed, 20 Nov 2024 11:43:08 +0100 Subject: [PATCH] Fetch the initrd address from the DTB blob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel entry has a pointer to the DTB blob as a parameter. From this pointer we can parse the the DTB blob to find the properties "chosen->linux,initrd-start" and "chosen->linux,initrd-start". These properties are guaranteed to have 64-bit addresses which point where the initrd is in memory, which we need to fetch the binaries to be loaded. Signed-off-by: Miquel Sabaté Solà --- .gitignore | 4 ++ Makefile | 58 ++++++++++++------ include/fbos/compiler.h | 12 +++- include/fbos/dt.h | 9 ++- include/fbos/init.h | 2 +- include/fbos/printk.h | 17 ++++++ include/fbos/string.h | 1 + kernel/dt.c | 6 -- kernel/main.c | 5 +- kernel/string.S | 46 +++++++++++++++ kernel/strlen.S | 22 ------- lib/dt.c | 126 ++++++++++++++++++++++++++++++++++++++++ test/test_dt.c | 29 +++++++++ test/testdata/qemu.dtb | Bin 0 -> 4864 bytes 14 files changed, 285 insertions(+), 52 deletions(-) delete mode 100644 kernel/dt.c create mode 100644 kernel/string.S delete mode 100644 kernel/strlen.S create mode 100644 lib/dt.c create mode 100644 test/test_dt.c create mode 100644 test/testdata/qemu.dtb diff --git a/.gitignore b/.gitignore index d87610d..dc7f65d 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Makefile b/Makefile index b793ba8..2a2751d 100644 --- a/Makefile +++ b/Makefile @@ -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/ @@ -105,6 +107,24 @@ usr/bin/%: usr/src/%.o # HACK: do not remove object files from usr/src/. .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 @@ -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 -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 +#include + +#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 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 - -__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 +#include +#include + +/* + * 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 +#include +#include + +#include + +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 new file mode 100644 index 0000000000000000000000000000000000000000..cd3d0661617438e67eefe7a1bd0039c22df35af9 GIT binary patch literal 4864 zcmbVQJ&YVR6!wI$M}WZbE20Q=Aq~Y2*@N6Akq}xWprN545)^2eo$=ldnZJx@_qeMf z1cC}dgAf#m0;H656cG&_X;M%lXb?gmC_w_k_dVNlv&)4jJjwHZ{QSM=x3*`zR}TOE zi&FO}&}pSse*=F6?Jl%cG|&O?)i12P@C=-G6Zk>22hk3od7T(uKMc87rzW0<^R#Z# zQk~bi^Sv8fb^H40o6)FLP?g)dE+-SKdhFn9rOuv(&BY47TI#<9ojI|e^XQo!6PHEU zg=20(t;c2AK-$9s_V#v1|1a!2;ODU&)L;OcU{_hxOv*YB$1^iLIS@i0$>*Io+2`4p zUObpQK|sz|>dTsYo%Qwl5y(B3t?z>>ljc>X8C&VRM8D*eL#}s*r)@S&Rq$2-wPO2d z5*zFG1eFVXfNrr&aN-KMmV7?^{e3=6pZSq|f1^Hl{{r*?V}}yC?F+a7J7a?t_`uYT zl{}n&GhNGb(T>xJ-(!v~o-A&!owF0)M6u#?<+3b>5@x}|v3 z_1y_>*JB?mIRsMN`eAR#dgng!je8=@qY5VMk8EZT*=-T%Wv!R?%Tx60jBjx&dvY>A z?wJ>zU!Yd5{?YocT>TTHk4Rg&`aAehX;wZ4`563vhd*&{MRRi@x@5w5!DKs(CtJhy z!FqeN(Vz4j8s$|q*}JQhEIk0mKGxozSr#bFbG#S!j=o@$MP@pDtaDWPx~rk{BhDI{ z#GpW8a-@eX^E!QHF7YG{1>265xx~}b=t2h24@X^amD(F?x089>&(_AL*zSYhcPC64 zpD?;kqYTBcq%ObnvGZ92fBen2e?9Z`CuctUe&f>VZ!fPr`PZq}Pdp2s&tLuY{H#~L z3?In@P!QXB{?|K|Yqr-p2Gt`TQXeg(#JXW z3bVesu`!68P3qQY)41Y$U*ht;0=|eVv!s_5nA0&d)N+!}b)Qh{czw3YK-sSc`0y1d z@$6U3h)Y(&@`Z{%lAj~zOnskE1+Y&){N1_a%B%#HnH;yd7aF`t)+|0gCt*W6$~FaDl>EpcsK-T*-2O5QlX2++LE0yh<9wsP8-|9 zOZJ1#e!RVfSO<>}93HhTd3@k3*ggI-j>lhfYs&+9eBiLGEqQ$4EZ9B%+LGPlBhQ7} zd3@k3*gZaQ7VI8>8OP%zPpRJv;C+I3iGzjE!2qP8JQun1BPMB?vzwEaeKMd2$W_6I z@ZWzZe(;|4Q2d@^Kk&F#guK3j^+p5r`_X#PLwu4op~m$h+|mWUEn?g6#>n@huM;tu zimxYg=C>1l>Eqj>yi9ba;xezICLL#59XIK&4r85VMjelmq}IkbwT_A{ZB$)sg+}W{ z)p|>fqvBk+ohHpxC3%F4!IW36#_euBha5I#nByknt{_G=X|BhS(cz@l=V!PF?kL=G z!e*z^Zd_`jJt?GbN7b)2x-e;3AQk?D(&sfS9A{;GE@bk1*4=%rRo7~osKL!W(epIc zBT1o}Mn!^3?0elg-myxJcrkBnCY0n77DrMm0KC^7R*@8E&tKs