aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2023-05-11 22:55:31 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2023-05-11 22:55:31 +0300
commit27ffa747f36aee9c1d5bbc61395a078238366070 (patch)
treef37c544aaca6bebd1cc146d1eb002483c7cfd474
parent3109effe7297232e17c1792e63c3a9c7d129a4eb (diff)
downloadkmi-27ffa747f36aee9c1d5bbc61395a078238366070.tar.gz
kmi-27ffa747f36aee9c1d5bbc61395a078238366070.zip
arbitrary load address and RAM base detection
+ Both kind of go hand in hand, made sense to do both at the same time. Some parts feel slightly hacky, the loader works by placing everything on the stack and avoiding global values. Still, seems to work?
-rw-r--r--Makefile13
-rw-r--r--arch/riscv64/conf/init-link.S13
-rw-r--r--arch/riscv64/conf/kmi.its4
-rw-r--r--arch/riscv64/config.h26
-rw-r--r--arch/riscv64/init/init.c88
-rw-r--r--arch/riscv64/init/start.S16
-rw-r--r--arch/riscv64/kernel/vmem.c2
-rw-r--r--arch/riscv64/source.mk2
-rw-r--r--common/main.c10
-rw-r--r--common/mem.c16
-rw-r--r--common/pmem.c6
-rw-r--r--include/kmi/mem.h20
-rw-r--r--lib/dbg_fdt.c (renamed from lib/fdt_dbg.c)2
-rwxr-xr-xscripts/gen-deps11
14 files changed, 146 insertions, 83 deletions
diff --git a/Makefile b/Makefile
index 56e7254..89f8033 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ DEBUGFLAGS != [ $(RELEASE) ] \
&& echo "-flto -O2 -DNDEBUG" \
|| echo "-O0 -DDEBUG"
-CFLAGS = -ffreestanding -nostdlib -static -fno-pie -std=c17 -Wall -Wextra -Wvla -D$(ARCH) -g
+CFLAGS = -ffreestanding -nostdlib -fno-pie -std=c17 -Wall -Wextra -Wvla -D$(ARCH) -g -fpic
DEPFLAGS = -MT $@ -MMD -MP -MF $@.d
LINTFLAGS = -fsyntax-only
PREPROCESS = -E
@@ -13,7 +13,8 @@ LDFLAGS != [ $(LLVM) ] \
|| echo -static-libgcc -lgcc
BUILD = build
-ARCH_BUILD = $(BUILD)/arch/$(ARCH)
+ARCH_KERN_BUILD = $(BUILD)/kernel/arch/$(ARCH)
+ARCH_INIT_BUILD = $(BUILD)/init/arch/$(ARCH)
ARCH_SOURCE = arch/$(ARCH)
all: kmi.bin
@@ -35,9 +36,11 @@ COMPILER != [ $(LLVM) ] \
KERNEL_SOURCES != echo common/*.c common/uapi/*.c lib/*.c
+# might consider renaming common, currently it refers to stuff common
+# to all arches but clearly there are bits that are common to init and kernel
+INIT_SOURCES != echo lib/fdt*.c common/fdt.c common/string.c
CLEANUP := build deps.mk kernel.* init.* kmi.bin
CLEANUP_CMD :=
-INIT_SOURCES :=
include arch/$(ARCH)/source.mk
@@ -80,10 +83,10 @@ INIT_LD != ./scripts/gen-deps --init --link "$(INIT_LINK).S"
$(INIT_LD): kernel.bin
init.elf: $(INIT_OBJECTS) $(INIT_LD)
- $(GENELF) -T $(INIT_LD) $(INIT_OBJECTS) -o init.elf $(LINK_FLAGS)
+ $(GENELF) $(INIT_FLAGS) -T $(INIT_LD) $(INIT_OBJECTS) -o init.elf $(LINK_FLAGS)
kernel.elf: $(KERNEL_OBJECTS) $(KERNEL_LD)
- $(GENELF) -T $(KERNEL_LD) $(KERNEL_OBJECTS) -o kernel.elf $(LINK_FLAGS)
+ $(GENELF) $(KERNEL_FLAGS) -T $(KERNEL_LD) $(KERNEL_OBJECTS) -o kernel.elf $(LINK_FLAGS)
init.bin: init.elf
$(OBJCOPY) $(OBJCOPY_FLAGS) init.elf init.bin
diff --git a/arch/riscv64/conf/init-link.S b/arch/riscv64/conf/init-link.S
index 600304f..0933fbb 100644
--- a/arch/riscv64/conf/init-link.S
+++ b/arch/riscv64/conf/init-link.S
@@ -5,11 +5,13 @@ OUTPUT_ARCH(riscv)
ENTRY(_start)
SECTIONS {
- . = ABSOLUTE(PM_KERN_BASE);
- __init_start = .;
+ /* slight hack, the binary is position independent code but technically
+ not a PIE, so avoid globals and just add the load address to external
+ addresses. */
+ __init_start = 0;
/* objcopy only copies these three sections (as far as I'm aware) into the
- produces binary, so __init_end should point to the correct location in the
+ produced binary, so __init_end should point to the correct location in the
final binary
*/
.text ALIGN(4K) : AT(0) {
@@ -29,7 +31,10 @@ SECTIONS {
*(.sbss*) *(.bss*) *(COMMON)
}
- __init_end = . ;
+ .top : {
+ *(.top*)
+ }
+
__kernel_size = <KERNEL_SIZE>;
.garbage : {
diff --git a/arch/riscv64/conf/kmi.its b/arch/riscv64/conf/kmi.its
index 0494030..768f98b 100644
--- a/arch/riscv64/conf/kmi.its
+++ b/arch/riscv64/conf/kmi.its
@@ -12,8 +12,8 @@
arch = "riscv";
os = "kmi";
compression = "none";
- load = <0x80240000>;
- entry = <0x80240000>;
+ load = <0x80260000>;
+ entry = <0x80260000>;
hash-1 {
algo = "sha1";
};
diff --git a/arch/riscv64/config.h b/arch/riscv64/config.h
index 1d4a6e0..3eb2e72 100644
--- a/arch/riscv64/config.h
+++ b/arch/riscv64/config.h
@@ -22,7 +22,7 @@
/* --- START ARCH USER CONFIG VALUES --- */
/** Physical address to where the OS image will be loaded. */
-#define RAM_BASE 0x80000000
+//#define RAM_BASE 0x80000000
/* --- END ARCH USER CONFIG VALUES --- */
/* don't touch >:( */
@@ -37,20 +37,14 @@
/** \todo UBSAN is getting pretty close to this limit, should it be raised? */
#define PM_KERN_SIZE (SZ_256K)
-/** Physical address to where the kernel proper will be relocated. */
-#define PM_KERN_BASE (RAM_BASE + FW_MAX_SIZE + PM_KERN_SIZE)
+/** Virtual memory stack base. In this case, right after the kernel. */
+#define VM_STACK_BASE (VM_KERN + PM_KERN_SIZE)
-/** Highest allowed physical address where kernel stuff may lie. */
-#define PM_KERN_TOP (PM_KERN_BASE + PM_KERN_SIZE)
+/** Size of virtual memory stack. */
+#define VM_STACK_SIZE (SZ_4K)
-/** Physical memory stack base. In this case, right after the kernel. */
-#define PM_STACK_BASE (PM_KERN_BASE + PM_KERN_SIZE)
-
-/** Size of physical memory stack. */
-#define PM_STACK_SIZE (SZ_256K)
-
-/** Top of physical memory stack. */
-#define PM_STACK_TOP (PM_STACK_BASE + PM_STACK_SIZE)
+/** Top of virtual memory stack. */
+#define VM_STACK_TOP (VM_STACK_BASE + VM_STACK_SIZE)
#if defined(riscv64)
/* 64bit */
@@ -67,7 +61,11 @@
/** Direct mapping starts from this page. */
#define KSTART_PAGE 256UL
-/** The RPC stack page. */
+/**
+ * The RPC stack page.
+ * @todo this should be page before KSTART, but currently
+ * RPC_STACK_TOP is too low (why? was there a reason to place it so low?) and overlaps.
+ */
#define CSTACK_PAGE 248UL
/** User virtual memory space start. */
diff --git a/arch/riscv64/init/init.c b/arch/riscv64/init/init.c
index 08462f6..92f8053 100644
--- a/arch/riscv64/init/init.c
+++ b/arch/riscv64/init/init.c
@@ -12,12 +12,9 @@
#include <kmi/utils.h>
#include <kmi/vmem.h>
#include <arch/vmem.h>
-#include "../kernel/csr.h"
+#include <libfdt.h>
-/** Temporary virtual memory space.
- * Assume 64bit riscv for now.
- */
-struct vmem *root_branch;
+#include "../kernel/csr.h"
/**
* Create page table entry.
@@ -28,22 +25,44 @@ struct vmem *root_branch;
*/
#define to_pte(a, f) (((a) >> 12) << 10 | (f))
-/** Jump into virtual memory. */
-static void init_bootmem()
+/**
+ * Get RAM base address from fdt.
+ * @todo in case of multiple RAM banks, should try to just find one
+ * of them and let the kernel figure the rest out. Kernel doesn't currently
+ * support multiple RAM banks.
+ *
+ * @param fdt FDT pointer.
+ * @return Physical address of ram base.
+ */
+static pm_t __fdt_ram_base(void *fdt)
+{
+ struct cell_info ci = get_reginfo(fdt, "/memory");
+ int mem_offset = fdt_path_offset(fdt, "/memory");
+ uint8_t *mem_reg = (uint8_t *)fdt_getprop(fdt, mem_offset, "reg", NULL);
+ return (pm_t)fdt_load_int_ptr(ci.addr_cells, mem_reg);
+}
+
+/**
+ * Jump into virtual memory.
+ *
+ * @param load_addr Address where init has been loaded.
+ * @param ram_base RAM base.
+ */
+static void init_bootmem(uintptr_t load_addr, uintptr_t ram_base)
{
size_t flags = VM_V | VM_X | VM_R | VM_W;
- extern char *__init_end;
+ extern char *__kernel;
extern char *__kernel_size;
- uintptr_t top = (uintptr_t)&__init_end + (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.
* @todo Allocate root_branch statically? */
- root_branch = (struct vmem *)align_up(top, SZ_4K);
+ struct vmem *root_branch = (struct vmem *)align_up(top, SZ_4K);
/* direct mapping (temp) */
- for (size_t i = 0; i < CSTACK_PAGE; ++i)
+ for (size_t i = 0; i <= CSTACK_PAGE; ++i)
root_branch->leaf[i] = (struct vmem *)to_pte(TOP_PAGE_SIZE * i,
flags);
@@ -51,7 +70,7 @@ static void init_bootmem()
flags |= VM_G;
for (size_t i = KSTART_PAGE; i < IO_PAGE; ++i)
root_branch->leaf[i] = (struct vmem *)to_pte(
- RAM_BASE + TOP_PAGE_SIZE * (i - KSTART_PAGE), flags);
+ ram_base + TOP_PAGE_SIZE * (i - KSTART_PAGE), flags);
/* kernel IO, map to 0 for now, will be updated in the future */
root_branch->leaf[IO_PAGE] = (struct vmem *)to_pte(0, flags);
@@ -66,48 +85,37 @@ static void init_bootmem()
csr_write(CSR_SATP, mode | ((uintptr_t)root_branch >> 12));
}
-/** Relocate kernel proper. */
-static void move_kernel()
+/**
+ * Relocate kernel proper.
+ * @param load_addr Address to where init has been loaded.
+ * Used in calculating kernel start address.
+ */
+static void move_kernel(uintptr_t load_addr)
{
- extern char *__init_end;
+ extern char *__kernel;
extern char *__kernel_size;
- unsigned long sz = (unsigned long)&__kernel_size;
- char *src = (char *)&__init_end;
+ size_t sz = (size_t)&__kernel_size;
+ char *src = load_addr + (char *)&__kernel;
char *dst = (char *)VM_KERN;
for (size_t i = 0; i < sz; ++i)
dst[i] = src[i];
}
/**
- * Convert an existing physical address in a register to a virtual address.
- * There is probably an easier way to do this, but this seems to work alright.
- *
- * @param reg Register to modify.
- */
-#define __va_reg(reg) \
- { \
- vm_t reg = 0; \
- __asm__ ("mv %0, " QUOTE(reg) : "=r" (reg)::); \
- reg = (vm_t)__va(reg); \
- __asm__ ("mv " QUOTE(reg) ", %0" ::"rK" (reg) :); \
- }
-
-/**
* Main driver for the init loader.
*
* @param fdt Global FDT pointer, provided by bootloader.
+ * @param load_addr Address to where init has been loaded.
*/
-void init(void *fdt)
+void init(void *fdt, pm_t load_addr)
{
- extern char *__init_end;
- extern void jump_to_kernel(void *k, void *fdt);
+ extern void jump_to_kernel(void *fdt, pm_t ram_base, void *k);
+
+ pm_t ram_base = __fdt_ram_base(fdt);
- init_bootmem();
- move_kernel();
- __va_reg(sp);
- __va_reg(fp);
- __va_reg(gp);
+ init_bootmem(load_addr, ram_base);
+ move_kernel(load_addr);
- jump_to_kernel((void *)VM_KERN, __va(fdt));
+ jump_to_kernel(fdt, ram_base, (void *)VM_KERN);
}
diff --git a/arch/riscv64/init/start.S b/arch/riscv64/init/start.S
index b54f685..759c409 100644
--- a/arch/riscv64/init/start.S
+++ b/arch/riscv64/init/start.S
@@ -5,8 +5,10 @@
.global _start
/* Entry point to the kernel loader. */
_start:
-/* load static stack */
-li sp, PM_STACK_TOP
+/* get load address */
+auipc a1, 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
@@ -14,6 +16,12 @@ call init
.section .text
.global jump_to_kernel
jump_to_kernel:
-mv a2, a0 // store kernel addr
-mv a0, a1 // move fdt pointer to first argument
+li sp, VM_STACK_TOP // load virtual stack address
+li fp, 0
+li gp, 0
jr a2 // jump to kernel
+
+/* there should be a kernel payload after this address */
+.section .top
+.global __kernel
+__kernel:
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index 6fd7069..d0cac2c 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -427,7 +427,7 @@ 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(
- 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/arch/riscv64/source.mk b/arch/riscv64/source.mk
index 384f75a..5de5309 100644
--- a/arch/riscv64/source.mk
+++ b/arch/riscv64/source.mk
@@ -21,7 +21,7 @@ run:
include $(ARCH_SOURCE)/asm/source.mk
# dependecy generation
-$(ARCH_BUILD)/kernel/entry.o: $(ARCH_SOURCE)/kernel/gen/asm-offsets.h
+$(ARCH_KERN_BUILD)/kernel/entry.o: $(ARCH_SOURCE)/kernel/gen/asm-offsets.h
# full cleanup
CLEANUP_CMD := $(ARCH_SOURCE)/conf/rmimage.sh
diff --git a/common/main.c b/common/main.c
index 5248d2a..9eb6414 100644
--- a/common/main.c
+++ b/common/main.c
@@ -23,11 +23,17 @@
* Sets up all kernel subsystems and jumps into \c init program, does not
* return.
*
- * @param fdt Global FDT pointer.
+ * @param fdt Global FDT pointer in physical memory.
+ * @param ram_base RAM base.
* @return Should not.
*/
-void __main main(void *fdt)
+void __main main(void *fdt, uintptr_t ram_base)
{
+ set_ram_base(ram_base);
+
+ /* convert physical address to virtual address */
+ fdt = __va(fdt);
+
/* dbg uses direct mapping at this point */
init_dbg(fdt);
setup_dmap_dbg();
diff --git a/common/mem.c b/common/mem.c
index 69ba4d6..e3f98e7 100644
--- a/common/mem.c
+++ b/common/mem.c
@@ -17,6 +17,12 @@ size_t __mm_sizes[10];
size_t __mm_page_shift;
enum mm_order __mm_max_order;
+/**
+ * RAM base address. Not sure if it should be provided through a macro
+ * like __mm_*.
+ */
+pm_t ram_base;
+
enum mm_order nearest_order(size_t size)
{
for (enum mm_order order = max_order(); order >= MM_MIN; --order)
@@ -41,3 +47,13 @@ void init_mem(size_t max_order, size_t bits[10], size_t page_shift)
__mm_sizes[i] = 1UL << __mm_shifts[i];
}
}
+
+void set_ram_base(uintptr_t base)
+{
+ ram_base = base;
+}
+
+uintptr_t get_ram_base()
+{
+ return ram_base;
+}
diff --git a/common/pmem.c b/common/pmem.c
index 16e6adf..22a1493 100644
--- a/common/pmem.c
+++ b/common/pmem.c
@@ -609,8 +609,8 @@ void init_pmem(void *fdt)
stat_pmem_conf(fdt, &max_order, &base_bits, bits);
init_mem(max_order, bits, base_bits);
- pm_t ram_size = __get_ramtop(fdt) - RAM_BASE;
- pm_t ram_base = (pm_t)__va(RAM_BASE);
+ pm_t ram_size = __get_ramtop(fdt) - get_ram_base();
+ pm_t ram_base = (pm_t)__va(get_ram_base());
pm_t initrd_top = get_initrdtop(fdt);
pm_t fdt_top = __get_fdttop(fdt);
@@ -629,7 +629,7 @@ void init_pmem(void *fdt)
/* mark init stack, this should be unmapped once we get to executing
* processes */
- __mark_area_used((pm_t)__va(PM_STACK_BASE), (pm_t)__va(PM_STACK_TOP));
+ __mark_area_used(VM_STACK_BASE, VM_STACK_TOP);
/* mark kernel */
/* this could be made more explicit, I suppose. */
diff --git a/include/kmi/mem.h b/include/kmi/mem.h
index a6c6bfa..f43ee08 100644
--- a/include/kmi/mem.h
+++ b/include/kmi/mem.h
@@ -92,7 +92,7 @@
* @param x Physical address.
* @return Corresponding virtual address.
*/
-#define __va(x) (void *)(((uintptr_t)(x)) + VM_DMAP - RAM_BASE)
+#define __va(x) (void *)(((uintptr_t)(x)) + VM_DMAP - get_ram_base())
/**
* Convert virtual address to physical address in direct mapping.
@@ -100,7 +100,7 @@
* @param x Virtual address.
* @return Corresponding physical address.
*/
-#define __pa(x) (void *)(((uintptr_t)(x)) - VM_DMAP + RAM_BASE)
+#define __pa(x) (void *)(((uintptr_t)(x)) - VM_DMAP + get_ram_base())
/**
* Get page number of physical address.
@@ -213,6 +213,22 @@ enum mm_order nearest_order(size_t size);
*/
void init_mem(size_t max_order, size_t shifts[10], size_t page_shift);
+/**
+ * Set RAM base address for global access.
+ * For now supports only one RAM bank.
+ *
+ * @param base RAM base address.
+ */
+void set_ram_base(pm_t base);
+
+/**
+ * Get RAM base address.
+ * Very much assumes set_ram_base() has been called beforehand.
+ *
+ * @return RAM base address.
+ */
+pm_t get_ram_base();
+
/** Base page size. */
#define BASE_PAGE_SIZE (order_size(BASE_PAGE))
diff --git a/lib/fdt_dbg.c b/lib/dbg_fdt.c
index d2d4e7b..6d02442 100644
--- a/lib/fdt_dbg.c
+++ b/lib/dbg_fdt.c
@@ -2,7 +2,7 @@
/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
/**
- * @file fdt_dbg.c
+ * @file dbg_fdt.c
* Dump fdt info to serial.
*/
diff --git a/scripts/gen-deps b/scripts/gen-deps
index 674ead6..9a9ad6e 100755
--- a/scripts/gen-deps
+++ b/scripts/gen-deps
@@ -1,9 +1,9 @@
#!/bin/sh
gencommon () {
- lint="build/${s%.*}${1}.l"
- dep="build/${s%.*}${1}.d"
- obj="build/${s%.*}${1}"
+ lint="build/${path}/${s%.*}${1}.l"
+ dep="build/${path}/${s%.*}${1}.d"
+ obj="build/${path}/${s%.*}${1}"
echo "${dep}:" >> deps.mk
echo "-include ${dep}" >> deps.mk
@@ -30,9 +30,11 @@ genrule () {
case "${1}" in
--kernel)
kern=1
+ path=kernel
flags='$(KERN_FLAGS)'
;;
--init)
+ path=init
flags='$(INIT_FLAGS)'
;;
esac
@@ -48,7 +50,8 @@ case "${2}" in
esac
# create all subdirectories
-mkdir -p $(echo "${3}" | xargs -n 1 dirname | uniq | sed 's|^|build/|g')
+mkdir -p $(echo "${3}" | xargs -n 1 dirname | uniq | sed 's|^|build/init/|g')
+mkdir -p $(echo "${3}" | xargs -n 1 dirname | uniq | sed 's|^|build/kernel/|g')
for s in ${3}
do