|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v5 07/11] xen/riscv: add Linux kernel loading support
Introduce support for loading a Linux kernel Image which is got by uncompressing of Image.gz on RISC-V. kernel_image_load() and place_modules() currently call panic() on failure rather than returning an error. This is because the common kernel_load() in common/device-tree/kernel.c does not expect a return code. Handling errors gracefully would require a separate refactor. The implementation is based on the Xen Arm kernel loading code. text_offset is available for both 32-bit and 64-bit Image, so fix that. Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx> --- Changes in v5: - Introduce MODULES_ALIGNMENT and use it inside place_modules(). - Add if() to avoid underflow when modbase is calculated in the case when modules would overlap the kernel. - Drop unnessary dprintk()s. - "&& size > effective_size" in kernel_image64_probe(). - Use 'size_t' for image_size in struct kernel_info to fit both RV32 and RV64. --- Changes in v4: - Update the patch subject: add "... Linux kernel ...". - Make bi variable unsigned. And use while() instead of for(). - Update the condition which calculates overlapping of kernel with modules. - Reject a malformed image before the loop to avoid wrapping load_addr + image_size in the per-bankcheck below. - Use mathematical representation to print ranges. - Use #error instead of just returning unsupported. - Update the comment above stuct image in kernel_image64_probe(). --- --- xen/arch/riscv/Makefile | 1 + xen/arch/riscv/include/asm/config.h | 13 ++ xen/arch/riscv/kernel.c | 249 ++++++++++++++++++++++++++++ xen/include/xen/fdt-kernel.h | 11 +- 4 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 xen/arch/riscv/kernel.c diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index cfc3fdf7d208..eecdcbc76867 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -8,6 +8,7 @@ obj-y += guestcopy.o obj-y += imsic.o obj-y += intc.o obj-y += irq.o +obj-y += kernel.init.o obj-y += mm.o obj-y += p2m.o obj-y += paging.o diff --git a/xen/arch/riscv/include/asm/config.h b/xen/arch/riscv/include/asm/config.h index 0613de008b13..fd69057826e1 100644 --- a/xen/arch/riscv/include/asm/config.h +++ b/xen/arch/riscv/include/asm/config.h @@ -151,6 +151,19 @@ extern unsigned long phys_offset; /* = load_start - XEN_VIRT_START */ #endif +/* + * KERNEL_LOAD_ADDR_ALIGNMENT is defined based on paragraph of + * "Kernel location" of boot.rst: + * https://docs.kernel.org/arch/riscv/boot.html#kernel-location + */ +#if defined(CONFIG_RISCV_32) +#define KERNEL_LOAD_ADDR_ALIGNMENT MB(4) +#elif defined(CONFIG_RISCV_64) +#define KERNEL_LOAD_ADDR_ALIGNMENT MB(2) +#else +#error "Define KERNEL_LOAD_ADDR_ALIGNMENT" +#endif + #endif /* ASM__RISCV__CONFIG_H */ /* * Local variables: diff --git a/xen/arch/riscv/kernel.c b/xen/arch/riscv/kernel.c new file mode 100644 index 000000000000..8580cdb32d7a --- /dev/null +++ b/xen/arch/riscv/kernel.c @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <xen/bug.h> +#include <xen/compiler.h> +#include <xen/errno.h> +#include <xen/fdt-kernel.h> +#include <xen/guest_access.h> +#include <xen/init.h> +#include <xen/libfdt/libfdt.h> +#include <xen/mm.h> +#include <xen/types.h> +#include <xen/vmap.h> + +#include <asm/setup.h> + +#define IMAGE64_MAGIC_V2 0x05435352 /* Magic number 2, le, "RSC\x05" */ + +/* + * Machine word alignment could be useful for copying to be more efficient. + * There is no reason for now to have bigger module alignment. + */ +#define MODULES_ALIGNMENT sizeof(unsigned long) + +static void __init place_modules(struct kernel_info *info, paddr_t kernbase, + paddr_t kernend) +{ + const struct boot_module *mod = info->bd.initrd; + const struct membanks *banks = kernel_info_get_mem_const(info); + const paddr_t initrd_len = ROUNDUP(mod ? mod->size : 0, + MODULES_ALIGNMENT); + const paddr_t dtb_len = ROUNDUP(fdt_totalsize(info->fdt), + MODULES_ALIGNMENT); + const paddr_t modsize = initrd_len + dtb_len; + unsigned int bi = banks->nr_banks; + + if ( modsize < initrd_len ) + panic("Module size overflow: initrd + dtb size wraps paddr_t\n"); + + /* + * Place modules as high in RAM as possible, scanning banks from + * last to first so that the end of the last bank is preferred. + */ + while ( bi-- > 0 ) + { + const struct membank *bank = &banks->bank[bi]; + const paddr_t bank_end = bank->start + bank->size; + paddr_t modbase; + + if ( modsize > bank->size ) + continue; + + modbase = ROUNDDOWN(bank_end - modsize, MODULES_ALIGNMENT); + + if ( modbase < bank->start ) + continue; + + /* + * If modules would overlap the kernel, try placing them below it. + */ + if ( (modbase < ROUNDUP(kernend, MODULES_ALIGNMENT)) && + (modbase + modsize > kernbase) ) + { + /* Avoid underflow below */ + if ( kernbase < modsize ) + continue; + + modbase = ROUNDDOWN(kernbase - modsize, MODULES_ALIGNMENT); + if ( modbase < bank->start ) + continue; + } + + info->dtb_paddr = modbase; + info->initrd_paddr = modbase + dtb_len; + + return; + } + + panic("Unable to find suitable location for dtb+initrd\n"); +} + +static paddr_t __init kernel_image_place(struct kernel_info *info) +{ + paddr_t load_addr = INVALID_PADDR; + uint64_t image_size = info->image.image_size ?: info->image.len; + const struct membanks *banks = kernel_info_get_mem_const(info); + unsigned int nr_banks = banks->nr_banks; + unsigned int bi; + + /* + * At the moment, RISC-V's Linux kernel should be always position + * independent based on "Per-MMU execution" of boot.rst: + * https://docs.kernel.org/arch/riscv/boot.html#pre-mmu-execution + * + * But just for the case when RISC-V's Linux kernel isn't position + * independent it is needed to take load address from + * info->image.start. + * + * If `start` is zero, the Image is position independent. + */ + if ( likely(!info->image.start) ) + { + for ( bi = 0; bi != nr_banks; bi++ ) + { + const struct membank *bank = &banks->bank[bi]; + paddr_t bank_start = bank->start; + /* + * According to boot.rst kernel load address should be properly + * aligned: + * https://docs.kernel.org/arch/riscv/boot.html#kernel-location + * + * As Image in this case is PIC we can ignore + * info->image.text_offset. + */ + paddr_t aligned_start = ROUNDUP(bank_start, KERNEL_LOAD_ADDR_ALIGNMENT); + paddr_t bank_end = bank_start + bank->size; + paddr_t bank_size; + + if ( aligned_start > bank_end ) + continue; + + bank_size = bank_end - aligned_start; + + if ( image_size <= bank_size ) + { + load_addr = aligned_start; + break; + } + } + } + else + { + load_addr = info->image.start + info->image.text_offset; + + WARN_ON(!IS_ALIGNED(load_addr, KERNEL_LOAD_ADDR_ALIGNMENT)); + + /* + * Reject a malformed image before the loop to avoid wrapping + * load_addr + image_size in the per-bank check below by setting + * bi = nr_banks. + * + * image_size covers the kernel from _start (placed at load_addr = + * start + text_offset) through _end. The alignment gap + * [start, load_addr) is padding and need not lie within a bank. + */ + bi = image_size <= (paddr_t)-1 - load_addr ? 0 : nr_banks; + for ( ; bi != nr_banks; bi++ ) + { + const struct membank *bank = &banks->bank[bi]; + paddr_t bank_start = bank->start; + paddr_t bank_end = bank_start + bank->size; + + if ( (load_addr >= bank_start) && + (load_addr + image_size <= bank_end) ) + break; + } + } + + if ( bi == nr_banks ) + panic("Failed to place kernel image in any memory bank\n"); + + info->entry = load_addr; + + return load_addr; +} + +static void __init kernel_image_load(struct kernel_info *info) +{ + int rc; + paddr_t load_addr = kernel_image_place(info); + paddr_t paddr = info->image.kernel_addr; + paddr_t len = info->image.len; + paddr_t effective_size = info->image.image_size ?: len; + void *kernel; + + place_modules(info, load_addr, load_addr + effective_size); + + printk("Loading Image from %"PRIpaddr" to [%"PRIpaddr",%"PRIpaddr")\n", + paddr, load_addr, load_addr + effective_size); + + kernel = ioremap_cache(paddr, len); + + if ( !kernel ) + panic("Unable to map kernel\n"); + + /* Move kernel to proper location in guest phys map */ + rc = copy_to_guest_phys(info->bd.d, load_addr, kernel, len); + + if ( rc ) + panic("Unable to copy kernel to proper guest location\n"); + + iounmap(kernel); +} + +/* Check if the image is a 64-bit Image */ +static int __init kernel_image64_probe(struct kernel_info *info, + paddr_t addr, paddr_t size) +{ + /* https://www.kernel.org/doc/Documentation/riscv/boot-image-header.rst */ + struct { + uint32_t code0; /* Executable code */ + uint32_t code1; /* Executable code */ + uint64_t text_offset; /* Image load offset, little endian */ + uint64_t image_size; /* Effective Image size, little endian */ + uint64_t flags; /* kernel flags, little endian */ + uint32_t version; /* Version of this header */ + uint32_t res1; /* Reserved */ + uint64_t res2; /* Reserved */ + uint64_t magic; /* Deprecated: Magic number, little endian, "RISCV" */ + uint32_t magic2; /* Magic number 2, little endian, "RSC\x05" */ + uint32_t res3; /* Reserved for PE COFF offset */ + } image; + uint64_t effective_size; + + if ( size < sizeof(image) ) + return -EINVAL; + + copy_from_paddr(&image, addr, sizeof(image)); + + /* Magic v1 is deprecated and may be removed. Only use v2 */ + if ( le32_to_cpu(image.magic2) != IMAGE64_MAGIC_V2 ) + return -EINVAL; + + effective_size = le64_to_cpu(image.image_size); + + if ( !effective_size ) + return -EINVAL; + + info->image.kernel_addr = addr; + /* Actual size in the binary file */ + info->image.len = size; + /* Total memory the kernel occupies at runtime */ + info->image.image_size = effective_size; + info->image.text_offset = le64_to_cpu(image.text_offset); + info->image.start = 0; + + info->load = kernel_image_load; + + return 0; +} + +int __init kernel_image_probe(struct kernel_info *info, paddr_t addr, + paddr_t size) +{ +#ifdef CONFIG_RISCV_64 + return kernel_image64_probe(info, addr, size); +#else +# error "Only 64-bit RISC-V is supported" +#endif +} diff --git a/xen/include/xen/fdt-kernel.h b/xen/include/xen/fdt-kernel.h index 8cd1670c2c6f..00c37be101bf 100644 --- a/xen/include/xen/fdt-kernel.h +++ b/xen/include/xen/fdt-kernel.h @@ -59,8 +59,15 @@ struct kernel_info { struct { paddr_t kernel_addr; paddr_t len; -#if defined(CONFIG_ARM_64) || defined(CONFIG_RISCV_64) - paddr_t text_offset; /* 64-bit Image only */ +#if defined(CONFIG_ARM_64) || defined(CONFIG_RISCV) + /* + * ARM: 64-bit Image only. + * RISC-V: both 32-bit and 64-bit Images. + */ + paddr_t text_offset; +#endif +#if defined(CONFIG_RISCV) + size_t image_size; /* Effective size of Image */ #endif paddr_t start; /* Must be 0 for 64-bit Image */ } image; -- 2.54.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |