aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--README.md18
-rwxr-xr-xarch/riscv64/conf/mkimage.sh4
-rw-r--r--arch/riscv64/init/init.c17
-rw-r--r--arch/riscv64/init/start.S20
-rw-r--r--arch/riscv64/kernel/vmem.c3
-rw-r--r--common/debug.c25
-rw-r--r--common/pmem.c2
-rw-r--r--common/string.c136
-rw-r--r--include/kmi/debug.h4
-rw-r--r--include/kmi/string.h15
11 files changed, 233 insertions, 16 deletions
diff --git a/Makefile b/Makefile
index 06ef283..92a275b 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,9 @@ DEBUGFLAGS != [ $(RELEASE) ] \
&& echo "-flto -O2 -DNDEBUG" \
|| echo "-O0 -DDEBUG"
+UBOOTFLAGS != [ $(GENERIC_UBOOT) ] \
+ && echo "-DGENERIC_UBOOT=1"
+
CFLAGS = -ffreestanding -nostdlib -static -fno-pie -std=c17 \
-Wall -Wextra -Wvla -D$(ARCH) -g
@@ -46,7 +49,7 @@ CLEANUP_CMD :=
include arch/$(ARCH)/source.mk
-COMPILE_FLAGS := $(CFLAGS) $(ARCH_CFLAGS)
+COMPILE_FLAGS := $(CFLAGS) $(ARCH_CFLAGS) $(UBOOTFLAGS)
LINK_FLAGS := $(LDFLAGS) $(ARCH_LDFLAGS)
INCLUDE_FLAGS := -I include -include config.h -include arch/$(ARCH)/config.h
diff --git a/README.md b/README.md
index b2b60f2..7216c1a 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ Open `docs/output/html/index.html` in your favorite web browser.
+ `RELEASE=<0/1>`: Enable optimizations when set to `1`. Default is `0`.
Note that with `RELEASE=1` the built in serial driver is disabled, as eventually
-I'd like to provide it as a separate userspace driver. At the moment only NS16550A and
+I'd like to provide it as a separate userspace driver. At the moment only 8250 and
compatible serial devices are supported.
+ `LLVM=<0/1>`: Use LLVM toolchain when set to `1`. Default is `0`.
@@ -36,6 +36,22 @@ Note that due to some bugs in the toolchain, LTO is disabled with `RELEASE=1`.
+ `UBSAN=<0/1>`: Enable undefined behavior sanitizer, outputs a number of warnings at
runtime when undefined behavior is detected. Only available with `RELEASE=0`.
++ `GENERIC_UBOOT=<0/1>`: Compile for use with generic u-boot. This allows
+booting the kernel through u-boot's `go` command. This would in theory allow
+using essentially any precompiled u-boot as a bootloader, but requires some
+extra effort by the user to manage loading different parts to where they should
+go. I should probably come up with a full example, but approximately:
+
+1. Load `kmi.bin` to address `A`
+2. Load `initrd` to address `B`
+3. Load FDT to address `C` (either provided by u-boot or a separate file)
+4. Add `initrd` to FDT's `chosen` node
+5. Boot with `go ${A} ${C}`.
+
+Note that u-boot likes to skip the `0x` prefix for
+hexadecimal values, which can confuse my string converter.
+Check that the strings you pass to `go` have their correct prefixes.
+
+ `run`: Load a test program into `qemu` and run it. Requires some outside support at
the moment, please see [kmi-example](https://github.com/Kimplul/kmi-example).
This command might eventually be moved out of this repo.
diff --git a/arch/riscv64/conf/mkimage.sh b/arch/riscv64/conf/mkimage.sh
index badac4c..1726e57 100755
--- a/arch/riscv64/conf/mkimage.sh
+++ b/arch/riscv64/conf/mkimage.sh
@@ -14,6 +14,10 @@ SSIZE=$(fdisk -l rootfs.img | awk '$1=="Units:" {print $8}')
OFFSET=$(fdisk -l rootfs.img | awk '$1=="rootfs.img1" {print $2}')
mount -o loop,offset=$((${SSIZE}*${OFFSET})) rootfs.img fs
+# copy over files outside the .itb to mess around with manual booting
+cp kmi.bin fs
+cp arch/riscv64/conf/initrd fs
+
# not entirely pleased with this solution, although eventually I should probably
# move `run` out of the kernel repo and into some `kmios` repo with a runtime
# and proper initrd etc. so this is good enough for now
diff --git a/arch/riscv64/init/init.c b/arch/riscv64/init/init.c
index 92f8053..278b9db 100644
--- a/arch/riscv64/init/init.c
+++ b/arch/riscv64/init/init.c
@@ -54,7 +54,8 @@ static void init_bootmem(uintptr_t load_addr, uintptr_t ram_base)
extern char *__kernel;
extern char *__kernel_size;
- uintptr_t top = load_addr + (uintptr_t)&__kernel + (uintptr_t)&__kernel_size;
+ uintptr_t top = load_addr + (uintptr_t)&__kernel +
+ (uintptr_t)&__kernel_size;
/* this could be risky, as we might overwrite some bits of initrd or fdt
* if they're allocated too close to the kernel payload.
@@ -119,3 +120,17 @@ void init(void *fdt, pm_t load_addr)
jump_to_kernel(fdt, ram_base, (void *)VM_KERN);
}
+
+#if GENERIC_UBOOT
+void init_go(int argc, char **argv, pm_t load_addr)
+{
+ if (argc != 2)
+ return;
+
+ void *fdt = (void *)strtouintptr(argv[1]);
+
+ /* fdt is passed as second argument, load address first but since we
+ * already have it due to our ingenious _start, no need to parse it */
+ init(fdt, load_addr);
+}
+#endif
diff --git a/arch/riscv64/init/start.S b/arch/riscv64/init/start.S
index 7439d93..b6394f7 100644
--- a/arch/riscv64/init/start.S
+++ b/arch/riscv64/init/start.S
@@ -1,17 +1,33 @@
/* SPDX-License-Identifier: copyleft-next-0.3.1 */
/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+/* generic u-boot passes argc and argv as if we were a regular
+ * program, so our load address must be placed third.
+ * If we have a 'custom' u-boot with kmi support, the fdt
+ * will be passed to us directly, so the load address can go second.
+ */
+#if GENERIC_UBOOT
+# define LOAD_REG a2
+# define INIT init_go
+#else
+# define LOAD_REG a1
+# define INIT init
+#endif
+
.section .init
.global _start
/* Entry point to the kernel loader. */
_start:
/* get load address */
-auipc a1, 0
+auipc LOAD_REG, 0
/* keep using bootloader stack for now */
//li sp, PM_STACK_TOP
/* make sure there's no garbage in tp, important for id assignment */
li tp, 0
-call init
+call INIT
+// INIT shouldn't return, but if we do we might as well just jump back to u-boot
+// or wherever
+ret
.section .text
.global jump_to_kernel
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index d0cac2c..235884b 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -427,7 +427,8 @@ stat_t populate_kvmem(struct vmem *b)
size_t flags = VM_V | VM_R | VM_W | VM_X | VM_G;
for (size_t i = KSTART_PAGE; i < IO_PAGE; ++i)
b->leaf[i] = (struct vmem *)to_pte(
- get_ram_base() + TOP_PAGE_SIZE * (i - KSTART_PAGE), flags);
+ get_ram_base() + TOP_PAGE_SIZE * (i - KSTART_PAGE),
+ flags);
/* map in IO region */
map_io_dbg(b);
diff --git a/common/debug.c b/common/debug.c
index 1bc87f7..2cd4570 100644
--- a/common/debug.c
+++ b/common/debug.c
@@ -59,8 +59,8 @@ vm_t map_io_dbg(struct vmem *b)
* try to implement some kind of basic driver subsystem, but this is good enough
* for now. */
-/** NS16550A and compatible serial drivers. */
-struct __packed ns16550a {
+/** 8250 and compatible serial drivers. */
+struct __packed uart_8250 {
/** Receiver buffer/transmitter holding register. */
uint8_t data;
@@ -111,10 +111,10 @@ struct __packed ns16550a {
#define LSR_ERR (1 << 7)
/**
- * Address of ns16550a port. If other serial drivers are added, this should
- * maybe be made a void *.
+ * Address of generic 8250 port. If other serial drivers are added, this should
+ * maybe be made a void *. No support for quirks at the moment.
*/
-static struct ns16550a *port = 0;
+static struct uart_8250 *port = 0;
/**
* Serial transmitter empty.
@@ -150,8 +150,17 @@ static void __putchar(char c)
*/
static enum serial_dev __serial_dev_enum(const char *dev_name)
{
+ /* qemu, for example */
if (strncmp("ns16550", dev_name, 7) == 0)
- return NS16550A;
+ return UART_8250;
+
+ /* mentioned in dtc documentation as an example */
+ if (strncmp("ns8250", dev_name, 7) == 0)
+ return UART_8250;
+
+ /* starfive visionfive 2, for example (hopefully works) */
+ if (strncmp("snps,dw-apb-uart", dev_name, 16) == 0)
+ return UART_8250;
return -1;
}
@@ -194,8 +203,8 @@ static struct dbg_info __dbg_from_fdt(const void *fdt)
void __setup_dbg(vm_t pt, enum serial_dev dev)
{
switch (dev) {
- case NS16550A:
- port = (struct ns16550a *)pt;
+ case UART_8250:
+ port = (struct uart_8250 *)pt;
break;
}
diff --git a/common/pmem.c b/common/pmem.c
index 22a1493..c817c5a 100644
--- a/common/pmem.c
+++ b/common/pmem.c
@@ -612,6 +612,8 @@ void init_pmem(void *fdt)
pm_t ram_size = __get_ramtop(fdt) - get_ram_base();
pm_t ram_base = (pm_t)__va(get_ram_base());
+ /** @todo could probably improve error messages on failing to get fdt
+ * values */
pm_t initrd_top = get_initrdtop(fdt);
pm_t fdt_top = __get_fdttop(fdt);
diff --git a/common/string.c b/common/string.c
index 6685c08..a76c875 100644
--- a/common/string.c
+++ b/common/string.c
@@ -324,3 +324,139 @@ __weak int memcmp(const void *ptr1, const void *ptr2, size_t num)
return (int)(p1[-1] - p2[-1]);
}
+
+/**
+ * Convert ASCII hex character to integer.
+ * Allows both upper- and lowercase letters.
+ *
+ * @param c Character to convert.
+ * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc.
+ * \c -1 if conversion failed.
+ */
+static int __hexval(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a';
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A';
+
+ return -1;
+}
+
+/**
+ * Convert string assumed to represent hex
+ * value to corresponding pointer.
+ *
+ * @param s String to convert to value.
+ * @return Corresponding pointer value.
+ */
+static uintptr_t __hexuintptr(const char *s)
+{
+ uintptr_t res = 0;
+ int val = 0;
+ while ((val = __hexval(*(s++))) != -1) {
+ res *= 16;
+ res += val;
+ }
+
+ return res;
+}
+
+/**
+ * Convert ASCII decimal character to integer.
+ *
+ * @param c Character to convert.
+ * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc.
+ * \c -1 if conversion failed.
+ */
+static int __decval(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ return -1;
+}
+
+/**
+ * Convert string assumed to represent decimal
+ * value to corresponding pointer.
+ *
+ * @param s String to convert to value.
+ * @return Corresponding pointer value.
+ */
+static uintptr_t __decuintptr(const char *s)
+{
+ uintptr_t res = 0;
+ int val = 0;
+ while ((val = __decval(*(s++))) != -1) {
+ res *= 10;
+ res += val;
+ }
+
+ return res;
+}
+
+/**
+ * Convert ASCII octal character to integer.
+ *
+ * @param c Character to convert.
+ * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc.
+ * \c -1 if conversion failed.
+ */
+static int __octval(char c)
+{
+ if (c >= '0' && c <= '7')
+ return c - '0';
+
+ return -1;
+}
+
+/**
+ * Convert string assumed to represent octal
+ * value to corresponding pointer.
+ *
+ * @param s String to convert to value.
+ * @return Corresponding pointer value.
+ */
+static uintptr_t __octuintptr(const char *s)
+{
+ uintptr_t res = 0;
+ int val = 0;
+ while ((val = __octval(*(s++))) != -1) {
+ res *= 8;
+ res += val;
+ }
+
+ return res;
+}
+
+uintptr_t strtouintptr(const char *s)
+{
+ if (!s)
+ return 0;
+
+ if (s[0] == 0)
+ return 0;
+
+ if (s[0] == '0') {
+ if (s[1] == 0)
+ return 0;
+
+ if (s[1] == 'x' || s[1] == 'X')
+ return __hexuintptr(s + 2);
+
+ return __octuintptr(s + 1);
+ }
+
+ if (s[0] == '-')
+ return -__decuintptr(s + 1);
+
+ if (s[0] == '+')
+ return __decuintptr(s + 1);
+
+ return __decuintptr(s);
+}
diff --git a/include/kmi/debug.h b/include/kmi/debug.h
index deca4ca..4079048 100644
--- a/include/kmi/debug.h
+++ b/include/kmi/debug.h
@@ -320,8 +320,8 @@
#if defined(DEBUG)
/** Serial devices supported. */
enum serial_dev {
- /** NS16550A and compatible. Currently the only supported serial device. */
- NS16550A,
+ /** 8250 and compatible. Currently the only supported serial device. */
+ UART_8250,
};
/**
diff --git a/include/kmi/string.h b/include/kmi/string.h
index 2b26a2d..f72299b 100644
--- a/include/kmi/string.h
+++ b/include/kmi/string.h
@@ -215,6 +215,21 @@ void *memmove(void *dst, const void *src, size_t num);
*/
int memcmp(const void *ptr1, const void *ptr2, size_t num);
+/**
+ * Try to convert string \p s into a corresponding
+ * \c uintptr_t. Handles decimal, octal and hex,
+ * assuming hex starts with \c 0x or \c 0X and octal with
+ * \c 0, otherwise assumes decimal. Allows \c + and \c - in
+ * decimal.
+ *
+ * Note that u-boot likes to skip leading \c 0x when using hex,
+ * so if you're passing u-boot variables make sure to check prefixes.
+ *
+ * @param s String to convert to pointer.
+ * @return Corresponding pointer value.
+ */
+uintptr_t strtouintptr(const char *s);
+
/* Honorable mentions:
*
* char *strerror(int err);