git: 6aea7b224a34 - main - stand: Retire arch_loadaddr using md_align, pending its replacement
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 30 May 2025 15:57:24 UTC
The branch main has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=6aea7b224a34ccc800f9598f034838af7e47eb62 commit 6aea7b224a34ccc800f9598f034838af7e47eb62 Author: Warner Losh <imp@FreeBSD.org> AuthorDate: 2025-05-30 14:14:44 +0000 Commit: Warner Losh <imp@FreeBSD.org> CommitDate: 2025-05-30 15:56:50 +0000 stand: Retire arch_loadaddr using md_align, pending its replacement arch_loadaddr() is inconsistently used. In fact, I removed it entirely from kboot because its interface wasn't wide enough (though I broke powerpc kboot several years ago, and this commit might fix that). For now, modify uboot to put it into copyin like all the other loaders do. This might break arm ubldr. In its place, use the new md_align() and any replacement will be called from there, or via some explicit init() function. These changes allow me to load a amd64 kernel via UEFI with all the modules aligned to 16k. Sponsored by: Netflix Reviewed by: jhibbits Differential Revision: https://reviews.freebsd.org/D50587 --- stand/common/bootstrap.h | 8 -- stand/common/load_elf.c | 6 +- stand/common/load_elf_obj.c | 5 +- stand/common/modinfo.c | 2 +- stand/common/module.c | 11 +- stand/kboot/kboot/arch/aarch64/exec.c | 6 +- stand/kboot/kboot/arch/amd64/elf64_freebsd.c | 6 +- .../kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c | 17 +-- stand/uboot/copy.c | 149 ++++++++++----------- stand/uboot/libuboot.h | 1 - stand/uboot/main.c | 1 - 11 files changed, 95 insertions(+), 117 deletions(-) diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h index 88ee6de2e520..dca0cf6bb2d5 100644 --- a/stand/common/bootstrap.h +++ b/stand/common/bootstrap.h @@ -351,14 +351,6 @@ struct arch_switch int (*arch_isainb)(int port); void (*arch_isaoutb)(int port, int value); - /* - * Interface to adjust the load address according to the "object" - * being loaded. - */ - uint64_t (*arch_loadaddr)(u_int type, void *data, uint64_t addr); -#define LOAD_ELF 1 /* data points to the ELF header. */ -#define LOAD_RAW 2 /* data points to the file name. */ - /* * Interface to inform MD code about a loaded (ELF) segment. This * can be used to flush caches and/or set up translations. diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c index 537692e7d745..b9f55a21e403 100644 --- a/stand/common/load_elf.c +++ b/stand/common/load_elf.c @@ -403,6 +403,7 @@ __elfN(loadfile_raw)(char *filename, uint64_t dest, * in the elf header (an ARM kernel can be loaded at any 2MB * boundary), so we leave dest set to the value calculated by * archsw.arch_loadaddr() and passed in to this function. + * XXX This comment is obsolete, but it still seems to work */ #ifndef __arm__ if (ehdr->e_type == ET_EXEC) @@ -445,10 +446,7 @@ __elfN(loadfile_raw)(char *filename, uint64_t dest, goto oerr; } - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_ELF, ehdr, dest); - else - dest = roundup(dest, PAGE_SIZE); + dest = md_align(dest); /* * Ok, we think we should handle this. diff --git a/stand/common/load_elf_obj.c b/stand/common/load_elf_obj.c index c20da80eb602..9e32daa53696 100644 --- a/stand/common/load_elf_obj.c +++ b/stand/common/load_elf_obj.c @@ -157,10 +157,7 @@ __elfN(obj_loadfile)(char *filename, uint64_t dest, goto oerr; } - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); - else - dest = roundup(dest, PAGE_SIZE); + dest = md_align(dest); /* * Ok, we think we should handle this. diff --git a/stand/common/modinfo.c b/stand/common/modinfo.c index 100f2c704d8f..313469d32f35 100644 --- a/stand/common/modinfo.c +++ b/stand/common/modinfo.c @@ -202,7 +202,7 @@ md_copyenv(vm_offset_t start) * platform. * * XXX For the moment, it's just PAGE_SIZE to make the refactoring go faster, - * but needs to hook-in arch_loadaddr (or its replacement) functionality. + * but needs to hook-in the replacement of arch_loadaddr. * * Also, we may need other logical things when dealing with different types of * page sizes and/or masking or sizes. This works well for addr and sizes, but diff --git a/stand/common/module.c b/stand/common/module.c index ce6d095d4315..ed57ebf0caf8 100644 --- a/stand/common/module.c +++ b/stand/common/module.c @@ -43,6 +43,7 @@ #endif #include "bootstrap.h" +#include "modinfo.h" #define MDIR_REMOVED 0x0001 #define MDIR_NOHINTS 0x0002 @@ -562,8 +563,7 @@ file_load(char *filename, vm_offset_t dest, struct preloaded_file **result) int i; TSENTER2(filename); - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); + dest = md_align(dest); error = EFTYPE; for (i = last_file_format, fp = NULL; @@ -713,8 +713,7 @@ file_loadraw(const char *fname, const char *type, int insert) #endif #endif - if (archsw.arch_loadaddr != NULL) - loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); + loadaddr = md_align(loadaddr); if (module_verbose > MODULE_VERBOSE_SILENT) printf("%s ", name); @@ -1015,9 +1014,7 @@ file_addbuf(const char *name, const char *type, size_t len, void *buf) } /* Figure out where to load the data. */ - dest = loadaddr; - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_RAW, (void *)name, dest); + dest = md_align(loadaddr); /* Create & populate control structure */ fp = file_alloc(); diff --git a/stand/kboot/kboot/arch/aarch64/exec.c b/stand/kboot/kboot/arch/aarch64/exec.c index 40eb57f371a9..df3e922dfe11 100644 --- a/stand/kboot/kboot/arch/aarch64/exec.c +++ b/stand/kboot/kboot/arch/aarch64/exec.c @@ -188,9 +188,9 @@ elf64_exec(struct preloaded_file *fp) /* * Figure out where to put it. * - * Linux does not allow us to kexec_load into any part of memory. Ask - * arch_loadaddr to resolve the first available chunk of physical memory - * where loading is possible (load_addr). + * Linux does not allow us to kexec_load into any part of memory. Find + * the first available chunk of physical memory where loading is + * possible (staging). * * The kernel is loaded at the 'base' address in continguous physical * memory. We use the 2MB in front of the kernel as a place to put our diff --git a/stand/kboot/kboot/arch/amd64/elf64_freebsd.c b/stand/kboot/kboot/arch/amd64/elf64_freebsd.c index 330646715289..576839e29734 100644 --- a/stand/kboot/kboot/arch/amd64/elf64_freebsd.c +++ b/stand/kboot/kboot/arch/amd64/elf64_freebsd.c @@ -178,9 +178,9 @@ elf64_exec(struct preloaded_file *fp) /* * Figure out where to put it. * - * Linux does not allow to do kexec_load into any part of memory. Ask - * arch_loadaddr to resolve the first available chunk of physical memory - * where loading is possible (load_addr). + * Linux does not allow us to do kexec_load into any part of memory. Ask + * kboot_get_phys_load_segment to resolve the first available chunk of + * physical memory where loading is possible (staging). * * The kernel is loaded at the 'base' address in continguous physical * pages (using 2MB super pages). The first such page is unused by the diff --git a/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c b/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c index ffa00e151661..ae398e357df2 100644 --- a/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c +++ b/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c @@ -89,21 +89,18 @@ ppc64_elf_exec(struct preloaded_file *fp) /* * Figure out where to put it. * - * Linux does not allow to do kexec_load into - * any part of memory. Ask arch_loadaddr to - * resolve the first available chunk of physical - * memory where loading is possible (load_addr). + * Linux does not allow us to do kexec_load into any part of memory. Ask + * kboot_get_phys_load_segment to resolve the first available chunk of + * physical memory where loading is possible (load_addr). * - * Memory organization is shown below. - * It is assumed, that text segment offset of - * kernel ELF (KERNPHYSADDR) is non-zero, - * which is true for PPC/PPC64 architectures, - * where default is 0x100000. + * Memory organization is shown below. It is assumed, that text segment + * offset of kernel ELF (KERNPHYSADDR) is non-zero, which is true for + * PPC/PPC64 architectures, where default is 0x100000. * * load_addr: trampoline code * load_addr + KERNPHYSADDR: kernel text segment */ - trampolinebase = archsw.arch_loadaddr(LOAD_RAW, NULL, 0); + trampolinebase = kboot_get_phys_load_segment(); printf("Load address at %#jx\n", (uintmax_t)trampolinebase); printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset); diff --git a/stand/uboot/copy.c b/stand/uboot/copy.c index e5e985c85b27..7758e754666b 100644 --- a/stand/uboot/copy.c +++ b/stand/uboot/copy.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> - * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com> + * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,7 +35,7 @@ #include "libuboot.h" /* - * MD primitives supporting placement of module data + * MD primitives supporting placement of module data */ #ifdef __arm__ @@ -51,20 +51,17 @@ extern void _start(void); /* ubldr entry point address. */ +uint64_t loadbase; +bool loadbase_set = false; + /* * This is called for every object loaded (kernel, module, dtb file, etc). The * expected return value is the next address at or after the given addr which is * appropriate for loading the given object described by type and data. On each * call the addr is the next address following the previously loaded object. - * - * The first call is for loading the kernel, and the addr argument will be zero, - * and we search for a big block of ram to load the kernel and modules. - * - * On subsequent calls the addr will be non-zero, and we just round it up so - * that each object begins on a page boundary. */ -uint64_t -uboot_loadaddr(u_int type, void *data, uint64_t addr) +static uint64_t +uboot_loadaddr(void) { struct sys_info *si; uint64_t sblock, eblock, subldr, eubldr; @@ -73,87 +70,89 @@ uboot_loadaddr(u_int type, void *data, uint64_t addr) int i; char *envstr; - if (addr == 0) { - /* - * If the loader_kernaddr environment variable is set, blindly - * honor it. It had better be right. We force interpretation - * of the value in base-16 regardless of any leading 0x prefix, - * because that's the U-Boot convention. - */ - envstr = ub_env_get("loader_kernaddr"); - if (envstr != NULL) - return (strtoul(envstr, NULL, 16)); - - /* - * Find addr/size of largest DRAM block. Carve our own address - * range out of the block, because loading the kernel over the - * top ourself is a poor memory-conservation strategy. Avoid - * memory at beginning of the first block of physical ram, - * since u-boot likes to pass args and data there. Assume that - * u-boot has moved itself to the very top of ram and - * optimistically assume that we won't run into it up there. - */ - if ((si = ub_get_sys_info()) == NULL) - panic("could not retrieve system info"); - - biggest_block = 0; - biggest_size = 0; - subldr = rounddown2((uintptr_t)_start, KERN_ALIGN); - eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN); - for (i = 0; i < si->mr_no; i++) { - if (si->mr[i].flags != MR_ATTR_DRAM) - continue; - sblock = roundup2((uint64_t)si->mr[i].start, - KERN_ALIGN); - eblock = rounddown2((uint64_t)si->mr[i].start + - si->mr[i].size, KERN_ALIGN); - if (biggest_size == 0) - sblock += KERN_MINADDR; - if (subldr >= sblock && subldr < eblock) { - if (subldr - sblock > eblock - eubldr) { - this_block = sblock; - this_size = subldr - sblock; - } else { - this_block = eubldr; - this_size = eblock - eubldr; - } - } else if (subldr < sblock && eubldr < eblock) { - /* Loader is below or engulfs the sblock */ - this_block = (eubldr < sblock) ? sblock : eubldr; - this_size = eblock - this_block; + /* + * If the loader_kernaddr environment variable is set, blindly + * honor it. It had better be right. We force interpretation + * of the value in base-16 regardless of any leading 0x prefix, + * because that's the U-Boot convention. + */ + envstr = ub_env_get("loader_kernaddr"); + if (envstr != NULL) + return (strtoul(envstr, NULL, 16)); + + /* + * Find addr/size of largest DRAM block. Carve our own address + * range out of the block, because loading the kernel over the + * top ourself is a poor memory-conservation strategy. Avoid + * memory at beginning of the first block of physical ram, + * since u-boot likes to pass args and data there. Assume that + * u-boot has moved itself to the very top of ram and + * optimistically assume that we won't run into it up there. + */ + if ((si = ub_get_sys_info()) == NULL) + panic("could not retrieve system info"); + + biggest_block = 0; + biggest_size = 0; + subldr = rounddown2((uintptr_t)_start, KERN_ALIGN); + eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN); + for (i = 0; i < si->mr_no; i++) { + if (si->mr[i].flags != MR_ATTR_DRAM) + continue; + sblock = roundup2((uint64_t)si->mr[i].start, + KERN_ALIGN); + eblock = rounddown2((uint64_t)si->mr[i].start + + si->mr[i].size, KERN_ALIGN); + if (biggest_size == 0) + sblock += KERN_MINADDR; + if (subldr >= sblock && subldr < eblock) { + if (subldr - sblock > eblock - eubldr) { + this_block = sblock; + this_size = subldr - sblock; } else { - this_block = 0; - this_size = 0; - } - if (biggest_size < this_size) { - biggest_block = this_block; - biggest_size = this_size; + this_block = eubldr; + this_size = eblock - eubldr; } + } else if (subldr < sblock && eubldr < eblock) { + /* Loader is below or engulfs the sblock */ + this_block = (eubldr < sblock) ? sblock : eubldr; + this_size = eblock - this_block; + } else { + this_block = 0; + this_size = 0; } - if (biggest_size == 0) - panic("Not enough DRAM to load kernel"); + if (biggest_size < this_size) { + biggest_block = this_block; + biggest_size = this_size; + } + } + if (biggest_size == 0) + panic("Not enough DRAM to load kernel"); #if 0 - printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n", - (uintmax_t)biggest_block, - (uintmax_t)biggest_block + biggest_size - 1, - (uintmax_t)biggest_size / 1024 / 1024); + printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n", + (uintmax_t)biggest_block, + (uintmax_t)biggest_block + biggest_size - 1, + (uintmax_t)biggest_size / 1024 / 1024); #endif - return (biggest_block); - } - return roundup2(addr, PAGE_SIZE); + return (biggest_block); } ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len) { - bcopy(src, (void *)dest, len); + if (!loadbase_set) { + loadbase = uboot_loadaddr(); + loadbase_set = true; + } + + bcopy(src, (void *)(dest + loadbase), len); return (len); } ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len) { - bcopy((void *)src, dest, len); + bcopy((void *)(src + loadbase), dest, len); return (len); } diff --git a/stand/uboot/libuboot.h b/stand/uboot/libuboot.h index 4723600b0cee..49bfff3ada6f 100644 --- a/stand/uboot/libuboot.h +++ b/stand/uboot/libuboot.h @@ -57,7 +57,6 @@ extern struct devsw uboot_storage; extern uintptr_t uboot_heap_start; extern uintptr_t uboot_heap_end; -uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr); ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len); ssize_t uboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); diff --git a/stand/uboot/main.c b/stand/uboot/main.c index 61b691f7edfa..6f53c83543ba 100644 --- a/stand/uboot/main.c +++ b/stand/uboot/main.c @@ -41,7 +41,6 @@ struct uboot_devdesc currdev; struct arch_switch archsw = { /* MI/MD interface boundary */ - .arch_loadaddr = uboot_loadaddr, .arch_getdev = uboot_getdev, .arch_copyin = uboot_copyin, .arch_copyout = uboot_copyout,