#include <assert.h>
#include <byteswap.h>
#include <ipxe/uaccess.h>
+#include <ipxe/memmap.h>
+#include <ipxe/io.h>
#include <ipxe/fdt.h>
#include <ipxe/fdtmem.h>
/** 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
* @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;
/* 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;
}
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;
}
/* 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 );
}
}
* @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;
/* 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, "<rsv>" );
+ 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
* 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;
/* 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 = "<empty>";
-
- /* 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,
- "<inaccessible>" );
- }
+ /* 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;
}
* 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;
}
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 );