From 3f6ee9573761af711f2b7752bd042d387c52b1bb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 14 May 2025 22:30:13 +0100 Subject: [PATCH] [fdtmem] Update to use the generic system memory map API Provide an implementation of the system memory map API based on the system device tree, excluding any memory outside the size of the accessible physical address space and defining an in-use region to cover the relocated copy of iPXE and the system device tree. Signed-off-by: Michael Brown --- src/arch/riscv/prefix/libprefix.S | 4 +- src/config/defaults/sbi.h | 2 +- src/core/fdtmem.c | 270 ++++++++++++++---------------- src/include/ipxe/fdtmem.h | 22 ++- src/include/ipxe/memmap.h | 1 + 5 files changed, 151 insertions(+), 148 deletions(-) diff --git a/src/arch/riscv/prefix/libprefix.S b/src/arch/riscv/prefix/libprefix.S index 834a48655..a101ba1cd 100644 --- a/src/arch/riscv/prefix/libprefix.S +++ b/src/arch/riscv/prefix/libprefix.S @@ -1003,7 +1003,7 @@ install: * s1 - device tree physical address * s2 - saved return address * s3 - relocation records physical address - * s4 - accessible physical address limit + * s4 - maximum accessible physical address * s5 - relocation physical address * s6 - relocation offset * tp - virtual address offset @@ -1030,7 +1030,7 @@ install: mv a0, a2 beqz a2, 1f call enable_paging -1: mv s4, a0 +1: addi s4, a0, -1 /* Apply relocations, if still needed after enabling paging */ mv a0, s3 diff --git a/src/config/defaults/sbi.h b/src/config/defaults/sbi.h index e12cf1af9..8e02bc3d1 100644 --- a/src/config/defaults/sbi.h +++ b/src/config/defaults/sbi.h @@ -19,7 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONSOLE_SBI #define REBOOT_SBI #define UMALLOC_SBI -#define MEMMAP_NULL +#define MEMMAP_FDT #define ACPI_NULL #define MPAPI_NULL diff --git a/src/core/fdtmem.c b/src/core/fdtmem.c index 18ae5ce96..b357287c5 100644 --- a/src/core/fdtmem.c +++ b/src/core/fdtmem.c @@ -29,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include #include @@ -44,85 +46,21 @@ extern char _prefix[]; /** End address of the iPXE image */ extern char _end[]; -/** Relocation required alignment (defined by prefix or linker) */ -extern physaddr_t ABS_SYMBOL ( _max_align ); -static physaddr_t max_align = ABS_VALUE_INIT ( _max_align ); - -/** Colour for debug messages */ -#define colour &_prefix[0] - -/** A memory region descriptor */ -struct fdtmem_region { - /** Region start address */ - physaddr_t start; - /** Region end address */ - physaddr_t end; - /** Region flags */ - int flags; - /** Region name (for debug messages) */ - const char *name; -}; - -/** Region is usable as RAM */ -#define FDTMEM_RAM 0x0001 - -/** Size of accessible physical address space (or zero for no limit) */ -static size_t fdtmem_limit; - -/** - * Update memory region descriptor - * - * @v region Memory region of interest to be updated - * @v start Start address of this region - * @v size Size of this region - * @v flags Region flags - * @v name Region name (for debugging) - */ -static void fdtmem_update ( struct fdtmem_region *region, uint64_t start, - uint64_t size, int flags, const char *name ) { - uint64_t end; - - /* Ignore empty regions */ - if ( ! size ) - return; - - /* Calculate end address (and truncate if necessary) */ - end = ( start + size - 1 ); - if ( end < start ) { - end = ~( ( uint64_t ) 0 ); - DBGC ( colour, "FDTMEM [%#08llx,%#08llx] %s truncated " - "(invalid size %#08llx)\n", - ( ( unsigned long long ) start ), - ( ( unsigned long long ) end ), name, - ( ( unsigned long long ) size ) ); - } - - /* Ignore regions entirely below the region of interest */ - if ( end < region->start ) - return; - - /* Ignore regions entirely above the region of interest */ - if ( start > region->end ) - return; - - /* Update region of interest as applicable */ - if ( start <= region->start ) { - - /* This region covers the region of interest */ - region->flags = flags; - if ( DBG_LOG ) - region->name = name; +/** Total in-memory size (calculated by linker) */ +extern size_t ABS_SYMBOL ( _memsz ); +static size_t memsz = ABS_VALUE_INIT ( _memsz ); - /* Update end address if no closer boundary exists */ - if ( end < region->end ) - region->end = end; +/** Relocation required alignment (defined by prefix or linker) */ +extern size_t ABS_SYMBOL ( _max_align ); +static size_t max_align = ABS_VALUE_INIT ( _max_align ); - } else if ( start < region->end ) { +/** In-use memory region for iPXE and system device tree copy */ +struct used_region fdtmem_used __used_region = { + .name = "iPXE/FDT", +}; - /* Update end address if no closer boundary exists */ - region->end = ( start - 1 ); - } -} +/** Maximum accessible physical address */ +static physaddr_t fdtmem_max; /** * Update memory region descriptor based on device tree node @@ -134,9 +72,9 @@ static void fdtmem_update ( struct fdtmem_region *region, uint64_t start, * @v flags Region flags * @ret rc Return status code */ -static int fdtmem_update_node ( struct fdtmem_region *region, struct fdt *fdt, +static int fdtmem_update_node ( struct memmap_region *region, struct fdt *fdt, unsigned int offset, const char *match, - int flags ) { + unsigned int flags ) { struct fdt_descriptor desc; struct fdt_reg_cells regs; const char *devtype; @@ -155,7 +93,7 @@ static int fdtmem_update_node ( struct fdtmem_region *region, struct fdt *fdt, /* Describe token */ if ( ( rc = fdt_describe ( fdt, offset, &desc ) ) != 0 ) { - DBGC ( colour, "FDTMEM has malformed node: %s\n", + DBGC ( region, "FDTMEM has malformed node: %s\n", strerror ( rc ) ); return rc; } @@ -182,7 +120,7 @@ static int fdtmem_update_node ( struct fdtmem_region *region, struct fdt *fdt, count = fdt_reg_count ( fdt, desc.offset, ®s ); if ( count < 0 ) { rc = count; - DBGC ( colour, "FDTMEM has malformed region %s: %s\n", + DBGC ( region, "FDTMEM has malformed region %s: %s\n", desc.name, strerror ( rc ) ); continue; } @@ -193,21 +131,21 @@ static int fdtmem_update_node ( struct fdtmem_region *region, struct fdt *fdt, /* Get region starting address and size */ if ( ( rc = fdt_reg_address ( fdt, desc.offset, ®s, index, &start ) ) != 0 ){ - DBGC ( colour, "FDTMEM %s region %d has " + DBGC ( region, "FDTMEM %s region %d has " "malformed start address: %s\n", desc.name, index, strerror ( rc ) ); break; } if ( ( rc = fdt_reg_size ( fdt, desc.offset, ®s, index, &size ) ) != 0 ) { - DBGC ( colour, "FDTMEM %s region %d has " + DBGC ( region, "FDTMEM %s region %d has " "malformed size: %s\n", desc.name, index, strerror ( rc ) ); break; } /* Update memory region descriptor */ - fdtmem_update ( region, start, size, flags, + memmap_update ( region, start, size, flags, desc.name ); } } @@ -222,7 +160,7 @@ static int fdtmem_update_node ( struct fdtmem_region *region, struct fdt *fdt, * @v fdt Device tree * @ret rc Return status code */ -static int fdtmem_update_tree ( struct fdtmem_region *region, +static int fdtmem_update_tree ( struct memmap_region *region, struct fdt *fdt ) { const struct fdt_reservation *rsv; unsigned int offset; @@ -230,35 +168,80 @@ static int fdtmem_update_tree ( struct fdtmem_region *region, /* Update based on memory regions in the root node */ if ( ( rc = fdtmem_update_node ( region, fdt, 0, "memory", - FDTMEM_RAM ) ) != 0 ) + MEMMAP_FL_MEMORY ) ) != 0 ) return rc; /* Update based on memory reservations block */ for_each_fdt_reservation ( rsv, fdt ) { - fdtmem_update ( region, be64_to_cpu ( rsv->start ), - be64_to_cpu ( rsv->size ), 0, "" ); + memmap_update ( region, be64_to_cpu ( rsv->start ), + be64_to_cpu ( rsv->size ), MEMMAP_FL_RESERVED, + NULL ); } /* Locate reserved-memory node */ if ( ( rc = fdt_path ( fdt, "/reserved-memory", &offset ) ) != 0 ) { - DBGC ( colour, "FDTMEM could not locate /reserved-memory: " + DBGC ( region, "FDTMEM could not locate /reserved-memory: " "%s\n", strerror ( rc ) ); return rc; } /* Update based on memory regions in the reserved-memory node */ if ( ( rc = fdtmem_update_node ( region, fdt, offset, NULL, - 0 ) ) != 0 ) + MEMMAP_FL_RESERVED ) ) != 0 ) return rc; return 0; } +/** + * Describe memory region + * + * @v addr Address within region + * @v fdt Device tree + * @v max Maximum accessible physical address + * @v region Region descriptor to fill in + */ +static void fdtmem_describe ( uint64_t addr, struct fdt *fdt, physaddr_t max, + struct memmap_region *region ) { + uint64_t inaccessible = ( ( ( uint64_t ) max ) + 1 ); + + /* Initialise region */ + memmap_init ( addr, region ); + + /* Update region based on device tree */ + fdtmem_update_tree ( region, fdt ); + + /* Treat inaccessible physical memory as such */ + memmap_update ( region, inaccessible, -inaccessible, + MEMMAP_FL_INACCESSIBLE, NULL ); +} + +/** + * Get length for copy of iPXE and device tree + * + * @v fdt Device tree + * @ret len Total length + */ +static size_t fdtmem_len ( struct fdt *fdt ) { + size_t len; + + /* Calculate total length and check device tree alignment */ + len = ( memsz + fdt->len ); + assert ( ( memsz % FDT_MAX_ALIGN ) == 0 ); + + /* Align length. Not technically necessary, but keeps the + * resulting memory maps looking relatively sane. + */ + len = ( ( len + PAGE_SIZE - 1 ) & ~( PAGE_SIZE - 1 ) ); + + return len; +} + /** * Find a relocation address for iPXE * * @v hdr FDT header - * @v limit Size of accessible physical address space (or zero) + * @v max Maximum accessible physical address * @ret new New physical address for relocation * * Find a suitably aligned address towards the top of existent memory @@ -270,13 +253,14 @@ static int fdtmem_update_tree ( struct fdtmem_region *region, * nor any function that it calls may write to or rely upon the zero * initialisation of any static variables. */ -physaddr_t fdtmem_relocate ( struct fdt_header *hdr, size_t limit ) { +physaddr_t fdtmem_relocate ( struct fdt_header *hdr, physaddr_t max ) { struct fdt fdt; - struct fdtmem_region region; + struct memmap_region region; + physaddr_t addr; + physaddr_t next; physaddr_t old; physaddr_t new; physaddr_t try; - size_t memsz; size_t len; int rc; @@ -288,73 +272,46 @@ physaddr_t fdtmem_relocate ( struct fdt_header *hdr, size_t limit ) { /* Parse FDT */ if ( ( rc = fdt_parse ( &fdt, hdr, -1UL ) ) != 0 ) { - DBGC ( colour, "FDTMEM could not parse FDT: %s\n", + DBGC ( ®ion, "FDTMEM could not parse FDT: %s\n", strerror ( rc ) ); /* Refuse relocation if we have no FDT */ return old; } /* Determine required length */ - memsz = ( _end - _prefix ); - assert ( memsz > 0 ); - assert ( ( memsz % FDT_MAX_ALIGN ) == 0 ); - len = ( memsz + fdt.len ); + len = fdtmem_len ( &fdt ); assert ( len > 0 ); - DBGC ( colour, "FDTMEM requires %#zx + %#zx => %#zx bytes for " + DBGC ( ®ion, "FDTMEM requires %#zx + %#zx => %#zx bytes for " "relocation\n", memsz, fdt.len, len ); /* Construct memory map and choose a relocation address */ - region.start = 0; new = old; - while ( 1 ) { - - /* Initialise region */ - region.end = ~( ( physaddr_t ) 0 ); - region.flags = 0; - region.name = ""; - - /* Update region based on device tree */ - if ( ( rc = fdtmem_update_tree ( ®ion, &fdt ) ) != 0 ) - break; - - /* Treat existing iPXE image as reserved */ - fdtmem_update ( ®ion, old, memsz, 0, "iPXE" ); + for ( addr = 0, next = 1 ; next ; addr = next ) { - /* Treat existing device tree as reserved */ - fdtmem_update ( ®ion, virt_to_phys ( hdr ), fdt.len, 0, - "FDT" ); - - /* Treat inaccessible physical memory as reserved */ - if ( limit ) { - fdtmem_update ( ®ion, limit, -limit, 0, - "" ); - } + /* Describe region and in-use memory */ + fdtmem_describe ( addr, &fdt, max, ®ion ); + memmap_update ( ®ion, old, memsz, MEMMAP_FL_USED, "iPXE" ); + memmap_update ( ®ion, virt_to_phys ( hdr ), fdt.len, + MEMMAP_FL_RESERVED, "FDT" ); + next = ( region.last + 1 ); /* Dump region descriptor (for debugging) */ - DBGC ( colour, "FDTMEM [%#08lx,%#08lx] %s\n", - region.start, region.end, region.name ); - assert ( region.end >= region.start ); + memmap_dump ( ®ion ); + assert ( region.last >= region.addr ); /* Use highest possible region */ - if ( ( region.flags & FDTMEM_RAM ) && - ( ( region.end - region.start ) > len ) ) { + if ( memmap_is_usable ( ®ion ) && ( next >= len ) ) { /* Determine candidate address after alignment */ - try = ( ( region.end - len - 1 ) & - ~( max_align - 1 ) ); + try = ( ( next - len ) & ~( max_align - 1 ) ); /* Use this address if within region */ - if ( try >= region.start ) + if ( try >= addr ) new = try; } - - /* Move to next region */ - region.start = ( region.end + 1 ); - if ( ! region.start ) - break; } - DBGC ( colour, "FDTMEM relocating %#08lx => [%#08lx,%#08lx]\n", + DBGC ( ®ion, "FDTMEM relocating %#08lx => [%#08lx,%#08lx]\n", old, new, ( ( physaddr_t ) ( new + len - 1 ) ) ); return new; } @@ -363,20 +320,20 @@ physaddr_t fdtmem_relocate ( struct fdt_header *hdr, size_t limit ) { * Copy and register system device tree * * @v hdr FDT header - * @v limit Size of accessible physical address space (or zero) + * @v max Maximum accessible physical address * @ret rc Return status code */ -int fdtmem_register ( struct fdt_header *hdr, size_t limit ) { +int fdtmem_register ( struct fdt_header *hdr, physaddr_t max ) { struct fdt_header *copy; struct fdt fdt; int rc; - /* Record size of accessible physical address space */ - fdtmem_limit = limit; + /* Record maximum accessible physical address */ + fdtmem_max = max; /* Parse FDT to obtain length */ if ( ( rc = fdt_parse ( &fdt, hdr, -1UL ) ) != 0 ) { - DBGC ( colour, "FDTMEM could not parse FDT: %s\n", + DBGC ( hdr, "FDTMEM could not parse FDT: %s\n", strerror ( rc ) ); return rc; } @@ -385,12 +342,41 @@ int fdtmem_register ( struct fdt_header *hdr, size_t limit ) { copy = ( ( void * ) _end ); memcpy ( copy, hdr, fdt.len ); + /* Update in-use memory region */ + memmap_use ( &fdtmem_used, virt_to_phys ( _prefix ), + fdtmem_len ( &fdt ) ); + /* Register copy as system device tree */ if ( ( rc = fdt_parse ( &sysfdt, copy, -1UL ) ) != 0 ) { - DBGC ( colour, "FDTMEM could not register FDT: %s\n", + DBGC ( hdr, "FDTMEM could not register FDT: %s\n", strerror ( rc ) ); return rc; } + assert ( sysfdt.len == fdt.len ); + + /* Dump system memory map (for debugging) */ + memmap_dump_all ( 1 ); return 0; } + +/** + * Describe memory region from system memory map + * + * @v addr Address within region + * @v hide Hide in-use regions from the memory map + * @v region Region descriptor to fill in + */ +static void fdtmem_describe_region ( uint64_t addr, int hide, + struct memmap_region *region ) { + + /* Describe memory region based on device tree */ + fdtmem_describe ( addr, &sysfdt, fdtmem_max, region ); + + /* Update memory region based on in-use regions, if applicable */ + if ( hide ) + memmap_update_used ( region ); +} + +PROVIDE_MEMMAP ( fdt, memmap_describe, fdtmem_describe_region ); +PROVIDE_MEMMAP_INLINE ( fdt, memmap_sync ); diff --git a/src/include/ipxe/fdtmem.h b/src/include/ipxe/fdtmem.h index 1bf0015b4..8d7ebfe7e 100644 --- a/src/include/ipxe/fdtmem.h +++ b/src/include/ipxe/fdtmem.h @@ -10,9 +10,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#include -extern physaddr_t fdtmem_relocate ( struct fdt_header *hdr, size_t limit ); -extern int fdtmem_register ( struct fdt_header *hdr, size_t limit ); +#ifdef MEMMAP_FDT +#define MEMMAP_PREFIX_fdt +#else +#define MEMMAP_PREFIX_fdt __fdt_ +#endif + +/** + * Synchronise in-use regions with the externally visible system memory map + * + */ +static inline __attribute__ (( always_inline )) void +MEMMAP_INLINE ( fdt, memmap_sync ) ( void ) { + /* Nothing to do */ +} + +struct fdt_header; + +extern physaddr_t fdtmem_relocate ( struct fdt_header *hdr, physaddr_t max ); +extern int fdtmem_register ( struct fdt_header *hdr, physaddr_t max ); #endif /* _IPXE_FDTMEM_H */ diff --git a/src/include/ipxe/memmap.h b/src/include/ipxe/memmap.h index 382032ee9..0262c68da 100644 --- a/src/include/ipxe/memmap.h +++ b/src/include/ipxe/memmap.h @@ -124,6 +124,7 @@ struct used_region { /* Include all architecture-independent ACPI API headers */ #include +#include /* Include all architecture-dependent ACPI API headers */ #include -- 2.47.2