aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile58
-rw-r--r--include/fbos/compiler.h12
-rw-r--r--include/fbos/dt.h9
-rw-r--r--include/fbos/init.h2
-rw-r--r--include/fbos/printk.h17
-rw-r--r--include/fbos/string.h1
-rw-r--r--kernel/dt.c6
-rw-r--r--kernel/main.c5
-rw-r--r--kernel/string.S46
-rw-r--r--kernel/strlen.S22
-rw-r--r--lib/dt.c126
-rw-r--r--test/test_dt.c29
-rw-r--r--test/testdata/qemu.dtbbin0 -> 4864 bytes
14 files changed, 285 insertions, 52 deletions
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/
@@ -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
new file mode 100644
index 0000000..cd3d066
--- /dev/null
+++ b/test/testdata/qemu.dtb
Binary files differ