From d51665904faa31c39106e4b2afa7541615072e72 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Fri, 29 Nov 2024 22:17:13 +0100 Subject: Allow the kernel to run as a Linux image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to run on real hardware, it's actually easier to re-use workflows from existing bootloaders than loading things in memory manually. In order to achieve this two things had to be settled: 1. The image has to have a Linux header as defined in the RISC-V port. This header will be taken into consideration by bootloaders in order to know where to jump, the image offset, the endianness of the image, etc. 2. The image cannot just be an ELF executable. Rather, we must translate it into binary form via `objcopy`. With all of this at hand, I was able to make the kernel run on a Starfive VisionFive 2 board, and I have recorded an example so it's available as documentation. Signed-off-by: Miquel Sabaté Solà --- kernel/head.S | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'kernel/head.S') diff --git a/kernel/head.S b/kernel/head.S index c8f523d..210eb7c 100644 --- a/kernel/head.S +++ b/kernel/head.S @@ -1,9 +1,61 @@ +#include #include .global _start +.global _start_kernel .section .head.text +// Since this kernel boots in virtualization environments and machines that are +// expecting a Linux kernel, we have to adhere to how bootloaders expect Linux +// kernel images to look like. In particular, the entry of the kernel is +// actually a Boot image header, as defined in: +// +// https://docs.kernel.org/arch/riscv/boot-image-header.html +// +// Conveniently, for machines who don't care about this (e.g. QEMU), the first +// 64 bits correspond to two instructions that can be set up, and hence entering +// here will simply jumpt to `_start_kernel`, our real entry. _start: + // The first two words give us room for two executable instructions. Linux + // uses that on EFI support to first allocate a magic value for UEFI and + // then have a `j _start_kernel` instruction. Otherwise it just allocates + // the first one for `j _start_kernel` and leaves the second word empty. The + // latter is what we do here as well. + j _start_kernel + .word 0 + + // Ensure alignment for the next double word. + .balign 8 + + // Load offset. Note that this matches the PAGE_OFFSET as defined in + // `include/mm.h`. + .dword 0x200000 + + // Size of the image. This is *mandatory* as per bootloader request. + .dword _end - _start + + // Flags. As defined by Linux, only one bit matters here, which is related + // to endianness. Setting 0 means little-endian. + .dword 0 + + // Header version. + .word RISCV_HEADER_VERSION + + // Reserved fields. + .word 0 + .dword 0 + + // Deprecated image magic. + .ascii RISCV_IMAGE_MAGIC + .balign 4 + + // Good image magic, in little-endian format. + .ascii RISCV_IMAGE_MAGIC2 + + // Reserved field. + .word 0 + +_start_kernel: // Mask all interrupts csrw sie, zero csrw sip, zero -- cgit v1.2.3