diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-05-11 22:55:31 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-05-11 22:55:31 +0300 |
| commit | 27ffa747f36aee9c1d5bbc61395a078238366070 (patch) | |
| tree | f37c544aaca6bebd1cc146d1eb002483c7cfd474 /arch | |
| parent | 3109effe7297232e17c1792e63c3a9c7d129a4eb (diff) | |
| download | kmi-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?
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/riscv64/conf/init-link.S | 13 | ||||
| -rw-r--r-- | arch/riscv64/conf/kmi.its | 4 | ||||
| -rw-r--r-- | arch/riscv64/config.h | 26 | ||||
| -rw-r--r-- | arch/riscv64/init/init.c | 88 | ||||
| -rw-r--r-- | arch/riscv64/init/start.S | 16 | ||||
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 2 | ||||
| -rw-r--r-- | arch/riscv64/source.mk | 2 |
7 files changed, 85 insertions, 66 deletions
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 |
