[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v2 5/7] xen/ppc: Enable bootfdt and boot allocator
On 15.12.2023 03:44, Shawn Anastasio wrote: > --- /dev/null > +++ b/xen/arch/ppc/include/asm/setup.h > @@ -0,0 +1,123 @@ > +#ifndef __ASM_PPC_SETUP_H__ > +#define __ASM_PPC_SETUP_H__ > + > +#define max_init_domid (0) > + > +#include <public/version.h> > +#include <asm/p2m.h> > +#include <xen/device_tree.h> > + > +#define MIN_FDT_ALIGN 8 > +#define MAX_FDT_SIZE SZ_2M > + > +#define NR_MEM_BANKS 256 > + > +#define MAX_MODULES 32 /* Current maximum useful modules */ > + > +typedef enum { > + BOOTMOD_XEN, > + BOOTMOD_FDT, > + BOOTMOD_KERNEL, > + BOOTMOD_RAMDISK, > + BOOTMOD_XSM, > + BOOTMOD_GUEST_DTB, > + BOOTMOD_UNKNOWN > +} bootmodule_kind; > + > +enum membank_type { > + /* > + * The MEMBANK_DEFAULT type refers to either reserved memory for the > + * device/firmware (when the bank is in 'reserved_mem') or any RAM (when > + * the bank is in 'mem'). > + */ > + MEMBANK_DEFAULT, > + /* > + * The MEMBANK_STATIC_DOMAIN type is used to indicate whether the memory > + * bank is bound to a static Xen domain. It is only valid when the bank > + * is in reserved_mem. > + */ > + MEMBANK_STATIC_DOMAIN, > + /* > + * The MEMBANK_STATIC_HEAP type is used to indicate whether the memory > + * bank is reserved as static heap. It is only valid when the bank is > + * in reserved_mem. > + */ > + MEMBANK_STATIC_HEAP, > +}; > + > +/* Indicates the maximum number of characters(\0 included) for shm_id */ > +#define MAX_SHM_ID_LENGTH 16 > + > +struct membank { > + paddr_t start; > + paddr_t size; > + enum membank_type type; > +}; > + > +struct meminfo { > + unsigned int nr_banks; > + struct membank bank[NR_MEM_BANKS]; > +}; > + > +/* > + * The domU flag is set for kernels and ramdisks of "xen,domain" nodes. > + * The purpose of the domU flag is to avoid getting confused in > + * kernel_probe, where we try to guess which is the dom0 kernel and > + * initrd to be compatible with all versions of the multiboot spec. > + */ > +#define BOOTMOD_MAX_CMDLINE 1024 Why does this live here, rather than ... > +struct bootmodule { > + bootmodule_kind kind; > + bool domU; > + paddr_t start; > + paddr_t size; > +}; > + > +/* DT_MAX_NAME is the node name max length according the DT spec */ > +#define DT_MAX_NAME 41 ... next to this? > +struct bootcmdline { > + bootmodule_kind kind; > + bool domU; > + paddr_t start; > + char dt_name[DT_MAX_NAME]; > + char cmdline[BOOTMOD_MAX_CMDLINE]; > +}; > + > +struct bootmodules { > + int nr_mods; > + struct bootmodule module[MAX_MODULES]; > +}; > + > +struct bootcmdlines { > + unsigned int nr_mods; > + struct bootcmdline cmdline[MAX_MODULES]; > +}; > + > +struct bootinfo { > + struct meminfo mem; > + struct meminfo reserved_mem; > + struct bootmodules modules; > + struct bootcmdlines cmdlines; > + bool static_heap; Unused field? > +}; > + > +extern struct bootinfo bootinfo; > + > +/* > + * setup.c > + */ > + > +bool check_reserved_regions_overlap(paddr_t region_start, paddr_t > region_size); > +struct bootmodule *add_boot_module(bootmodule_kind kind, > + paddr_t start, paddr_t size, bool domU); > +void add_boot_cmdline(const char *name, const char *cmdline, > + bootmodule_kind kind, paddr_t start, bool domU); > +const char *boot_module_kind_as_string(bootmodule_kind kind); > +struct bootcmdline * __init boot_cmdline_find_by_kind(bootmodule_kind kind); No __init please on declarations; section placement attributes make sense only on definitions (with pretty narrow exceptions). > --- a/xen/arch/ppc/setup.c > +++ b/xen/arch/ppc/setup.c > @@ -1,16 +1,296 @@ > /* SPDX-License-Identifier: GPL-2.0-or-later */ > #include <xen/init.h> > #include <xen/lib.h> > +#include <xen/libfdt/libfdt.h> > #include <xen/mm.h> > #include <public/version.h> > #include <asm/boot.h> > #include <asm/early_printk.h> > #include <asm/mm.h> > #include <asm/processor.h> > +#include <asm/setup.h> > > /* Xen stack for bringing up the first CPU. */ > unsigned char __initdata cpu0_boot_stack[STACK_SIZE] __aligned(STACK_SIZE); > > +struct bootinfo __initdata bootinfo; > + > +void __init add_boot_cmdline(const char *name, const char *cmdline, > + bootmodule_kind kind, paddr_t start, bool domU) > +{ > + struct bootcmdlines *cmds = &bootinfo.cmdlines; > + struct bootcmdline *cmd; > + > + if ( cmds->nr_mods == MAX_MODULES ) > + { > + printk("Ignoring %s cmdline (too many)\n", name); > + return; > + } > + > + cmd = &cmds->cmdline[cmds->nr_mods++]; > + cmd->kind = kind; > + cmd->domU = domU; > + cmd->start = start; > + > + ASSERT(strlen(name) <= DT_MAX_NAME); > + safe_strcpy(cmd->dt_name, name); > + > + if ( strlen(cmdline) > BOOTMOD_MAX_CMDLINE ) > + panic("module %s command line too long\n", name); > + safe_strcpy(cmd->cmdline, cmdline); > +} > + > +struct bootmodule __init *add_boot_module(bootmodule_kind kind, > + paddr_t start, paddr_t size, > + bool domU) > +{ > + struct bootmodules *mods = &bootinfo.modules; > + struct bootmodule *mod; > + unsigned int i; > + > + if ( mods->nr_mods == MAX_MODULES ) > + { > + printk("Ignoring %s boot module at %"PRIpaddr"-%"PRIpaddr" (too > many)\n", > + boot_module_kind_as_string(kind), start, start + size); > + return NULL; > + } > + > + if ( check_reserved_regions_overlap(start, size) ) > + return NULL; > + > + for ( i = 0 ; i < mods->nr_mods ; i++ ) > + { > + mod = &mods->module[i]; > + if ( mod->kind == kind && mod->start == start ) > + { > + if ( !domU ) > + mod->domU = false; What's the intention behind this (negative) accumulation of "domU"? > + return mod; > + } > + } And what's the intention behind checking existing modules here (but not existing command lines higher up in add_boot_cmdline())? > + mod = &mods->module[mods->nr_mods++]; > + mod->kind = kind; > + mod->start = start; > + mod->size = size; > + mod->domU = domU; > + > + return mod; > +} > + > +const char * __init boot_module_kind_as_string(bootmodule_kind kind) > +{ > + switch ( kind ) > + { > + case BOOTMOD_XEN: return "Xen"; > + case BOOTMOD_FDT: return "Device Tree"; > + case BOOTMOD_KERNEL: return "Kernel"; > + default: BUG(); > + } > +} > + > +/* > + * TODO: '*_end' could be 0 if the module/region is at the end of the > physical > + * address space. This is for now not handled as it requires more rework. > + */ > +static bool __init bootmodules_overlap_check(struct bootmodules *bootmodules, > + paddr_t region_start, > + paddr_t region_size) > +{ > + paddr_t mod_start = INVALID_PADDR, mod_end = 0; Pointless initializers? (The variables might benefit from moving into the more narrow scope anyway.) > + paddr_t region_end = region_start + region_size; > + unsigned int i, mod_num = bootmodules->nr_mods; > + > + for ( i = 0; i < mod_num; i++ ) > + { > + mod_start = bootmodules->module[i].start; > + mod_end = mod_start + bootmodules->module[i].size; > + > + if ( region_end <= mod_start || region_start >= mod_end ) > + continue; > + else I for one consider such an "else" misleading. > + { > + printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with" > + " mod[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n", region_start, > + region_end, i, mod_start, mod_end); > + return true; > + } > + } > + > + return false; > +} > + > +/* > + * TODO: '*_end' could be 0 if the bank/region is at the end of the physical > + * address space. This is for now not handled as it requires more rework. > + */ > +static bool __init meminfo_overlap_check(struct meminfo *meminfo, > + paddr_t region_start, > + paddr_t region_size) > +{ > + paddr_t bank_start = INVALID_PADDR, bank_end = 0; As above: Pointless initializers? (The variables might benefit from moving into the more narrow scope anyway.) > + paddr_t region_end = region_start + region_size; > + unsigned int i, bank_num = meminfo->nr_banks; > + > + for ( i = 0; i < bank_num; i++ ) > + { > + bank_start = meminfo->bank[i].start; > + bank_end = bank_start + meminfo->bank[i].size; > + > + if ( region_end <= bank_start || region_start >= bank_end ) > + continue; > + else > + { > + printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with" > + " bank[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n", region_start, > + region_end, i, bank_start, bank_end); I think this is confusing to read: When the format string already needs wrapping, I don't think the next argument should be put on the same line. I was almost going to complain that arguments don't fit the format string. > + return true; > + } > + } > + > + return false; > +} > + > +/* > + * Given an input physical address range, check if this range is overlapping > + * with the existing reserved memory regions defined in bootinfo. > + * Return true if the input physical address range is overlapping with any > + * existing reserved memory regions, otherwise false. > + */ > +bool __init check_reserved_regions_overlap(paddr_t region_start, > + paddr_t region_size) > +{ > + /* Check if input region is overlapping with bootinfo.reserved_mem banks > */ > + if ( meminfo_overlap_check(&bootinfo.reserved_mem, > + region_start, region_size) ) > + return true; > + > + /* Check if input region is overlapping with bootmodules */ > + if ( bootmodules_overlap_check(&bootinfo.modules, > + region_start, region_size) ) > + return true; > + > + return false; > +} > + > +/* > + * Return the end of the non-module region starting at s. In other > + * words return s the start of the next modules after s. Stray " s" after "return"? > + * On input *end is the end of the region which should be considered > + * and it is updated to reflect the end of the module, clipped to the > + * end of the region if it would run over. > + */ > +static paddr_t __init next_module(paddr_t s, paddr_t *end) > +{ > + struct bootmodules *mi = &bootinfo.modules; const? > + paddr_t lowest = ~(paddr_t)0; > + int i; unsigned int? > + for ( i = 0; i < mi->nr_mods; i++ ) > + { > + paddr_t mod_s = mi->module[i].start; > + paddr_t mod_e = mod_s + mi->module[i].size; > + > + if ( !mi->module[i].size ) > + continue; > + > + if ( mod_s < s ) > + continue; > + if ( mod_s > lowest ) > + continue; > + if ( mod_s > *end ) > + continue; > + lowest = mod_s; > + *end = min(*end, mod_e); > + } > + return lowest; > +} > + > +static void __init dt_unreserved_regions(paddr_t s, paddr_t e, > + void (*cb)(paddr_t ps, paddr_t pe), > + unsigned int first) > +{ > + unsigned int i; > + > + for ( i = 0 ; i < bootinfo.reserved_mem.nr_banks; i++ ) > + { > + paddr_t r_s = bootinfo.reserved_mem.bank[i].start; > + paddr_t r_e = r_s + bootinfo.reserved_mem.bank[i].size; > + > + if ( s < r_e && r_s < e ) > + { > + dt_unreserved_regions(r_e, e, cb, i + 1); > + dt_unreserved_regions(s, r_s, cb, i + 1); What's the reason for this recursion? Can there be overlapping regions? If so, is there a guaranteed depth limit (seeing in particular that you're not using the last function parameter)? > + return; > + } > + } > + > + cb(s, e); > +} > + > +/* > + * boot_cmdline_find_by_kind can only be used to return Xen modules (e.g > + * XSM, DTB) or Dom0 modules. This is not suitable for looking up guest > + * modules. > + */ > +struct bootcmdline * __init boot_cmdline_find_by_kind(bootmodule_kind kind) This function looks to be unused? > +{ > + struct bootcmdlines *cmds = &bootinfo.cmdlines; > + struct bootcmdline *cmd; > + int i; unsigned int? > + for ( i = 0 ; i < cmds->nr_mods ; i++ ) > + { > + cmd = &cmds->cmdline[i]; > + if ( cmd->kind == kind && !cmd->domU ) > + return cmd; > + } > + return NULL; > +} > + > +/* > + * Populate the boot allocator. Based on arch/arm/setup.c's > + * populate_boot_allocator. > + * All RAM but the following regions will be added to the boot allocator: > + * - Modules (e.g., Xen, Kernel) > + * - Reserved regions > + */ > +static void __init populate_boot_allocator(void) > +{ > + unsigned int i; > + const struct meminfo *banks = &bootinfo.mem; > + paddr_t s, e; > + > + for ( i = 0; i < banks->nr_banks; i++ ) > + { > + const struct membank *bank = &banks->bank[i]; > + paddr_t bank_end = bank->start + bank->size; Since you already make use of inner scope variables, and reason not to put s here and ... > + s = bank->start; > + while ( s < bank_end ) > + { > + paddr_t n = bank_end; ... e even here (each then gaining an initializer as well)? > + e = next_module(s, &n); > + > + if ( e == ~(paddr_t)0 ) > + e = n = bank_end; > + > + /* > + * Module in a RAM bank other than the one which we are > + * not dealing with here. Nit: "not"? > + */ > + if ( e > bank_end ) > + e = bank_end; > + > + dt_unreserved_regions(s, e, init_boot_pages, 0); > + > + s = n; > + } > + } > +} > + > void setup_exceptions(void) > { > unsigned long lpcr; > @@ -24,6 +304,8 @@ void __init noreturn start_xen(unsigned long r3, unsigned > long r4, > unsigned long r5, unsigned long r6, > unsigned long r7) > { > + void *boot_fdt; const? > @@ -32,11 +314,16 @@ void __init noreturn start_xen(unsigned long r3, > unsigned long r4, > else > { > /* kexec boot protocol */ > - boot_opal_init((void *)r3); > + boot_fdt = (void *)r3; And then here as well, to avoid Misra complaints. > + boot_opal_init(boot_fdt); > } > > setup_exceptions(); > > + boot_fdt_info(boot_fdt, r3); > + > + populate_boot_allocator(); > + > setup_initial_pagetables(); > > early_printk("Hello, ppc64le!\n"); > --- a/xen/common/device-tree/bootfdt.c > +++ b/xen/common/device-tree/bootfdt.c > @@ -543,12 +543,33 @@ size_t __init boot_fdt_info(const void *fdt, paddr_t > paddr) > if ( ret < 0 ) > panic("No valid device tree\n"); > > - add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), false); > - > ret = device_tree_for_each_node((void *)fdt, 0, early_scan_node, NULL); > if ( ret ) > panic("Early FDT parsing failed (%d)\n", ret); > > + /* > + * Add module for the FDT itself after the device tree has been parsed. > This > + * is required on ppc64le where the device tree passed to Xen may have > been > + * allocated by skiboot, in which case it will exist within a reserved > + * region and this call will fail. This is fine, however, since either > way > + * the allocator will know not to step on the device tree. > + */ > + add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), false); > + > + /* > + * Xen relocates itself at the ppc64 entrypoint, so we need to manually > mark > + * the kernel module. > + */ > + if ( IS_ENABLED(CONFIG_PPC64) ) { Nit (style): Brace placement. > + paddr_t xen_start, xen_end; > + > + xen_start = __pa(_start); > + xen_end = PAGE_ALIGN(__pa(_end)); > + if ( !add_boot_module(BOOTMOD_XEN, xen_start, xen_end, false) ) > + panic("Xen overlaps reserved memory! %016lx - %016lx\n", > xen_start, > + xen_end); > + } I'll need to leave commenting on this to the DT maintainers. Jan
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |