summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/acpi.c152
-rw-r--r--src/core/acpi_settings.c23
-rw-r--r--src/core/acpimac.c18
-rw-r--r--src/core/ansicol.c1
-rw-r--r--src/core/ansicoldef.c1
-rw-r--r--src/core/ansiesc.c1
-rw-r--r--src/core/archive.c16
-rw-r--r--src/core/asprintf.c1
-rw-r--r--src/core/base16.c1
-rw-r--r--src/core/base64.c1
-rw-r--r--src/core/basename.c1
-rw-r--r--src/core/bitmap.c1
-rw-r--r--src/core/blockdev.c9
-rw-r--r--src/core/blocktrans.c98
-rw-r--r--src/core/cachedhcp.c9
-rw-r--r--src/core/console.c1
-rw-r--r--src/core/cpio.c143
-rw-r--r--src/core/ctype.c1
-rw-r--r--src/core/cwuri.c1
-rw-r--r--src/core/debug.c1
-rw-r--r--src/core/device.c1
-rw-r--r--src/core/dma.c23
-rw-r--r--src/core/downloader.c19
-rw-r--r--src/core/dummy_sanboot.c1
-rw-r--r--src/core/dynkeymap.c1
-rw-r--r--src/core/dynui.c234
-rw-r--r--src/core/edd.c1
-rw-r--r--src/core/errno.c1
-rw-r--r--src/core/exec.c26
-rw-r--r--src/core/fbcon.c205
-rw-r--r--src/core/fdt.c1323
-rw-r--r--src/core/fdtcon.c108
-rw-r--r--src/core/fdtmem.c401
-rw-r--r--src/core/fnrec.c1
-rw-r--r--src/core/gdbserial.c55
-rw-r--r--src/core/gdbstub.c1
-rw-r--r--src/core/getkey.c1
-rw-r--r--src/core/getopt.c1
-rw-r--r--src/core/gpio.c165
-rw-r--r--src/core/image.c86
-rw-r--r--src/core/init.c5
-rw-r--r--src/core/interface.c1
-rw-r--r--src/core/iobuf.c53
-rw-r--r--src/core/isqrt.c1
-rw-r--r--src/core/job.c1
-rw-r--r--src/core/keymap.c1
-rw-r--r--src/core/linebuf.c1
-rw-r--r--src/core/lineconsole.c1
-rw-r--r--src/core/list.c1
-rw-r--r--src/core/main.c1
-rw-r--r--src/core/malloc.c425
-rw-r--r--src/core/memmap.c144
-rw-r--r--src/core/memmap_settings.c57
-rw-r--r--src/core/menu.c191
-rw-r--r--src/core/monojob.c1
-rw-r--r--src/core/netbios.c1
-rw-r--r--src/core/null_pci.c43
-rw-r--r--src/core/null_reboot.c4
-rw-r--r--src/core/null_smbios.c47
-rw-r--r--src/core/null_time.c14
-rw-r--r--src/core/nvo.c1
-rw-r--r--src/core/open.c1
-rw-r--r--src/core/params.c1
-rw-r--r--src/core/parseopt.c19
-rw-r--r--src/core/pending.c1
-rw-r--r--src/core/pinger.c1
-rw-r--r--src/core/pixbuf.c5
-rw-r--r--src/core/pool.c1
-rw-r--r--src/core/posix_io.c9
-rw-r--r--src/core/process.c2
-rw-r--r--src/core/profile.c1
-rw-r--r--src/core/quiesce.c1
-rw-r--r--src/core/random.c1
-rw-r--r--src/core/refcnt.c1
-rw-r--r--src/core/resolv.c1
-rw-r--r--src/core/sanboot.c27
-rw-r--r--src/core/serial.c94
-rw-r--r--src/core/settings.c2
-rw-r--r--src/core/spcr.c163
-rw-r--r--src/core/string.c18
-rw-r--r--src/core/time.c1
-rw-r--r--src/core/timer.c2
-rw-r--r--src/core/uaccess.c40
-rw-r--r--src/core/uart.c179
-rw-r--r--src/core/uheap.c197
-rw-r--r--src/core/uri.c1
-rw-r--r--src/core/utf8.c1
-rw-r--r--src/core/uuid.c1
-rw-r--r--src/core/version.c9
-rw-r--r--src/core/vsprintf.c1
-rw-r--r--src/core/wchar.c20
-rw-r--r--src/core/xfer.c1
-rw-r--r--src/core/xferbuf.c127
93 files changed, 3847 insertions, 1210 deletions
diff --git a/src/core/acpi.c b/src/core/acpi.c
index 526bf8555..d8c1903f3 100644
--- a/src/core/acpi.c
+++ b/src/core/acpi.c
@@ -22,10 +22,13 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/uaccess.h>
+#include <ipxe/iomap.h>
#include <ipxe/acpi.h>
#include <ipxe/interface.h>
@@ -54,25 +57,17 @@ typeof ( acpi_find ) *acpi_finder __attribute__ (( weak )) = acpi_find;
/**
* Compute ACPI table checksum
*
- * @v table Any ACPI table
+ * @v acpi Any ACPI table header
* @ret checksum 0 if checksum is good
*/
-static uint8_t acpi_checksum ( userptr_t table ) {
- struct acpi_header acpi;
+static uint8_t acpi_checksum ( const struct acpi_header *acpi ) {
+ const uint8_t *byte = ( ( const void * ) acpi );
+ size_t len = le32_to_cpu ( acpi->length );
uint8_t sum = 0;
- uint8_t data = 0;
- unsigned int i;
-
- /* Read table length */
- copy_from_user ( &acpi.length, table,
- offsetof ( typeof ( acpi ), length ),
- sizeof ( acpi.length ) );
/* Compute checksum */
- for ( i = 0 ; i < le32_to_cpu ( acpi.length ) ; i++ ) {
- copy_from_user ( &data, table, i, sizeof ( data ) );
- sum += data;
- }
+ while ( len-- )
+ sum += *(byte++);
return sum;
}
@@ -85,7 +80,7 @@ static uint8_t acpi_checksum ( userptr_t table ) {
void acpi_fix_checksum ( struct acpi_header *acpi ) {
/* Update checksum */
- acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) );
+ acpi->checksum -= acpi_checksum ( acpi );
}
/**
@@ -93,9 +88,10 @@ void acpi_fix_checksum ( struct acpi_header *acpi ) {
*
* @v signature Requested table signature
* @v index Requested index of table with this signature
- * @ret table Table, or UNULL if not found
+ * @ret table Table, or NULL if not found
*/
-userptr_t acpi_table ( uint32_t signature, unsigned int index ) {
+const struct acpi_header * acpi_table ( uint32_t signature,
+ unsigned int index ) {
return ( *acpi_finder ) ( signature, index );
}
@@ -105,14 +101,12 @@ userptr_t acpi_table ( uint32_t signature, unsigned int index ) {
*
* @v signature Requested table signature
* @v index Requested index of table with this signature
- * @ret table Table, or UNULL if not found
+ * @ret table Table, or NULL if not found
*/
-userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index ) {
- struct acpi_header acpi;
- struct acpi_rsdt *rsdtab;
- typeof ( rsdtab->entry[0] ) entry;
- userptr_t rsdt;
- userptr_t table;
+const struct acpi_header * acpi_find_via_rsdt ( uint32_t signature,
+ unsigned int index ) {
+ const struct acpi_rsdt *rsdt;
+ const struct acpi_header *table;
size_t len;
unsigned int count;
unsigned int i;
@@ -121,45 +115,38 @@ userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index ) {
rsdt = acpi_find_rsdt();
if ( ! rsdt ) {
DBG ( "RSDT not found\n" );
- return UNULL;
+ return NULL;
}
/* Read RSDT header */
- copy_from_user ( &acpi, rsdt, 0, sizeof ( acpi ) );
- if ( acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
+ if ( rsdt->acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) {
DBGC ( colour, "RSDT %#08lx has invalid signature:\n",
- user_to_phys ( rsdt, 0 ) );
- DBGC_HDA ( colour, user_to_phys ( rsdt, 0 ), &acpi,
- sizeof ( acpi ) );
- return UNULL;
+ virt_to_phys ( rsdt ) );
+ DBGC_HDA ( colour, virt_to_phys ( rsdt ), &rsdt->acpi,
+ sizeof ( rsdt->acpi ) );
+ return NULL;
}
- len = le32_to_cpu ( acpi.length );
- if ( len < sizeof ( rsdtab->acpi ) ) {
+ len = le32_to_cpu ( rsdt->acpi.length );
+ if ( len < sizeof ( rsdt->acpi ) ) {
DBGC ( colour, "RSDT %#08lx has invalid length:\n",
- user_to_phys ( rsdt, 0 ) );
- DBGC_HDA ( colour, user_to_phys ( rsdt, 0 ), &acpi,
- sizeof ( acpi ) );
- return UNULL;
+ virt_to_phys ( rsdt ) );
+ DBGC_HDA ( colour, virt_to_phys ( rsdt ), &rsdt->acpi,
+ sizeof ( rsdt->acpi ) );
+ return NULL;
}
/* Calculate number of entries */
- count = ( ( len - sizeof ( rsdtab->acpi ) ) / sizeof ( entry ) );
+ count = ( ( len - sizeof ( rsdt->acpi ) ) /
+ sizeof ( rsdt->entry[0] ) );
/* Search through entries */
for ( i = 0 ; i < count ; i++ ) {
- /* Get table address */
- copy_from_user ( &entry, rsdt,
- offsetof ( typeof ( *rsdtab ), entry[i] ),
- sizeof ( entry ) );
-
/* Read table header */
- table = phys_to_user ( entry );
- copy_from_user ( &acpi.signature, table, 0,
- sizeof ( acpi.signature ) );
+ table = phys_to_virt ( rsdt->entry[i] );
/* Check table signature */
- if ( acpi.signature != cpu_to_le32 ( signature ) )
+ if ( table->signature != cpu_to_le32 ( signature ) )
continue;
/* Check index */
@@ -169,21 +156,21 @@ userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index ) {
/* Check table integrity */
if ( acpi_checksum ( table ) != 0 ) {
DBGC ( colour, "RSDT %#08lx found %s with bad "
- "checksum at %08lx\n", user_to_phys ( rsdt, 0 ),
+ "checksum at %#08lx\n", virt_to_phys ( rsdt ),
acpi_name ( signature ),
- user_to_phys ( table, 0 ) );
+ virt_to_phys ( table ) );
break;
}
- DBGC ( colour, "RSDT %#08lx found %s at %08lx\n",
- user_to_phys ( rsdt, 0 ), acpi_name ( signature ),
- user_to_phys ( table, 0 ) );
+ DBGC ( colour, "RSDT %#08lx found %s at %#08lx\n",
+ virt_to_phys ( rsdt ), acpi_name ( signature ),
+ virt_to_phys ( table ) );
return table;
}
DBGC ( colour, "RSDT %#08lx could not find %s\n",
- user_to_phys ( rsdt, 0 ), acpi_name ( signature ) );
- return UNULL;
+ virt_to_phys ( rsdt ), acpi_name ( signature ) );
+ return NULL;
}
/**
@@ -195,30 +182,31 @@ userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index ) {
* @v extract Extraction method
* @ret rc Return status code
*/
-static int acpi_zsdt ( userptr_t zsdt, uint32_t signature, void *data,
- int ( * extract ) ( userptr_t zsdt, size_t len,
- size_t offset, void *data ) ) {
- struct acpi_header acpi;
+static int acpi_zsdt ( const struct acpi_header *zsdt,
+ uint32_t signature, void *data,
+ int ( * extract ) ( const struct acpi_header *zsdt,
+ size_t len, size_t offset,
+ void *data ) ) {
uint32_t buf;
size_t offset;
size_t len;
int rc;
/* Read table header */
- copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
- len = le32_to_cpu ( acpi.length );
+ len = le32_to_cpu ( zsdt->length );
/* Locate signature */
- for ( offset = sizeof ( acpi ) ;
+ for ( offset = sizeof ( *zsdt ) ;
( ( offset + sizeof ( buf ) /* signature */ ) < len ) ;
offset++ ) {
/* Check signature */
- copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
+ memcpy ( &buf, ( ( ( const void * ) zsdt ) + offset ),
+ sizeof ( buf ) );
if ( buf != cpu_to_le32 ( signature ) )
continue;
DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
- user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
+ virt_to_phys ( zsdt ), acpi_name ( signature ),
offset );
/* Attempt to extract data */
@@ -238,20 +226,20 @@ static int acpi_zsdt ( userptr_t zsdt, uint32_t signature, void *data,
* @ret rc Return status code
*/
int acpi_extract ( uint32_t signature, void *data,
- int ( * extract ) ( userptr_t zsdt, size_t len,
- size_t offset, void *data ) ) {
- struct acpi_fadt fadtab;
- userptr_t fadt;
- userptr_t dsdt;
- userptr_t ssdt;
+ int ( * extract ) ( const struct acpi_header *zsdt,
+ size_t len, size_t offset,
+ void *data ) ) {
+ const struct acpi_fadt *fadt;
+ const struct acpi_header *dsdt;
+ const struct acpi_header *ssdt;
unsigned int i;
int rc;
/* Try DSDT first */
- fadt = acpi_table ( FADT_SIGNATURE, 0 );
+ fadt = container_of ( acpi_table ( FADT_SIGNATURE, 0 ),
+ struct acpi_fadt, acpi );
if ( fadt ) {
- copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
- dsdt = phys_to_user ( fadtab.dsdt );
+ dsdt = phys_to_virt ( fadt->dsdt );
if ( ( rc = acpi_zsdt ( dsdt, signature, data,
extract ) ) == 0 )
return 0;
@@ -272,6 +260,26 @@ int acpi_extract ( uint32_t signature, void *data,
return -ENOENT;
}
+/**
+ * Map an ACPI generic address
+ *
+ * @v address Generic address
+ * @v len Length of region
+ * @ret io_addr I/O address, or NULL on error
+ */
+void * acpi_ioremap ( struct acpi_address *address, size_t len ) {
+ physaddr_t base = le64_to_cpu ( address->address );
+
+ switch ( address->type ) {
+ case ACPI_ADDRESS_TYPE_MEM:
+ return ioremap ( base, len );
+ case ACPI_ADDRESS_TYPE_IO:
+ return ( ( void * ) base );
+ default:
+ return NULL;
+ }
+}
+
/******************************************************************************
*
* Descriptors
diff --git a/src/core/acpi_settings.c b/src/core/acpi_settings.c
index b9e2b7f61..8dc2a7fd8 100644
--- a/src/core/acpi_settings.c
+++ b/src/core/acpi_settings.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -64,14 +65,15 @@ static int acpi_settings_applies ( struct settings *settings __unused,
static int acpi_settings_fetch ( struct settings *settings,
struct setting *setting,
void *data, size_t len ) {
- struct acpi_header acpi;
+ const struct acpi_header *acpi;
+ const uint8_t *src;
+ uint8_t *dst;
uint32_t tag_high;
uint32_t tag_low;
uint32_t tag_signature;
unsigned int tag_index;
size_t tag_offset;
size_t tag_len;
- userptr_t table;
size_t offset;
size_t max_len;
int delta;
@@ -88,15 +90,12 @@ static int acpi_settings_fetch ( struct settings *settings,
acpi_name ( tag_signature ), tag_index, tag_offset, tag_len );
/* Locate ACPI table */
- table = acpi_table ( tag_signature, tag_index );
- if ( ! table )
+ acpi = acpi_table ( tag_signature, tag_index );
+ if ( ! acpi )
return -ENOENT;
- /* Read table header */
- copy_from_user ( &acpi, table, 0, sizeof ( acpi ) );
-
/* Calculate starting offset and maximum available length */
- max_len = le32_to_cpu ( acpi.length );
+ max_len = le32_to_cpu ( acpi->length );
if ( tag_offset > max_len )
return -ENOENT;
offset = tag_offset;
@@ -115,10 +114,11 @@ static int acpi_settings_fetch ( struct settings *settings,
}
/* Read data */
+ src = ( ( ( const void * ) acpi ) + offset );
+ dst = data;
for ( i = 0 ; ( ( i < max_len ) && ( i < len ) ) ; i++ ) {
- copy_from_user ( data, table, offset, 1 );
- data++;
- offset += delta;
+ *(dst++) = *src;
+ src += delta;
}
/* Set type if not already specified */
@@ -157,5 +157,6 @@ static void acpi_settings_init ( void ) {
/** ACPI settings initialiser */
struct init_fn acpi_settings_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "acpi",
.initialise = acpi_settings_init,
};
diff --git a/src/core/acpimac.c b/src/core/acpimac.c
index e0074ba43..04fd98836 100644
--- a/src/core/acpimac.c
+++ b/src/core/acpimac.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
@@ -142,8 +143,9 @@ static struct acpimac_extractor acpimac_rtxmac = {
* string that appears shortly after an "AMAC" or "MACA" signature.
* This should work for most implementations encountered in practice.
*/
-static int acpimac_extract ( userptr_t zsdt, size_t len, size_t offset,
- void *data, struct acpimac_extractor *extractor ){
+static int acpimac_extract ( const struct acpi_header *zsdt, size_t len,
+ size_t offset, void *data,
+ struct acpimac_extractor *extractor ) {
size_t prefix_len = strlen ( extractor->prefix );
uint8_t *hw_addr = data;
size_t skip = 0;
@@ -161,8 +163,8 @@ static int acpimac_extract ( userptr_t zsdt, size_t len, size_t offset,
skip++ ) {
/* Read value */
- copy_from_user ( buf, zsdt, ( offset + skip ),
- sizeof ( buf ) );
+ memcpy ( buf, ( ( ( const void * ) zsdt ) + offset + skip ),
+ sizeof ( buf ) );
/* Check for expected format */
if ( memcmp ( buf, extractor->prefix, prefix_len ) != 0 )
@@ -203,8 +205,8 @@ static int acpimac_extract ( userptr_t zsdt, size_t len, size_t offset,
* @v data Data buffer
* @ret rc Return status code
*/
-static int acpimac_extract_auxmac ( userptr_t zsdt, size_t len, size_t offset,
- void *data ) {
+static int acpimac_extract_auxmac ( const struct acpi_header *zsdt,
+ size_t len, size_t offset, void *data ) {
return acpimac_extract ( zsdt, len, offset, data, &acpimac_auxmac );
}
@@ -218,8 +220,8 @@ static int acpimac_extract_auxmac ( userptr_t zsdt, size_t len, size_t offset,
* @v data Data buffer
* @ret rc Return status code
*/
-static int acpimac_extract_rtxmac ( userptr_t zsdt, size_t len, size_t offset,
- void *data ) {
+static int acpimac_extract_rtxmac ( const struct acpi_header *zsdt,
+ size_t len, size_t offset, void *data ) {
return acpimac_extract ( zsdt, len, offset, data, &acpimac_rtxmac );
}
diff --git a/src/core/ansicol.c b/src/core/ansicol.c
index ddf9ba77c..d53ebeeb6 100644
--- a/src/core/ansicol.c
+++ b/src/core/ansicol.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdio.h>
#include <errno.h>
diff --git a/src/core/ansicoldef.c b/src/core/ansicoldef.c
index 6d8598e11..4555c4e36 100644
--- a/src/core/ansicoldef.c
+++ b/src/core/ansicoldef.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdio.h>
#include <errno.h>
diff --git a/src/core/ansiesc.c b/src/core/ansiesc.c
index 7f545db0e..57a2345d7 100644
--- a/src/core/ansiesc.c
+++ b/src/core/ansiesc.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <assert.h>
diff --git a/src/core/archive.c b/src/core/archive.c
index bb62c7e47..8b6accc6a 100644
--- a/src/core/archive.c
+++ b/src/core/archive.c
@@ -43,7 +43,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
int image_extract ( struct image *image, const char *name,
struct image **extracted ) {
- char *dot;
int rc;
/* Check that this image can be used to extract an archive image */
@@ -66,10 +65,8 @@ int image_extract ( struct image *image, const char *name,
}
/* Strip any archive or compression suffix from implicit name */
- if ( ( ! name ) && ( (*extracted)->name ) &&
- ( ( dot = strrchr ( (*extracted)->name, '.' ) ) != NULL ) ) {
- *dot = '\0';
- }
+ if ( ! name )
+ image_strip_suffix ( *extracted );
/* Try extracting archive image */
if ( ( rc = image->type->extract ( image, *extracted ) ) != 0 ) {
@@ -122,9 +119,14 @@ int image_extract_exec ( struct image *image ) {
/* Set auto-unregister flag */
extracted->flags |= IMAGE_AUTO_UNREGISTER;
- /* Tail-recurse into extracted image */
- return image_exec ( extracted );
+ /* Replace current image */
+ if ( ( rc = image_replace ( extracted ) ) != 0 )
+ goto err_replace;
+
+ /* Return to allow replacement image to be executed */
+ return 0;
+ err_replace:
err_set_cmdline:
unregister_image ( extracted );
err_extract:
diff --git a/src/core/asprintf.c b/src/core/asprintf.c
index 00edf8e11..17a65c715 100644
--- a/src/core/asprintf.c
+++ b/src/core/asprintf.c
@@ -5,6 +5,7 @@
#include <errno.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* Write a formatted string to newly allocated memory.
diff --git a/src/core/base16.c b/src/core/base16.c
index 47e35f414..0c597480f 100644
--- a/src/core/base16.c
+++ b/src/core/base16.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdio.h>
diff --git a/src/core/base64.c b/src/core/base64.c
index ec11be261..fe7198c42 100644
--- a/src/core/base64.c
+++ b/src/core/base64.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
diff --git a/src/core/basename.c b/src/core/basename.c
index f4f929517..7a903c25f 100644
--- a/src/core/basename.c
+++ b/src/core/basename.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/core/bitmap.c b/src/core/bitmap.c
index 2aac33870..e3570c629 100644
--- a/src/core/bitmap.c
+++ b/src/core/bitmap.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <errno.h>
#include <ipxe/bitmap.h>
diff --git a/src/core/blockdev.c b/src/core/blockdev.c
index c219d9673..ff0f3b68b 100644
--- a/src/core/blockdev.c
+++ b/src/core/blockdev.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <errno.h>
#include <ipxe/interface.h>
@@ -45,8 +46,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @ret rc Return status code
*/
int block_read ( struct interface *control, struct interface *data,
- uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len ) {
+ uint64_t lba, unsigned int count, void *buffer,
+ size_t len ) {
struct interface *dest;
block_read_TYPE ( void * ) *op =
intf_get_dest_op ( control, block_read, &dest );
@@ -76,8 +77,8 @@ int block_read ( struct interface *control, struct interface *data,
* @ret rc Return status code
*/
int block_write ( struct interface *control, struct interface *data,
- uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len ) {
+ uint64_t lba, unsigned int count, void *buffer,
+ size_t len ) {
struct interface *dest;
block_write_TYPE ( void * ) *op =
intf_get_dest_op ( control, block_write, &dest );
diff --git a/src/core/blocktrans.c b/src/core/blocktrans.c
index f9dcb95d2..d9c24582c 100644
--- a/src/core/blocktrans.c
+++ b/src/core/blocktrans.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -31,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/iobuf.h>
@@ -39,91 +41,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/blocktrans.h>
/**
- * Reallocate block device translator data buffer
- *
- * @v xferbuf Data transfer buffer
- * @v len New length (or zero to free buffer)
- * @ret rc Return status code
- */
-static int blktrans_xferbuf_realloc ( struct xfer_buffer *xferbuf,
- size_t len ) {
- struct block_translator *blktrans =
- container_of ( xferbuf, struct block_translator, xferbuf );
-
- /* Record length, if applicable */
- if ( blktrans->buffer ) {
-
- /* We have a (non-reallocatable) data buffer */
- return -ENOTSUP;
-
- } else {
-
- /* Record length (for block device capacity) */
- xferbuf->len = len;
- return 0;
- }
-}
-
-/**
- * Write data to block device translator data buffer
- *
- * @v xferbuf Data transfer buffer
- * @v offset Starting offset
- * @v data Data to copy
- * @v len Length of data
- */
-static void blktrans_xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset,
- const void *data, size_t len ) {
- struct block_translator *blktrans =
- container_of ( xferbuf, struct block_translator, xferbuf );
-
- /* Write data to buffer, if applicable */
- if ( blktrans->buffer ) {
-
- /* Write data to buffer */
- copy_to_user ( blktrans->buffer, offset, data, len );
-
- } else {
-
- /* Sanity check */
- assert ( len == 0 );
- }
-}
-
-/**
- * Read data from block device translator data buffer
- *
- * @v xferbuf Data transfer buffer
- * @v offset Starting offset
- * @v data Data to read
- * @v len Length of data
- */
-static void blktrans_xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset,
- void *data, size_t len ) {
- struct block_translator *blktrans =
- container_of ( xferbuf, struct block_translator, xferbuf );
-
- /* Read data from buffer, if applicable */
- if ( blktrans->buffer ) {
-
- /* Read data from buffer */
- copy_from_user ( data, blktrans->buffer, offset, len );
-
- } else {
-
- /* Sanity check */
- assert ( len == 0 );
- }
-}
-
-/** Block device translator data transfer buffer operations */
-static struct xfer_buffer_operations blktrans_xferbuf_operations = {
- .realloc = blktrans_xferbuf_realloc,
- .write = blktrans_xferbuf_write,
- .read = blktrans_xferbuf_read,
-};
-
-/**
* Close block device translator
*
* @v blktrans Block device translator
@@ -216,11 +133,11 @@ static struct interface_descriptor blktrans_xfer_desc =
* Insert block device translator
*
* @v block Block device interface
- * @v buffer Data buffer (or UNULL)
+ * @v buffer Data buffer (or NULL)
* @v size Length of data buffer, or block size
* @ret rc Return status code
*/
-int block_translate ( struct interface *block, userptr_t buffer, size_t size ) {
+int block_translate ( struct interface *block, void *buffer, size_t size ) {
struct block_translator *blktrans;
int rc;
@@ -233,11 +150,10 @@ int block_translate ( struct interface *block, userptr_t buffer, size_t size ) {
ref_init ( &blktrans->refcnt, NULL );
intf_init ( &blktrans->block, &blktrans_block_desc, &blktrans->refcnt );
intf_init ( &blktrans->xfer, &blktrans_xfer_desc, &blktrans->refcnt );
- blktrans->xferbuf.op = &blktrans_xferbuf_operations;
- blktrans->buffer = buffer;
if ( buffer ) {
- blktrans->xferbuf.len = size;
+ xferbuf_fixed_init ( &blktrans->xferbuf, buffer, size );
} else {
+ xferbuf_void_init ( &blktrans->xferbuf );
blktrans->blksize = size;
}
@@ -248,7 +164,7 @@ int block_translate ( struct interface *block, userptr_t buffer, size_t size ) {
DBGC2 ( blktrans, "BLKTRANS %p created", blktrans );
if ( buffer ) {
DBGC2 ( blktrans, " for %#lx+%#zx",
- user_to_phys ( buffer, 0 ), size );
+ virt_to_phys ( buffer ), size );
}
DBGC2 ( blktrans, "\n" );
return 0;
diff --git a/src/core/cachedhcp.c b/src/core/cachedhcp.c
index 04945e646..3f6564efd 100644
--- a/src/core/cachedhcp.c
+++ b/src/core/cachedhcp.c
@@ -22,14 +22,17 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <ipxe/dhcppkt.h>
#include <ipxe/init.h>
#include <ipxe/netdevice.h>
#include <ipxe/vlan.h>
+#include <ipxe/uaccess.h>
#include <ipxe/cachedhcp.h>
/** @file
@@ -198,7 +201,7 @@ static int cachedhcp_apply ( struct cached_dhcp_packet *cache,
* @ret rc Return status code
*/
int cachedhcp_record ( struct cached_dhcp_packet *cache, unsigned int vlan,
- userptr_t data, size_t max_len ) {
+ const void *data, size_t max_len ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
@@ -216,7 +219,7 @@ int cachedhcp_record ( struct cached_dhcp_packet *cache, unsigned int vlan,
return -ENOMEM;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
- copy_from_user ( dhcphdr, data, 0, max_len );
+ memcpy ( dhcphdr, data, max_len );
dhcppkt_init ( dhcppkt, dhcphdr, max_len );
/* Shrink packet to required length. If reallocation fails,
@@ -251,7 +254,7 @@ int cachedhcp_record ( struct cached_dhcp_packet *cache, unsigned int vlan,
/* Store as cached packet */
DBGC ( colour, "CACHEDHCP %s at %#08lx+%#zx/%#zx\n", cache->name,
- user_to_phys ( data, 0 ), len, max_len );
+ virt_to_phys ( data ), len, max_len );
cache->dhcppkt = dhcppkt;
cache->vlan = vlan;
diff --git a/src/core/console.c b/src/core/console.c
index 2b90809bf..240dde3d6 100644
--- a/src/core/console.c
+++ b/src/core/console.c
@@ -6,6 +6,7 @@
/** @file */
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** Current console usage */
int console_usage = CONSOLE_USAGE_STDOUT;
diff --git a/src/core/cpio.c b/src/core/cpio.c
index 4b607e260..d2f9d0c2d 100644
--- a/src/core/cpio.c
+++ b/src/core/cpio.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -34,13 +35,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <ipxe/cpio.h>
+/** CPIO default file mode */
+#define CPIO_DEFAULT_MODE 0644
+
+/** CPIO directory mode */
+#define CPIO_DEFAULT_DIR_MODE 0755
+
/**
* Set field within a CPIO header
*
* @v field Field within CPIO header
* @v value Value to set
*/
-void cpio_set_field ( char *field, unsigned long value ) {
+static void cpio_set_field ( char *field, unsigned long value ) {
char buf[9];
snprintf ( buf, sizeof ( buf ), "%08lx", value );
@@ -48,23 +55,53 @@ void cpio_set_field ( char *field, unsigned long value ) {
}
/**
- * Get CPIO image filename
+ * Get maximum number of CPIO headers (i.e. number of path components)
*
* @v image Image
- * @ret len CPIO filename length (0 for no filename)
+ * @ret max Maximum number of CPIO headers
*/
-size_t cpio_name_len ( struct image *image ) {
+static unsigned int cpio_max ( struct image *image ) {
const char *name = cpio_name ( image );
- char *sep;
- size_t len;
+ unsigned int max = 0;
+ char c;
+ char p;
/* Check for existence of CPIO filename */
if ( ! name )
return 0;
- /* Locate separator (if any) */
- sep = strchr ( name, ' ' );
- len = ( sep ? ( ( size_t ) ( sep - name ) ) : strlen ( name ) );
+ /* Count number of path components */
+ for ( p = '/' ; ( ( ( c = *(name++) ) ) && ( c != ' ' ) ) ; p = c ) {
+ if ( ( p == '/' ) && ( c != '/' ) )
+ max++;
+ }
+
+ return max;
+}
+
+/**
+ * Get CPIO image filename
+ *
+ * @v image Image
+ * @v depth Path depth
+ * @ret len Filename length
+ */
+static size_t cpio_name_len ( struct image *image, unsigned int depth ) {
+ const char *name = cpio_name ( image );
+ size_t len;
+ char c;
+ char p;
+
+ /* Sanity checks */
+ assert ( name != NULL );
+ assert ( depth > 0 );
+
+ /* Calculate length up to specified path depth */
+ for ( len = 0, p = '/' ; ( ( ( c = name[len] ) ) && ( c != ' ' ) ) ;
+ len++, p = c ) {
+ if ( ( c == '/' ) && ( p != '/' ) && ( --depth == 0 ) )
+ break;
+ }
return len;
}
@@ -73,55 +110,99 @@ size_t cpio_name_len ( struct image *image ) {
* Parse CPIO image parameters
*
* @v image Image
- * @v cpio CPIO header to fill in
+ * @v mode Mode to fill in
+ * @v count Number of CPIO headers to fill in
*/
-static void cpio_parse_cmdline ( struct image *image,
- struct cpio_header *cpio ) {
+static void cpio_parse_cmdline ( struct image *image, unsigned int *mode,
+ unsigned int *count ) {
const char *arg;
char *end;
- unsigned int mode;
- /* Look for "mode=" */
+ /* Set default values */
+ *mode = CPIO_DEFAULT_MODE;
+ *count = 1;
+
+ /* Parse "mode=...", if present */
if ( ( arg = image_argument ( image, "mode=" ) ) ) {
- mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
+ *mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
+ if ( *end && ( *end != ' ' ) ) {
+ DBGC ( image, "CPIO %s strange \"mode=\" "
+ "terminator '%c'\n", image->name, *end );
+ }
+ }
+
+ /* Parse "mkdir=...", if present */
+ if ( ( arg = image_argument ( image, "mkdir=" ) ) ) {
+ *count += strtoul ( arg, &end, 10 );
if ( *end && ( *end != ' ' ) ) {
- DBGC ( image, "CPIO %p strange \"mode=\" "
- "terminator '%c'\n", image, *end );
+ DBGC ( image, "CPIO %s strange \"mkdir=\" "
+ "terminator '%c'\n", image->name, *end );
}
- cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) );
}
+
+ /* Allow "mkdir=-1" to request creation of full directory tree */
+ if ( ! *count )
+ *count = -1U;
}
/**
* Construct CPIO header for image, if applicable
*
* @v image Image
+ * @v index CPIO header index
* @v cpio CPIO header to fill in
- * @ret len Length of magic CPIO header (including filename)
+ * @ret len Length of CPIO header (including name, excluding NUL)
*/
-size_t cpio_header ( struct image *image, struct cpio_header *cpio ) {
+size_t cpio_header ( struct image *image, unsigned int index,
+ struct cpio_header *cpio ) {
+ const char *name = cpio_name ( image );
+ unsigned int mode;
+ unsigned int count;
+ unsigned int max;
+ unsigned int depth;
+ unsigned int i;
size_t name_len;
size_t len;
- /* Get filename length */
- name_len = cpio_name_len ( image );
+ /* Parse command line arguments */
+ cpio_parse_cmdline ( image, &mode, &count );
- /* Images with no filename are assumed to already be CPIO archives */
- if ( ! name_len )
+ /* Determine number of CPIO headers to be constructed */
+ max = cpio_max ( image );
+ if ( count > max )
+ count = max;
+
+ /* Determine path depth of this CPIO header */
+ if ( index >= count )
return 0;
+ depth = ( max - count + index + 1 );
+
+ /* Get filename length */
+ name_len = cpio_name_len ( image, depth );
+
+ /* Set directory mode or file mode as appropriate */
+ if ( name[name_len] == '/' ) {
+ mode = ( CPIO_MODE_DIR | CPIO_DEFAULT_DIR_MODE );
+ } else {
+ mode |= CPIO_MODE_FILE;
+ }
+
+ /* Set length on final header */
+ len = ( ( depth < max ) ? 0 : image->len );
/* Construct CPIO header */
memset ( cpio, '0', sizeof ( *cpio ) );
memcpy ( cpio->c_magic, CPIO_MAGIC, sizeof ( cpio->c_magic ) );
- cpio_set_field ( cpio->c_mode, 0100644 );
+ cpio_set_field ( cpio->c_mode, mode );
cpio_set_field ( cpio->c_nlink, 1 );
- cpio_set_field ( cpio->c_filesize, image->len );
+ cpio_set_field ( cpio->c_filesize, len );
cpio_set_field ( cpio->c_namesize, ( name_len + 1 /* NUL */ ) );
- cpio_parse_cmdline ( image, cpio );
+ DBGC ( image, "CPIO %s %d/%d \"", image->name, depth, max );
+ for ( i = 0 ; i < name_len ; i++ )
+ DBGC ( image, "%c", name[i] );
+ DBGC ( image, "\"\n" );
+ DBGC2_HDA ( image, 0, cpio, sizeof ( *cpio ) );
/* Calculate total length */
- len = ( ( sizeof ( *cpio ) + name_len + 1 /* NUL */ + CPIO_ALIGN - 1 )
- & ~( CPIO_ALIGN - 1 ) );
-
- return len;
+ return ( sizeof ( *cpio ) + name_len );
}
diff --git a/src/core/ctype.c b/src/core/ctype.c
index 891af71ea..d7de060e3 100644
--- a/src/core/ctype.c
+++ b/src/core/ctype.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/core/cwuri.c b/src/core/cwuri.c
index 612f0b179..36475b159 100644
--- a/src/core/cwuri.c
+++ b/src/core/cwuri.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <ipxe/uri.h>
diff --git a/src/core/debug.c b/src/core/debug.c
index 9b2a823f5..3f7661dda 100644
--- a/src/core/debug.c
+++ b/src/core/debug.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdio.h>
#include <stdint.h>
diff --git a/src/core/device.c b/src/core/device.c
index efe4eb687..2ab5fa117 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <ipxe/list.h>
diff --git a/src/core/dma.c b/src/core/dma.c
index 5d6868216..dc266545b 100644
--- a/src/core/dma.c
+++ b/src/core/dma.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <assert.h>
#include <errno.h>
@@ -47,7 +48,7 @@ PROVIDE_DMAAPI_INLINE ( flat, dma_free );
PROVIDE_DMAAPI_INLINE ( flat, dma_umalloc );
PROVIDE_DMAAPI_INLINE ( flat, dma_ufree );
PROVIDE_DMAAPI_INLINE ( flat, dma_set_mask );
-PROVIDE_DMAAPI_INLINE ( flat, dma_phys );
+PROVIDE_DMAAPI_INLINE ( flat, dma );
/******************************************************************************
*
@@ -67,7 +68,7 @@ PROVIDE_DMAAPI_INLINE ( flat, dma_phys );
* @ret rc Return status code
*/
static int dma_op_map ( struct dma_device *dma, struct dma_mapping *map,
- physaddr_t addr, size_t len, int flags ) {
+ void *addr, size_t len, int flags ) {
struct dma_operations *op = dma->op;
if ( ! op )
@@ -79,13 +80,14 @@ static int dma_op_map ( struct dma_device *dma, struct dma_mapping *map,
* Unmap buffer
*
* @v map DMA mapping
+ * @v len Used length
*/
-static void dma_op_unmap ( struct dma_mapping *map ) {
+static void dma_op_unmap ( struct dma_mapping *map, size_t len ) {
struct dma_device *dma = map->dma;
assert ( dma != NULL );
assert ( dma->op != NULL );
- dma->op->unmap ( dma, map );
+ dma->op->unmap ( dma, map, len );
}
/**
@@ -130,13 +132,13 @@ static void dma_op_free ( struct dma_mapping *map, void *addr, size_t len ) {
* @v align Physical alignment
* @ret addr Buffer address, or NULL on error
*/
-static userptr_t dma_op_umalloc ( struct dma_device *dma,
- struct dma_mapping *map,
- size_t len, size_t align ) {
+static void * dma_op_umalloc ( struct dma_device *dma,
+ struct dma_mapping *map,
+ size_t len, size_t align ) {
struct dma_operations *op = dma->op;
if ( ! op )
- return UNULL;
+ return NULL;
return op->umalloc ( dma, map, len, align );
}
@@ -147,8 +149,7 @@ static userptr_t dma_op_umalloc ( struct dma_device *dma,
* @v addr Buffer address
* @v len Length of buffer
*/
-static void dma_op_ufree ( struct dma_mapping *map, userptr_t addr,
- size_t len ) {
+static void dma_op_ufree ( struct dma_mapping *map, void *addr, size_t len ) {
struct dma_device *dma = map->dma;
assert ( dma != NULL );
@@ -176,4 +177,4 @@ PROVIDE_DMAAPI ( op, dma_free, dma_op_free );
PROVIDE_DMAAPI ( op, dma_umalloc, dma_op_umalloc );
PROVIDE_DMAAPI ( op, dma_ufree, dma_op_ufree );
PROVIDE_DMAAPI ( op, dma_set_mask, dma_op_set_mask );
-PROVIDE_DMAAPI_INLINE ( op, dma_phys );
+PROVIDE_DMAAPI_INLINE ( op, dma );
diff --git a/src/core/downloader.c b/src/core/downloader.c
index 33737bfac..aa81e7365 100644
--- a/src/core/downloader.c
+++ b/src/core/downloader.c
@@ -22,15 +22,16 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <ipxe/iobuf.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/job.h>
-#include <ipxe/uaccess.h>
#include <ipxe/umalloc.h>
#include <ipxe/image.h>
#include <ipxe/xferbuf.h>
@@ -67,6 +68,7 @@ static void downloader_free ( struct refcnt *refcnt ) {
struct downloader *downloader =
container_of ( refcnt, struct downloader, refcnt );
+ xferbuf_free ( &downloader->buffer );
image_put ( downloader->image );
free ( downloader );
}
@@ -78,18 +80,21 @@ static void downloader_free ( struct refcnt *refcnt ) {
* @v rc Reason for termination
*/
static void downloader_finished ( struct downloader *downloader, int rc ) {
+ struct xfer_buffer *buffer = &downloader->buffer;
+ struct image *image = downloader->image;
/* Log download status */
if ( rc == 0 ) {
- syslog ( LOG_NOTICE, "Downloaded \"%s\"\n",
- downloader->image->name );
+ syslog ( LOG_NOTICE, "Downloaded \"%s\"\n", image->name );
} else {
syslog ( LOG_ERR, "Download of \"%s\" failed: %s\n",
- downloader->image->name, strerror ( rc ) );
+ image->name, strerror ( rc ) );
}
- /* Update image length */
- downloader->image->len = downloader->buffer.len;
+ /* Transfer ownership from data transfer buffer to image */
+ image->data = buffer->data;
+ image->len = buffer->len;
+ xferbuf_detach ( buffer );
/* Shut down interfaces */
intf_shutdown ( &downloader->xfer, rc );
@@ -269,7 +274,7 @@ int create_downloader ( struct interface *job, struct image *image ) {
intf_init ( &downloader->xfer, &downloader_xfer_desc,
&downloader->refcnt );
downloader->image = image_get ( image );
- xferbuf_umalloc_init ( &downloader->buffer, &image->data );
+ xferbuf_umalloc_init ( &downloader->buffer );
/* Instantiate child objects and attach to our interfaces */
if ( ( rc = xfer_open_uri ( &downloader->xfer, image->uri ) ) != 0 )
diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c
index e22998da5..5ca120aac 100644
--- a/src/core/dummy_sanboot.c
+++ b/src/core/dummy_sanboot.c
@@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+#include <string.h>
#include <errno.h>
#include <ipxe/sanboot.h>
diff --git a/src/core/dynkeymap.c b/src/core/dynkeymap.c
index 2f7c49937..fa9d3cb0f 100644
--- a/src/core/dynkeymap.c
+++ b/src/core/dynkeymap.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/dynui.c b/src/core/dynui.c
new file mode 100644
index 000000000..90fda1944
--- /dev/null
+++ b/src/core/dynui.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * Dynamic user interfaces
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ipxe/list.h>
+#include <ipxe/dynui.h>
+
+/** List of all dynamic user interfaces */
+static LIST_HEAD ( dynamic_uis );
+
+/**
+ * Create dynamic user interface
+ *
+ * @v name User interface name, or NULL
+ * @v title User interface title, or NULL
+ * @ret dynui Dynamic user interface, or NULL on failure
+ */
+struct dynamic_ui * create_dynui ( const char *name, const char *title ) {
+ struct dynamic_ui *dynui;
+ size_t name_len;
+ size_t title_len;
+ size_t len;
+ char *name_copy;
+ char *title_copy;
+
+ /* Destroy any existing user interface of this name */
+ dynui = find_dynui ( name );
+ if ( dynui )
+ destroy_dynui ( dynui );
+
+ /* Use empty title if none given */
+ if ( ! title )
+ title = "";
+
+ /* Allocate user interface */
+ name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 );
+ title_len = ( strlen ( title ) + 1 /* NUL */ );
+ len = ( sizeof ( *dynui ) + name_len + title_len );
+ dynui = zalloc ( len );
+ if ( ! dynui )
+ return NULL;
+ name_copy = ( ( void * ) ( dynui + 1 ) );
+ title_copy = ( name_copy + name_len );
+
+ /* Initialise user interface */
+ if ( name ) {
+ strcpy ( name_copy, name );
+ dynui->name = name_copy;
+ }
+ strcpy ( title_copy, title );
+ dynui->title = title_copy;
+ INIT_LIST_HEAD ( &dynui->items );
+ INIT_LIST_HEAD ( &dynui->hidden_items );
+
+ /* Add to list of user interfaces */
+ list_add_tail ( &dynui->list, &dynamic_uis );
+
+ DBGC ( dynui, "DYNUI %s created with title \"%s\"\n",
+ dynui->name, dynui->title );
+
+ return dynui;
+}
+
+/**
+ * Add dynamic user interface item
+ *
+ * @v dynui Dynamic user interface
+ * @v name Name, or NULL
+ * @v text Text, or NULL
+ * @v flags Flags
+ * @v shortcut Shortcut key
+ * @ret item User interface item, or NULL on failure
+ */
+struct dynamic_item * add_dynui_item ( struct dynamic_ui *dynui,
+ const char *name, const char *text,
+ unsigned int flags, int shortcut ) {
+ struct dynamic_item *item;
+ size_t name_len;
+ size_t text_len;
+ size_t len;
+ char *name_copy;
+ char *text_copy;
+
+ /* Use empty text if none given */
+ if ( ! text )
+ text = "";
+
+ /* Allocate item */
+ name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 );
+ text_len = ( strlen ( text ) + 1 /* NUL */ );
+ len = ( sizeof ( *item ) + name_len + text_len );
+ item = zalloc ( len );
+ if ( ! item )
+ return NULL;
+ name_copy = ( ( void * ) ( item + 1 ) );
+ text_copy = ( name_copy + name_len );
+
+ /* Initialise item */
+ if ( name ) {
+ strcpy ( name_copy, name );
+ item->name = name_copy;
+ }
+ strcpy ( text_copy, text );
+ item->text = text_copy;
+ item->index = dynui->count++;
+ item->flags = flags;
+ item->shortcut = shortcut;
+
+ /* Add to list of items */
+ if ( ( item->flags & DYNUI_HIDDEN ) != 0 ) {
+ list_add_tail ( &item->list, &dynui->hidden_items );
+ } else {
+ list_add_tail ( &item->list, &dynui->items );
+ }
+
+ return item;
+}
+
+/**
+ * Destroy dynamic user interface
+ *
+ * @v dynui Dynamic user interface
+ */
+void destroy_dynui ( struct dynamic_ui *dynui ) {
+ struct dynamic_item *item;
+ struct dynamic_item *tmp;
+
+ /* Remove from list of user interfaces */
+ list_del ( &dynui->list );
+
+ /* Free items */
+ list_for_each_entry_safe ( item, tmp, &dynui->items, list ) {
+ list_del ( &item->list );
+ free ( item );
+ }
+ list_for_each_entry_safe ( item, tmp, &dynui->hidden_items, list ) {
+ list_del ( &item->list );
+ free ( item );
+ }
+
+ /* Free user interface */
+ free ( dynui );
+}
+
+/**
+ * Find dynamic user interface
+ *
+ * @v name User interface name, or NULL
+ * @ret dynui Dynamic user interface, or NULL if not found
+ */
+struct dynamic_ui * find_dynui ( const char *name ) {
+ struct dynamic_ui *dynui;
+
+ list_for_each_entry ( dynui, &dynamic_uis, list ) {
+ if ( ( dynui->name == name ) ||
+ ( strcmp ( dynui->name, name ) == 0 ) ) {
+ return dynui;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Find dynamic user interface item by index
+ *
+ * @v dynui Dynamic user interface
+ * @v index Index
+ * @ret item User interface item, or NULL if not found
+ */
+struct dynamic_item * dynui_item ( struct dynamic_ui *dynui,
+ unsigned int index ) {
+ struct dynamic_item *item;
+
+ list_for_each_entry ( item, &dynui->items, list ) {
+ if ( index-- == 0 )
+ return item;
+ }
+
+ return NULL;
+}
+
+/**
+ * Find dynamic user interface item by shortcut key
+ *
+ * @v dynui Dynamic user interface
+ * @v key Shortcut key
+ * @ret item User interface item, or NULL if not found
+ */
+struct dynamic_item * dynui_shortcut ( struct dynamic_ui *dynui, int key ) {
+ struct dynamic_item *item;
+
+ list_for_each_entry ( item, &dynui->items, list ) {
+ if ( key && ( key == item->shortcut ) )
+ return item;
+ }
+ list_for_each_entry ( item, &dynui->hidden_items, list ) {
+ if ( key && ( key == item->shortcut ) )
+ return item;
+ }
+
+ return NULL;
+}
diff --git a/src/core/edd.c b/src/core/edd.c
index a50b74ab1..4fcccf117 100644
--- a/src/core/edd.c
+++ b/src/core/edd.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <errno.h>
#include <ipxe/interface.h>
diff --git a/src/core/errno.c b/src/core/errno.c
index 5de15bb92..7afa40859 100644
--- a/src/core/errno.c
+++ b/src/core/errno.c
@@ -1,6 +1,7 @@
#include <errno.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/exec.c b/src/core/exec.c
index a13884b68..4db1248b0 100644
--- a/src/core/exec.c
+++ b/src/core/exec.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
@@ -428,10 +429,7 @@ static int echo_exec ( int argc, char **argv ) {
}
/** "echo" command */
-struct command echo_command __command = {
- .name = "echo",
- .exec = echo_exec,
-};
+COMMAND ( echo, echo_exec );
/** "exit" options */
struct exit_options {};
@@ -472,10 +470,7 @@ static int exit_exec ( int argc, char **argv ) {
}
/** "exit" command */
-struct command exit_command __command = {
- .name = "exit",
- .exec = exit_exec,
-};
+COMMAND ( exit, exit_exec );
/** "isset" options */
struct isset_options {};
@@ -507,10 +502,7 @@ static int isset_exec ( int argc, char **argv ) {
}
/** "isset" command */
-struct command isset_command __command = {
- .name = "isset",
- .exec = isset_exec,
-};
+COMMAND ( isset, isset_exec );
/** "iseq" options */
struct iseq_options {};
@@ -544,10 +536,7 @@ static int iseq_exec ( int argc, char **argv ) {
}
/** "iseq" command */
-struct command iseq_command __command = {
- .name = "iseq",
- .exec = iseq_exec,
-};
+COMMAND ( iseq, iseq_exec );
/** "sleep" options */
struct sleep_options {};
@@ -587,7 +576,4 @@ static int sleep_exec ( int argc, char **argv ) {
}
/** "sleep" command */
-struct command sleep_command __command = {
- .name = "sleep",
- .exec = sleep_exec,
-};
+COMMAND ( sleep, sleep_exec );
diff --git a/src/core/fbcon.c b/src/core/fbcon.c
index 056b164cd..c65efcaea 100644
--- a/src/core/fbcon.c
+++ b/src/core/fbcon.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -36,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/ansiesc.h>
#include <ipxe/image.h>
#include <ipxe/pixbuf.h>
+#include <ipxe/uaccess.h>
#include <ipxe/umalloc.h>
#include <ipxe/console.h>
#include <ipxe/fbcon.h>
@@ -103,50 +105,45 @@ static void fbcon_set_default_background ( struct fbcon *fbcon ) {
}
/**
+ * Get character cell
+ *
+ * @v fbcon Frame buffer console
+ * @v xpos X position
+ * @v ypos Y position
+ * @ret cell Text cell
+ */
+static inline struct fbcon_text_cell * fbcon_cell ( struct fbcon *fbcon,
+ unsigned int xpos,
+ unsigned int ypos ) {
+ unsigned int index;
+
+ index = ( ( ypos * fbcon->character.width ) + xpos );
+ return &fbcon->text.cells[index];
+}
+
+/**
* Clear rows of characters
*
* @v fbcon Frame buffer console
* @v ypos Starting Y position
*/
static void fbcon_clear ( struct fbcon *fbcon, unsigned int ypos ) {
- struct fbcon_text_cell cell = {
- .foreground = fbcon->foreground,
- .background = fbcon->background,
- .character = ' ',
- };
- size_t offset;
+ struct fbcon_text_cell *cell;
unsigned int xpos;
/* Clear stored character array */
+ cell = fbcon_cell ( fbcon, 0, ypos );
for ( ; ypos < fbcon->character.height ; ypos++ ) {
- offset = ( ypos * fbcon->character.width * sizeof ( cell ) );
for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
- copy_to_user ( fbcon->text.start, offset, &cell,
- sizeof ( cell ) );
- offset += sizeof ( cell );
+ cell->foreground = fbcon->foreground;
+ cell->background = fbcon->background;
+ cell->character = ' ';
+ cell++;
}
}
}
/**
- * Store character at specified position
- *
- * @v fbcon Frame buffer console
- * @v cell Text cell
- * @v xpos X position
- * @v ypos Y position
- */
-static void fbcon_store ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
- unsigned int xpos, unsigned int ypos ) {
- size_t offset;
-
- /* Store cell */
- offset = ( ( ( ypos * fbcon->character.width ) + xpos ) *
- sizeof ( *cell ) );
- copy_to_user ( fbcon->text.start, offset, cell, sizeof ( *cell ) );
-}
-
-/**
* Draw character at specified position
*
* @v fbcon Frame buffer console
@@ -156,7 +153,7 @@ static void fbcon_store ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
*/
static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
unsigned int xpos, unsigned int ypos ) {
- uint8_t glyph[fbcon->font->height];
+ const uint8_t *glyph;
size_t offset;
size_t pixel_len;
size_t skip_len;
@@ -164,10 +161,10 @@ static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
unsigned int column;
uint8_t bitmask;
int transparent;
- void *src;
+ const void *src;
/* Get font character */
- fbcon->font->glyph ( cell->character, glyph );
+ glyph = fbcon->font->glyph ( cell->character );
/* Calculate pixel geometry */
offset = ( fbcon->indent +
@@ -185,12 +182,12 @@ static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
/* Draw background picture, if applicable */
if ( transparent ) {
if ( fbcon->picture.start ) {
- memcpy_user ( fbcon->start, offset,
- fbcon->picture.start, offset,
- fbcon->character.len );
+ memcpy ( ( fbcon->start + offset ),
+ ( fbcon->picture.start + offset ),
+ fbcon->character.len );
} else {
- memset_user ( fbcon->start, offset, 0,
- fbcon->character.len );
+ memset ( ( fbcon->start + offset ), 0,
+ fbcon->character.len );
}
}
@@ -204,7 +201,7 @@ static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
} else {
continue;
}
- copy_to_user ( fbcon->start, offset, src, pixel_len );
+ memcpy ( ( fbcon->start + offset ), src, pixel_len );
}
/* Move to next row */
@@ -218,18 +215,16 @@ static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
* @v fbcon Frame buffer console
*/
static void fbcon_redraw ( struct fbcon *fbcon ) {
- struct fbcon_text_cell cell;
- size_t offset = 0;
+ struct fbcon_text_cell *cell;
unsigned int xpos;
unsigned int ypos;
/* Redraw characters */
+ cell = fbcon_cell ( fbcon, 0, 0 );
for ( ypos = 0 ; ypos < fbcon->character.height ; ypos++ ) {
for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
- copy_from_user ( &cell, fbcon->text.start, offset,
- sizeof ( cell ) );
- fbcon_draw ( fbcon, &cell, xpos, ypos );
- offset += sizeof ( cell );
+ fbcon_draw ( fbcon, cell, xpos, ypos );
+ cell++;
}
}
}
@@ -240,22 +235,46 @@ static void fbcon_redraw ( struct fbcon *fbcon ) {
* @v fbcon Frame buffer console
*/
static void fbcon_scroll ( struct fbcon *fbcon ) {
- size_t row_len;
+ const struct fbcon_text_cell *old;
+ struct fbcon_text_cell *new;
+ unsigned int xpos;
+ unsigned int ypos;
+ unsigned int character;
+ uint32_t foreground;
+ uint32_t background;
/* Sanity check */
assert ( fbcon->ypos == fbcon->character.height );
/* Scroll up character array */
- row_len = ( fbcon->character.width * sizeof ( struct fbcon_text_cell ));
- memmove_user ( fbcon->text.start, 0, fbcon->text.start, row_len,
- ( row_len * ( fbcon->character.height - 1 ) ) );
- fbcon_clear ( fbcon, ( fbcon->character.height - 1 ) );
+ new = fbcon_cell ( fbcon, 0, 0 );
+ old = fbcon_cell ( fbcon, 0, 1 );
+ for ( ypos = 0 ; ypos < ( fbcon->character.height - 1 ) ; ypos++ ) {
+ for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
+ /* Redraw character (if changed) */
+ character = old->character;
+ foreground = old->foreground;
+ background = old->background;
+ if ( ( new->character != character ) ||
+ ( new->foreground != foreground ) ||
+ ( new->background != background ) ) {
+ new->character = character;
+ new->foreground = foreground;
+ new->background = background;
+ fbcon_draw ( fbcon, new, xpos, ypos );
+ }
+ new++;
+ old++;
+ }
+ }
+
+ /* Clear bottom row */
+ fbcon_clear ( fbcon, ypos );
+ for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ )
+ fbcon_draw ( fbcon, new++, xpos, ypos );
/* Update cursor position */
fbcon->ypos--;
-
- /* Redraw all characters */
- fbcon_redraw ( fbcon );
}
/**
@@ -265,18 +284,19 @@ static void fbcon_scroll ( struct fbcon *fbcon ) {
* @v show_cursor Show cursor
*/
static void fbcon_draw_cursor ( struct fbcon *fbcon, int show_cursor ) {
- struct fbcon_text_cell cell;
- size_t offset;
+ struct fbcon_text_cell *cell;
+ struct fbcon_text_cell cursor;
- offset = ( ( ( fbcon->ypos * fbcon->character.width ) + fbcon->xpos ) *
- sizeof ( cell ) );
- copy_from_user ( &cell, fbcon->text.start, offset, sizeof ( cell ) );
+ cell = fbcon_cell ( fbcon, fbcon->xpos, fbcon->ypos );
if ( show_cursor ) {
- cell.background = fbcon->foreground;
- cell.foreground = ( ( fbcon->background == FBCON_TRANSPARENT ) ?
- 0 : fbcon->background );
+ cursor.background = fbcon->foreground;
+ cursor.foreground =
+ ( ( fbcon->background == FBCON_TRANSPARENT ) ?
+ 0 : fbcon->background );
+ cursor.character = cell->character;
+ cell = &cursor;
}
- fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
+ fbcon_draw ( fbcon, cell, fbcon->xpos, fbcon->ypos );
}
/**
@@ -439,7 +459,7 @@ static struct ansiesc_handler fbcon_ansiesc_handlers[] = {
* @v character Character
*/
void fbcon_putchar ( struct fbcon *fbcon, int character ) {
- struct fbcon_text_cell cell;
+ struct fbcon_text_cell *cell;
/* Intercept ANSI escape sequences */
character = ansiesc_process ( &fbcon->ctx, character );
@@ -473,11 +493,11 @@ void fbcon_putchar ( struct fbcon *fbcon, int character ) {
break;
default:
/* Print character at current cursor position */
- cell.foreground = ( fbcon->foreground | fbcon->bold );
- cell.background = fbcon->background;
- cell.character = character;
- fbcon_store ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
- fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
+ cell = fbcon_cell ( fbcon, fbcon->xpos, fbcon->ypos );
+ cell->foreground = ( fbcon->foreground | fbcon->bold );
+ cell->background = fbcon->background;
+ cell->character = character;
+ fbcon_draw ( fbcon, cell, fbcon->xpos, fbcon->ypos );
/* Advance cursor */
fbcon->xpos++;
@@ -508,12 +528,9 @@ static int fbcon_picture_init ( struct fbcon *fbcon,
struct fbcon_geometry *pixel = fbcon->pixel;
struct fbcon_picture *picture = &fbcon->picture;
size_t len;
- size_t pixbuf_stride;
size_t indent;
- size_t pixbuf_indent;
size_t offset;
- size_t pixbuf_offset;
- uint32_t rgb;
+ const uint32_t *rgb;
uint32_t raw;
unsigned int x;
unsigned int y;
@@ -537,13 +554,10 @@ static int fbcon_picture_init ( struct fbcon *fbcon,
}
/* Centre picture on console */
- pixbuf_stride = ( pixbuf->width * sizeof ( rgb ) );
xgap = ( ( ( int ) ( pixel->width - pixbuf->width ) ) / 2 );
ygap = ( ( ( int ) ( pixel->height - pixbuf->height ) ) / 2 );
indent = ( ( ( ( ygap >= 0 ) ? ygap : 0 ) * pixel->stride ) +
( ( ( xgap >= 0 ) ? xgap : 0 ) * pixel->len ) );
- pixbuf_indent = ( ( ( ( ygap < 0 ) ? -ygap : 0 ) * pixbuf_stride ) +
- ( ( ( xgap < 0 ) ? -xgap : 0 ) * sizeof ( rgb ) ) );
width = pixbuf->width;
if ( width > pixel->width )
width = pixel->width;
@@ -555,18 +569,17 @@ static int fbcon_picture_init ( struct fbcon *fbcon,
( ygap + pixbuf->height ) );
/* Convert to frame buffer raw format */
- memset_user ( picture->start, 0, 0, len );
+ memset ( picture->start, 0, len );
for ( y = 0 ; y < height ; y++ ) {
offset = ( indent + ( y * pixel->stride ) );
- pixbuf_offset = ( pixbuf_indent + ( y * pixbuf_stride ) );
+ rgb = pixbuf_pixel ( pixbuf, ( ( xgap < 0 ) ? -xgap : 0 ),
+ ( ( ( ygap < 0 ) ? -ygap : 0 ) + y ) );
for ( x = 0 ; x < width ; x++ ) {
- copy_from_user ( &rgb, pixbuf->data, pixbuf_offset,
- sizeof ( rgb ) );
- raw = fbcon_colour ( fbcon, rgb );
- copy_to_user ( picture->start, offset, &raw,
- pixel->len );
+ raw = fbcon_colour ( fbcon, *rgb );
+ memcpy ( ( picture->start + offset ), &raw,
+ pixel->len );
offset += pixel->len;
- pixbuf_offset += sizeof ( rgb );
+ rgb++;
}
}
@@ -588,7 +601,7 @@ static int fbcon_picture_init ( struct fbcon *fbcon,
* @v config Console configuration
* @ret rc Return status code
*/
-int fbcon_init ( struct fbcon *fbcon, userptr_t start,
+int fbcon_init ( struct fbcon *fbcon, void *start,
struct fbcon_geometry *pixel,
struct fbcon_colour_map *map,
struct fbcon_font *font,
@@ -617,8 +630,8 @@ int fbcon_init ( struct fbcon *fbcon, userptr_t start,
/* Derive overall length */
fbcon->len = ( pixel->height * pixel->stride );
DBGC ( fbcon, "FBCON %p at [%08lx,%08lx)\n", fbcon,
- user_to_phys ( fbcon->start, 0 ),
- user_to_phys ( fbcon->start, fbcon->len ) );
+ virt_to_phys ( fbcon->start ),
+ ( virt_to_phys ( fbcon->start ) + fbcon->len ) );
}
/* Calculate margin. If the actual screen size is larger than
@@ -680,32 +693,30 @@ int fbcon_init ( struct fbcon *fbcon, userptr_t start,
fbcon_set_default_background ( fbcon );
/* Allocate and initialise stored character array */
- fbcon->text.start = umalloc ( fbcon->character.width *
- fbcon->character.height *
- sizeof ( struct fbcon_text_cell ) );
- if ( ! fbcon->text.start ) {
+ fbcon->text.cells = umalloc ( fbcon->character.width *
+ fbcon->character.height *
+ sizeof ( fbcon->text.cells[0] ) );
+ if ( ! fbcon->text.cells ) {
rc = -ENOMEM;
goto err_text;
}
fbcon_clear ( fbcon, 0 );
/* Set framebuffer to all black (including margins) */
- memset_user ( fbcon->start, 0, 0, fbcon->len );
+ memset ( fbcon->start, 0, fbcon->len );
} else {
fbcon_clear ( fbcon, 0 );
}
/* Generate pixel buffer from background image, if applicable */
if ( config->pixbuf &&
- ( ( rc = fbcon_picture_init ( fbcon, config->pixbuf ) ) != 0 ) &&
+ ( ( rc = fbcon_picture_init ( fbcon, config->pixbuf ) ) != 0 ) &&
( ! config->lazy_update ) )
- goto err_picture; /* Keep going w/o background in lazy_update mode */
+ goto err_picture;
/* Draw background picture (including margins), if applicable */
- if ( fbcon->picture.start ) {
- memcpy_user ( fbcon->start, 0, fbcon->picture.start, 0,
- fbcon->len );
- }
+ if ( fbcon->picture.start )
+ memcpy ( fbcon->start, fbcon->picture.start, fbcon->len );
/* Update console width and height */
console_set_size ( fbcon->character.width, fbcon->character.height );
@@ -714,7 +725,7 @@ int fbcon_init ( struct fbcon *fbcon, userptr_t start,
ufree ( fbcon->picture.start );
err_picture:
- ufree ( fbcon->text.start );
+ ufree ( fbcon->text.cells );
err_text:
err_margin:
return rc;
@@ -727,6 +738,6 @@ int fbcon_init ( struct fbcon *fbcon, userptr_t start,
*/
void fbcon_fini ( struct fbcon *fbcon ) {
- ufree ( fbcon->text.start );
+ ufree ( fbcon->text.cells );
ufree ( fbcon->picture.start );
}
diff --git a/src/core/fdt.c b/src/core/fdt.c
index f439422cf..8ac781b05 100644
--- a/src/core/fdt.c
+++ b/src/core/fdt.c
@@ -22,12 +22,17 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
+#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
+#include <ipxe/image.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/umalloc.h>
#include <ipxe/fdt.h>
/** @file
@@ -37,45 +42,53 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/** The system flattened device tree (if present) */
-static struct fdt fdt;
+struct fdt sysfdt;
-/** A position within a device tree */
-struct fdt_cursor {
- /** Offset within structure block */
- unsigned int offset;
- /** Tree depth */
- int depth;
+/** The downloaded flattened device tree tag */
+struct image_tag fdt_image __image_tag = {
+ .name = "FDT",
};
-/** A lexical descriptor */
-struct fdt_descriptor {
- /** Node or property name (if applicable) */
- const char *name;
- /** Property data (if applicable) */
- const void *data;
- /** Length of property data (if applicable) */
- size_t len;
-};
+/** Amount of free space to add whenever we have to reallocate a tree */
+#define FDT_INSERT_PAD 1024
+
+/**
+ * Check if character is permitted in a name
+ *
+ * @v ch Character
+ * @ret is_permitted Character is permitted in a name
+ */
+static int fdt_permitted ( char ch ) {
+ static const char permitted[] = ",._+?#-";
+
+ return ( isalnum ( ch ) || strchr ( permitted, ch ) );
+}
/**
- * Check if device tree exists
+ * Compare node name
*
- * @v has_fdt Device tree exists
+ * @v desc Token descriptor
+ * @v name Name (terminated by NUL or any non-permitted character)
+ * @ret is_match Name matches token descriptor
*/
-static inline __attribute__ (( always_inline )) int fdt_exists ( void ) {
+static int fdt_match ( const struct fdt_descriptor *desc, const char *name ) {
+ size_t len = strlen ( desc->name );
- return ( fdt.hdr != NULL );
+ /* Check name and terminator */
+ return ( ( memcmp ( desc->name, name, len ) == 0 ) &&
+ ( ! ( name[len] && fdt_permitted ( name[len] ) ) ) );
}
/**
- * Traverse device tree
+ * Describe device tree token
*
- * @v pos Position within device tree
- * @v desc Lexical descriptor to fill in
+ * @v fdt Device tree
+ * @v offset Offset within structure block
+ * @v desc Token descriptor to fill in
* @ret rc Return status code
*/
-static int fdt_traverse ( struct fdt_cursor *pos,
- struct fdt_descriptor *desc ) {
+int fdt_describe ( struct fdt *fdt, unsigned int offset,
+ struct fdt_descriptor *desc ) {
const fdt_token_t *token;
const void *data;
const struct fdt_prop *prop;
@@ -84,17 +97,18 @@ static int fdt_traverse ( struct fdt_cursor *pos,
size_t len;
/* Sanity checks */
- assert ( pos->offset < fdt.len );
- assert ( ( pos->offset & ( FDT_STRUCTURE_ALIGN - 1 ) ) == 0 );
+ assert ( offset <= fdt->len );
+ assert ( ( offset & ( FDT_STRUCTURE_ALIGN - 1 ) ) == 0 );
- /* Clear descriptor */
+ /* Initialise descriptor */
memset ( desc, 0, sizeof ( *desc ) );
+ desc->offset = offset;
/* Locate token and calculate remaining space */
- token = ( fdt.raw + fdt.structure + pos->offset );
- remaining = ( fdt.len - pos->offset );
+ token = ( fdt->raw + fdt->structure + offset );
+ remaining = ( fdt->len - offset );
if ( remaining < sizeof ( *token ) ) {
- DBGC ( &fdt, "FDT truncated tree at +%#04x\n", pos->offset );
+ DBGC ( fdt, "FDT truncated tree at +%#04x\n", offset );
return -EINVAL;
}
remaining -= sizeof ( *token );
@@ -110,26 +124,17 @@ static int fdt_traverse ( struct fdt_cursor *pos,
desc->name = data;
len = ( strnlen ( desc->name, remaining ) + 1 /* NUL */ );
if ( remaining < len ) {
- DBGC ( &fdt, "FDT unterminated node name at +%#04x\n",
- pos->offset );
+ DBGC ( fdt, "FDT unterminated node name at +%#04x\n",
+ offset );
return -EINVAL;
}
- pos->depth++;
+ desc->depth = +1;
break;
case cpu_to_be32 ( FDT_END_NODE ):
/* End of node */
- if ( pos->depth < 0 ) {
- DBGC ( &fdt, "FDT spurious node end at +%#04x\n",
- pos->offset );
- return -EINVAL;
- }
- pos->depth--;
- if ( pos->depth < 0 ) {
- /* End of (sub)tree */
- return -ENOENT;
- }
+ desc->depth = -1;
break;
case cpu_to_be32 ( FDT_PROP ):
@@ -137,25 +142,25 @@ static int fdt_traverse ( struct fdt_cursor *pos,
/* Property */
prop = data;
if ( remaining < sizeof ( *prop ) ) {
- DBGC ( &fdt, "FDT truncated property at +%#04x\n",
- pos->offset );
+ DBGC ( fdt, "FDT truncated property at +%#04x\n",
+ offset );
return -EINVAL;
}
desc->data = ( ( ( const void * ) prop ) + sizeof ( *prop ) );
desc->len = be32_to_cpu ( prop->len );
len = ( sizeof ( *prop ) + desc->len );
if ( remaining < len ) {
- DBGC ( &fdt, "FDT overlength property at +%#04x\n",
- pos->offset );
+ DBGC ( fdt, "FDT overlength property at +%#04x\n",
+ offset );
return -EINVAL;
}
name_off = be32_to_cpu ( prop->name_off );
- if ( name_off > fdt.strings_len ) {
- DBGC ( &fdt, "FDT property name outside strings "
- "block at +%#04x\n", pos->offset );
+ if ( name_off > fdt->strings_len ) {
+ DBGC ( fdt, "FDT property name outside strings "
+ "block at +%#04x\n", offset );
return -EINVAL;
}
- desc->name = ( fdt.raw + fdt.strings + name_off );
+ desc->name = ( fdt->raw + fdt->strings + name_off );
break;
case cpu_to_be32 ( FDT_NOP ):
@@ -166,136 +171,313 @@ static int fdt_traverse ( struct fdt_cursor *pos,
default:
/* Unrecognised or unexpected token */
- DBGC ( &fdt, "FDT unexpected token %#08x at +%#04x\n",
- be32_to_cpu ( *token ), pos->offset );
+ DBGC ( fdt, "FDT unexpected token %#08x at +%#04x\n",
+ be32_to_cpu ( *token ), offset );
return -EINVAL;
}
- /* Update cursor */
+ /* Calculate offset to next token */
len = ( ( len + FDT_STRUCTURE_ALIGN - 1 ) &
~( FDT_STRUCTURE_ALIGN - 1 ) );
- pos->offset += ( sizeof ( *token ) + len );
+ offset += ( sizeof ( *token ) + len );
+ desc->next = offset;
/* Sanity checks */
- assert ( pos->offset <= fdt.len );
+ assert ( offset <= fdt->len );
return 0;
}
/**
+ * Describe next device tree token
+ *
+ * @v fdt Device tree
+ * @v desc Token descriptor to update
+ * @ret rc Return status code
+ */
+static int fdt_next ( struct fdt *fdt, struct fdt_descriptor *desc ) {
+
+ /* Describe next token */
+ return fdt_describe ( fdt, desc->next, desc );
+}
+
+/**
+ * Enter node
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v desc Begin node descriptor to fill in
+ * @ret rc Return status code
+ */
+static int fdt_enter ( struct fdt *fdt, unsigned int offset,
+ struct fdt_descriptor *desc ) {
+ int rc;
+
+ /* Find begin node token */
+ for ( ; ; offset = desc->next ) {
+
+ /* Describe token */
+ if ( ( rc = fdt_describe ( fdt, offset, desc ) ) != 0 ) {
+ DBGC ( fdt, "FDT +%#04x has malformed node: %s\n",
+ offset, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check for begin node token */
+ if ( desc->depth > 0 )
+ return 0;
+
+ /* Check for non-NOPs */
+ if ( desc->depth ) {
+ DBGC ( fdt, "FDT +%#04x has spurious node end at "
+ "+%#04x\n", offset, desc->offset );
+ return -EINVAL;
+ }
+ if ( desc->name ) {
+ DBGC ( fdt, "FDT +%#04x has spurious property at "
+ "+%#04x\n", offset, desc->offset );
+ return -EINVAL;
+ }
+ }
+}
+
+/**
+ * Find node relative depth
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v target Target node offset
+ * @ret depth Depth, or negative error
+ */
+static int fdt_depth ( struct fdt *fdt, unsigned int offset,
+ unsigned int target ) {
+ struct fdt_descriptor desc;
+ int depth;
+ int rc;
+
+ /* Enter node */
+ if ( ( rc = fdt_enter ( fdt, offset, &desc ) ) != 0 )
+ return rc;
+
+ /* Find target node */
+ for ( depth = 0 ; depth >= 0 ; depth += desc.depth ) {
+
+ /* Describe token */
+ if ( ( rc = fdt_next ( fdt, &desc ) ) != 0 ) {
+ DBGC ( fdt, "FDT +%#04x has malformed node: %s\n",
+ offset, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check for target node */
+ if ( desc.offset == target ) {
+ DBGC2 ( fdt, "FDT +%#04x has descendant node +%#04x "
+ "at depth +%d\n", offset, target, depth );
+ return depth;
+ }
+ }
+
+ DBGC ( fdt, "FDT +#%04x has no descendant node +%#04x\n",
+ offset, target );
+ return -ENOENT;
+}
+
+/**
+ * Find parent node
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v parent Parent node offset to fill in
+ * @ret rc Return status code
+ */
+int fdt_parent ( struct fdt *fdt, unsigned int offset, unsigned int *parent ) {
+ struct fdt_descriptor desc;
+ int pdepth;
+ int depth;
+ int rc;
+
+ /* Find depth from root of tree */
+ depth = fdt_depth ( fdt, 0, offset );
+ if ( depth < 0 ) {
+ rc = depth;
+ return rc;
+ }
+ pdepth = ( depth - 1 );
+
+ /* Enter root node */
+ if ( ( rc = fdt_enter ( fdt, 0, &desc ) ) != 0 )
+ return rc;
+ *parent = desc.offset;
+
+ /* Find parent node */
+ for ( depth = 0 ; depth >= 0 ; depth += desc.depth ) {
+
+ /* Describe token */
+ if ( ( rc = fdt_next ( fdt, &desc ) ) != 0 ) {
+ DBGC ( fdt, "FDT +%#04x has malformed node: %s\n",
+ offset, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Record possible parent node */
+ if ( ( depth == pdepth ) && desc.name && ( ! desc.data ) )
+ *parent = desc.offset;
+
+ /* Check for target node */
+ if ( desc.offset == offset ) {
+ DBGC2 ( fdt, "FDT +%#04x has parent node at +%#04x\n",
+ offset, *parent );
+ return 0;
+ }
+ }
+
+ DBGC ( fdt, "FDT +#%04x has no parent node\n", offset );
+ return -ENOENT;
+}
+
+/**
* Find child node
*
+ * @v fdt Device tree
* @v offset Starting node offset
* @v name Node name
* @v child Child node offset to fill in
* @ret rc Return status code
*/
-static int fdt_child ( unsigned int offset, const char *name,
+static int fdt_child ( struct fdt *fdt, unsigned int offset, const char *name,
unsigned int *child ) {
- struct fdt_cursor pos;
struct fdt_descriptor desc;
- unsigned int orig_offset;
+ int depth;
int rc;
- /* Record original offset (for debugging) */
- orig_offset = offset;
-
- /* Initialise cursor */
- pos.offset = offset;
- pos.depth = -1;
+ /* Enter node */
+ if ( ( rc = fdt_enter ( fdt, offset, &desc ) ) != 0 )
+ return rc;
/* Find child node */
- while ( 1 ) {
-
- /* Record current offset */
- *child = pos.offset;
+ for ( depth = 0 ; depth >= 0 ; depth += desc.depth ) {
- /* Traverse tree */
- if ( ( rc = fdt_traverse ( &pos, &desc ) ) != 0 ) {
- DBGC ( &fdt, "FDT +%#04x has no child node \"%s\": "
- "%s\n", orig_offset, name, strerror ( rc ) );
+ /* Describe token */
+ if ( ( rc = fdt_next ( fdt, &desc ) ) != 0 ) {
+ DBGC ( fdt, "FDT +%#04x has malformed node: %s\n",
+ offset, strerror ( rc ) );
return rc;
}
/* Check for matching immediate child node */
- if ( ( pos.depth == 1 ) && desc.name && ( ! desc.data ) ) {
- DBGC2 ( &fdt, "FDT +%#04x has child node \"%s\"\n",
- orig_offset, desc.name );
- if ( strcmp ( name, desc.name ) == 0 ) {
- DBGC2 ( &fdt, "FDT +%#04x found child node "
- "\"%s\" at +%#04x\n", orig_offset,
- desc.name, *child );
+ if ( ( depth == 0 ) && desc.name && ( ! desc.data ) ) {
+ DBGC2 ( fdt, "FDT +%#04x has child node \"%s\" at "
+ "+%#04x\n", offset, desc.name, desc.offset );
+ assert ( desc.depth > 0 );
+ if ( fdt_match ( &desc, name ) ) {
+ *child = desc.offset;
return 0;
}
}
}
+
+ DBGC2 ( fdt, "FDT +%#04x has no child node \"%s\"\n", offset, name );
+ return -ENOENT;
+}
+
+/**
+ * Find end of node
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v end End of node offset to fill in
+ * @ret rc Return status code
+ */
+static int fdt_end ( struct fdt *fdt, unsigned int offset,
+ unsigned int *end ) {
+ struct fdt_descriptor desc;
+ int depth;
+ int rc;
+
+ /* Enter node */
+ if ( ( rc = fdt_enter ( fdt, offset, &desc ) ) != 0 )
+ return rc;
+
+ /* Find end of this node */
+ for ( depth = 0 ; depth >= 0 ; depth += desc.depth ) {
+
+ /* Describe token */
+ if ( ( rc = fdt_next ( fdt, &desc ) ) != 0 ) {
+ DBGC ( fdt, "FDT +%#04x has malformed node: %s\n",
+ offset, strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ /* Record end offset */
+ *end = desc.offset;
+ DBGC2 ( fdt, "FDT +%#04x has end at +%#04x\n", offset, *end );
+ return 0;
}
/**
* Find node by path
*
+ * @v fdt Device tree
* @v path Node path
* @v offset Offset to fill in
* @ret rc Return status code
*/
-int fdt_path ( const char *path, unsigned int *offset ) {
- char *tmp = ( ( char * ) path );
- char *del;
+int fdt_path ( struct fdt *fdt, const char *path, unsigned int *offset ) {
+ const char *tmp = path;
int rc;
/* Initialise offset */
*offset = 0;
/* Traverse tree one path segment at a time */
- while ( *tmp ) {
+ while ( 1 ) {
/* Skip any leading '/' */
while ( *tmp == '/' )
tmp++;
- /* Find next '/' delimiter and convert to NUL */
- del = strchr ( tmp, '/' );
- if ( del )
- *del = '\0';
+ /* Terminate if there are no more path components */
+ if ( ! *tmp )
+ break;
- /* Find child and restore delimiter */
- rc = fdt_child ( *offset, tmp, offset );
- if ( del )
- *del = '/';
- if ( rc != 0 )
+ /* Find child */
+ if ( ( rc = fdt_child ( fdt, *offset, tmp, offset ) ) != 0 )
return rc;
/* Move to next path component, if any */
- while ( *tmp && ( *tmp != '/' ) )
- tmp++;
+ tmp = strchr ( tmp, '/' );
+ if ( ! tmp )
+ break;
}
- DBGC2 ( &fdt, "FDT found path \"%s\" at +%#04x\n", path, *offset );
+ DBGC2 ( fdt, "FDT found path \"%s\" at +%#04x\n", path, *offset );
return 0;
}
/**
* Find node by alias
*
+ * @v fdt Device tree
* @v name Alias name
* @v offset Offset to fill in
* @ret rc Return status code
*/
-int fdt_alias ( const char *name, unsigned int *offset ) {
+int fdt_alias ( struct fdt *fdt, const char *name, unsigned int *offset ) {
const char *alias;
int rc;
/* Locate "/aliases" node */
- if ( ( rc = fdt_child ( 0, "aliases", offset ) ) != 0 )
+ if ( ( rc = fdt_child ( fdt, 0, "aliases", offset ) ) != 0 )
return rc;
/* Locate alias property */
- if ( ( alias = fdt_string ( *offset, name ) ) == NULL )
+ if ( ( alias = fdt_string ( fdt, *offset, name ) ) == NULL )
return -ENOENT;
- DBGC ( &fdt, "FDT alias \"%s\" is \"%s\"\n", name, alias );
+ DBGC ( fdt, "FDT alias \"%s\" is \"%s\"\n", name, alias );
/* Locate aliased node */
- if ( ( rc = fdt_path ( alias, offset ) ) != 0 )
+ if ( ( rc = fdt_path ( fdt, alias, offset ) ) != 0 )
return rc;
return 0;
@@ -304,84 +486,394 @@ int fdt_alias ( const char *name, unsigned int *offset ) {
/**
* Find property
*
+ * @v fdt Device tree
* @v offset Starting node offset
* @v name Property name
- * @v desc Lexical descriptor to fill in
+ * @v desc Token descriptor to fill in
* @ret rc Return status code
*/
-static int fdt_property ( unsigned int offset, const char *name,
- struct fdt_descriptor *desc ) {
- struct fdt_cursor pos;
+static int fdt_property ( struct fdt *fdt, unsigned int offset,
+ const char *name, struct fdt_descriptor *desc ) {
+ int depth;
int rc;
- /* Initialise cursor */
- pos.offset = offset;
- pos.depth = -1;
+ /* Enter node */
+ if ( ( rc = fdt_enter ( fdt, offset, desc ) ) != 0 )
+ return rc;
/* Find property */
- while ( 1 ) {
+ for ( depth = 0 ; depth == 0 ; depth += desc->depth ) {
- /* Traverse tree */
- if ( ( rc = fdt_traverse ( &pos, desc ) ) != 0 ) {
- DBGC ( &fdt, "FDT +%#04x has no property \"%s\": %s\n",
- offset, name, strerror ( rc ) );
+ /* Describe token */
+ if ( ( rc = fdt_next ( fdt, desc ) ) != 0 ) {
+ DBGC ( fdt, "FDT +%#04x has malformed node: %s\n",
+ offset, strerror ( rc ) );
return rc;
}
/* Check for matching immediate child property */
- if ( ( pos.depth == 0 ) && desc->data ) {
- DBGC2 ( &fdt, "FDT +%#04x has property \"%s\" len "
- "%#zx\n", offset, desc->name, desc->len );
- if ( strcmp ( name, desc->name ) == 0 ) {
- DBGC2 ( &fdt, "FDT +%#04x found property "
- "\"%s\"\n", offset, desc->name );
- DBGC2_HDA ( &fdt, 0, desc->data, desc->len );
+ if ( desc->data ) {
+ DBGC2 ( fdt, "FDT +%#04x has property \"%s\" at "
+ "+%#04x len %#zx\n", offset, desc->name,
+ desc->offset, desc->len );
+ assert ( desc->depth == 0 );
+ if ( fdt_match ( desc, name ) ) {
+ DBGC2_HDA ( fdt, 0, desc->data, desc->len );
return 0;
}
}
}
+
+ DBGC2 ( fdt, "FDT +%#04x has no property \"%s\"\n", offset, name );
+ return -ENOENT;
}
/**
- * Find string property
+ * Find strings property
*
+ * @v fdt Device tree
* @v offset Starting node offset
* @v name Property name
+ * @v count String count to fill in
* @ret string String property, or NULL on error
*/
-const char * fdt_string ( unsigned int offset, const char *name ) {
+const char * fdt_strings ( struct fdt *fdt, unsigned int offset,
+ const char *name, unsigned int *count ) {
struct fdt_descriptor desc;
+ const char *data;
+ size_t len;
int rc;
+ /* Return a zero count on error */
+ *count = 0;
+
/* Find property */
- if ( ( rc = fdt_property ( offset, name, &desc ) ) != 0 )
+ if ( ( rc = fdt_property ( fdt, offset, name, &desc ) ) != 0 )
return NULL;
/* Check NUL termination */
- if ( strnlen ( desc.data, desc.len ) == desc.len ) {
- DBGC ( &fdt, "FDT unterminated string property \"%s\"\n",
+ data = desc.data;
+ if ( desc.len && ( data[ desc.len - 1 ] != '\0' ) ) {
+ DBGC ( fdt, "FDT unterminated string property \"%s\"\n",
name );
return NULL;
}
- return desc.data;
+ /* Count number of strings */
+ for ( len = desc.len ; len-- ; ) {
+ if ( data[len] == '\0' )
+ (*count)++;
+ }
+
+ return data;
+}
+
+/**
+ * Find string property
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v name Property name
+ * @ret string String property, or NULL on error
+ */
+const char * fdt_string ( struct fdt *fdt, unsigned int offset,
+ const char *name ) {
+ unsigned int count;
+
+ /* Find strings property */
+ return fdt_strings ( fdt, offset, name, &count );
+}
+
+/**
+ * Get integer property
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v name Property name
+ * @v index Starting cell index
+ * @v count Number of cells (or 0 to read all remaining cells)
+ * @v value Integer value to fill in
+ * @ret rc Return status code
+ */
+int fdt_cells ( struct fdt *fdt, unsigned int offset, const char *name,
+ unsigned int index, unsigned int count, uint64_t *value ) {
+ struct fdt_descriptor desc;
+ const uint32_t *cell;
+ unsigned int total;
+ int rc;
+
+ /* Clear value */
+ *value = 0;
+
+ /* Find property */
+ if ( ( rc = fdt_property ( fdt, offset, name, &desc ) ) != 0 )
+ return rc;
+ cell = desc.data;
+
+ /* Determine number of cells */
+ total = ( desc.len / sizeof ( *cell ) );
+ if ( ( index > total ) || ( count > ( total - index ) ) ) {
+ DBGC ( fdt, "FDT truncated integer \"%s\"\n", name );
+ return -ERANGE;
+ }
+ if ( ! count )
+ count = ( total - index );
+ if ( count > ( sizeof ( *value ) / sizeof ( *cell ) ) ) {
+ DBGC ( fdt, "FDT overlength integer \"%s\"\n", name );
+ return -ERANGE;
+ }
+
+ /* Read value */
+ for ( cell += index ; count ; cell++, count-- ) {
+ *value <<= 32;
+ *value |= be32_to_cpu ( *cell );
+ }
+
+ return 0;
+}
+
+/**
+ * Get 64-bit integer property
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v name Property name
+ * @v value Integer value to fill in
+ * @ret rc Return status code
+ */
+int fdt_u64 ( struct fdt *fdt, unsigned int offset, const char *name,
+ uint64_t *value ) {
+ int rc;
+
+ /* Read value */
+ if ( ( rc = fdt_cells ( fdt, offset, name, 0, 0, value ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Get 32-bit integer property
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v name Property name
+ * @v value Integer value to fill in
+ * @ret rc Return status code
+ */
+int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name,
+ uint32_t *value ) {
+ uint64_t value64;
+ int rc;
+
+ /* Read value */
+ if ( ( rc = fdt_u64 ( fdt, offset, name, &value64 ) ) != 0 )
+ return rc;
+
+ /* Check range */
+ *value = value64;
+ if ( *value != value64 ) {
+ DBGC ( fdt, "FDT overlength 32-bit integer \"%s\"\n", name );
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+/**
+ * Get package handle (phandle) property
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @ret phandle Package handle, or 0 on error
+ */
+uint32_t fdt_phandle ( struct fdt *fdt, unsigned int offset ) {
+ uint32_t phandle;
+ int rc;
+
+ /* Get "phandle" or "linux,phandle" property */
+ if ( ( ( rc = fdt_u32 ( fdt, offset, "phandle", &phandle ) ) == 0 ) ||
+ ( ( rc = fdt_u32 ( fdt, offset, "linux,phandle",
+ &phandle ) ) == 0 ) ) {
+ assert ( phandle != 0 );
+ return phandle;
+ }
+
+ return 0;
+}
+
+/**
+ * Get region cell size specification
+ *
+ * @v fdt Device tree
+ * @v offset Starting (parent) node offset
+ * @v regs Region cell size specification to fill in
+ *
+ * Note that #address-cells and #size-cells are defined on the
+ * immediate parent node, rather than on the node with the "reg"
+ * property itself.
+ */
+void fdt_reg_cells ( struct fdt *fdt, unsigned int offset,
+ struct fdt_reg_cells *regs ) {
+ int rc;
+
+ /* Read #address-cells, if present */
+ if ( ( rc = fdt_u32 ( fdt, offset, "#address-cells",
+ &regs->address_cells ) ) != 0 ) {
+ regs->address_cells = FDT_DEFAULT_ADDRESS_CELLS;
+ }
+
+ /* Read #size-cells, if present */
+ if ( ( rc = fdt_u32 ( fdt, offset, "#size-cells",
+ &regs->size_cells ) ) != 0 ) {
+ regs->size_cells = FDT_DEFAULT_SIZE_CELLS;
+ }
+
+ /* Calculate stride */
+ regs->stride = ( regs->address_cells + regs->size_cells );
+}
+
+/**
+ * Get parent region cell size specification
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v regs Region cell size specification to fill in
+ * @ret rc Return status code
+ */
+int fdt_parent_reg_cells ( struct fdt *fdt, unsigned int offset,
+ struct fdt_reg_cells *regs ) {
+ unsigned int parent;
+ int rc;
+
+ /* Get parent node */
+ if ( ( rc = fdt_parent ( fdt, offset, &parent ) ) != 0 )
+ return rc;
+
+ /* Read #address-cells and #size-cells, if present */
+ fdt_reg_cells ( fdt, parent, regs );
+
+ return 0;
+}
+
+/**
+ * Get number of regions
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v regs Region cell size specification
+ * @ret count Number of regions, or negative error
+ */
+int fdt_reg_count ( struct fdt *fdt, unsigned int offset,
+ struct fdt_reg_cells *regs ) {
+ struct fdt_descriptor desc;
+ const uint32_t *cell;
+ unsigned int count;
+ int rc;
+
+ /* Find property */
+ if ( ( rc = fdt_property ( fdt, offset, "reg", &desc ) ) != 0 )
+ return rc;
+
+ /* Determine number of regions */
+ count = ( desc.len / ( regs->stride * sizeof ( *cell ) ) );
+ return count;
+}
+
+/**
+ * Get region address
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v regs Region cell size specification
+ * @v index Region index
+ * @v address Region starting address to fill in
+ * @ret rc Return status code
+ */
+int fdt_reg_address ( struct fdt *fdt, unsigned int offset,
+ struct fdt_reg_cells *regs, unsigned int index,
+ uint64_t *address ) {
+ unsigned int cell = ( index * regs->stride );
+ int rc;
+
+ /* Read relevant portion of region array */
+ if ( ( rc = fdt_cells ( fdt, offset, "reg", cell, regs->address_cells,
+ address ) ) != 0 ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Get region size
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v regs Region cell size specification
+ * @v index Region index
+ * @v size Region size to fill in
+ * @ret rc Return status code
+ */
+int fdt_reg_size ( struct fdt *fdt, unsigned int offset,
+ struct fdt_reg_cells *regs, unsigned int index,
+ uint64_t *size ) {
+ unsigned int cell = ( ( index * regs->stride ) + regs->address_cells );
+ int rc;
+
+ /* Read relevant portion of region array */
+ if ( ( rc = fdt_cells ( fdt, offset, "reg", cell, regs->size_cells,
+ size ) ) != 0 ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Get unsized single-entry region address
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v address Region address to fill in
+ * @ret rc Return status code
+ *
+ * Many region types (e.g. I2C bus addresses) can only ever contain a
+ * single region with no size cells specified.
+ */
+int fdt_reg ( struct fdt *fdt, unsigned int offset, uint64_t *region ) {
+ struct fdt_reg_cells regs;
+ int rc;
+
+ /* Get parent region cell size specification */
+ if ( ( rc = fdt_parent_reg_cells ( fdt, offset, &regs ) ) != 0 )
+ return rc;
+
+ /* Get first region address */
+ if ( ( rc = fdt_reg_address ( fdt, offset, &regs, 0, region ) ) != 0 )
+ return rc;
+
+ return 0;
}
/**
* Get MAC address from property
*
+ * @v fdt Device tree
* @v offset Starting node offset
* @v netdev Network device
* @ret rc Return status code
*/
-int fdt_mac ( unsigned int offset, struct net_device *netdev ) {
+int fdt_mac ( struct fdt *fdt, unsigned int offset,
+ struct net_device *netdev ) {
struct fdt_descriptor desc;
size_t len;
int rc;
/* Find applicable MAC address property */
- if ( ( ( rc = fdt_property ( offset, "mac-address", &desc ) ) != 0 ) &&
- ( ( rc = fdt_property ( offset, "local-mac-address",
+ if ( ( ( rc = fdt_property ( fdt, offset, "mac-address",
+ &desc ) ) != 0 ) &&
+ ( ( rc = fdt_property ( fdt, offset, "local-mac-address",
&desc ) ) != 0 ) ) {
return rc;
}
@@ -389,9 +881,9 @@ int fdt_mac ( unsigned int offset, struct net_device *netdev ) {
/* Check length */
len = netdev->ll_protocol->hw_addr_len;
if ( len != desc.len ) {
- DBGC ( &fdt, "FDT malformed MAC address \"%s\":\n",
+ DBGC ( fdt, "FDT malformed MAC address \"%s\":\n",
desc.name );
- DBGC_HDA ( &fdt, 0, desc.data, desc.len );
+ DBGC_HDA ( fdt, 0, desc.data, desc.len );
return -ERANGE;
}
@@ -402,85 +894,576 @@ int fdt_mac ( unsigned int offset, struct net_device *netdev ) {
}
/**
- * Register device tree
+ * Parse device tree
*
- * @v fdt Device tree header
+ * @v fdt Device tree
+ * @v hdr Device tree header
+ * @v max_len Maximum device tree length
* @ret rc Return status code
*/
-int register_fdt ( const struct fdt_header *hdr ) {
- const uint8_t *end;
+int fdt_parse ( struct fdt *fdt, struct fdt_header *hdr, size_t max_len ) {
+ const uint8_t *nul;
+ unsigned int chosen;
+ size_t end;
+
+ /* Sanity check */
+ if ( sizeof ( *hdr ) > max_len ) {
+ DBGC ( fdt, "FDT length %#zx too short for header\n",
+ max_len );
+ goto err;
+ }
/* Record device tree location */
- fdt.hdr = hdr;
- fdt.len = be32_to_cpu ( hdr->totalsize );
- DBGC ( &fdt, "FDT version %d at %p+%#04zx\n",
- be32_to_cpu ( hdr->version ), fdt.hdr, fdt.len );
+ fdt->hdr = hdr;
+ fdt->len = be32_to_cpu ( hdr->totalsize );
+ fdt->used = sizeof ( *hdr );
+ if ( fdt->len > max_len ) {
+ DBGC ( fdt, "FDT has invalid length %#zx / %#zx\n",
+ fdt->len, max_len );
+ goto err;
+ }
+ DBGC ( fdt, "FDT version %d at %p+%#04zx (phys %#08lx)\n",
+ be32_to_cpu ( hdr->version ), fdt->hdr, fdt->len,
+ virt_to_phys ( hdr ) );
/* Check signature */
if ( hdr->magic != cpu_to_be32 ( FDT_MAGIC ) ) {
- DBGC ( &fdt, "FDT has invalid magic value %#08x\n",
+ DBGC ( fdt, "FDT has invalid magic value %#08x\n",
be32_to_cpu ( hdr->magic ) );
goto err;
}
/* Check version */
if ( hdr->last_comp_version != cpu_to_be32 ( FDT_VERSION ) ) {
- DBGC ( &fdt, "FDT unsupported version %d\n",
+ DBGC ( fdt, "FDT unsupported version %d\n",
be32_to_cpu ( hdr->last_comp_version ) );
goto err;
}
/* Record structure block location */
- fdt.structure = be32_to_cpu ( hdr->off_dt_struct );
- fdt.structure_len = be32_to_cpu ( hdr->size_dt_struct );
- DBGC ( &fdt, "FDT structure block at +[%#04x,%#04zx)\n",
- fdt.structure, ( fdt.structure + fdt.structure_len ) );
- if ( ( fdt.structure > fdt.len ) ||
- ( fdt.structure_len > ( fdt.len - fdt.structure ) ) ) {
- DBGC ( &fdt, "FDT structure block exceeds table\n" );
+ fdt->structure = be32_to_cpu ( hdr->off_dt_struct );
+ fdt->structure_len = be32_to_cpu ( hdr->size_dt_struct );
+ DBGC ( fdt, "FDT structure block at +[%#04x,%#04zx)\n",
+ fdt->structure, ( fdt->structure + fdt->structure_len ) );
+ if ( ( fdt->structure > fdt->len ) ||
+ ( fdt->structure_len > ( fdt->len - fdt->structure ) ) ) {
+ DBGC ( fdt, "FDT structure block exceeds table\n" );
goto err;
}
- if ( ( fdt.structure | fdt.structure_len ) &
+ if ( ( fdt->structure | fdt->structure_len ) &
( FDT_STRUCTURE_ALIGN - 1 ) ) {
- DBGC ( &fdt, "FDT structure block is misaligned\n" );
+ DBGC ( fdt, "FDT structure block is misaligned\n" );
goto err;
}
+ end = ( fdt->structure + fdt->structure_len );
+ if ( fdt->used < end )
+ fdt->used = end;
/* Record strings block location */
- fdt.strings = be32_to_cpu ( hdr->off_dt_strings );
- fdt.strings_len = be32_to_cpu ( hdr->size_dt_strings );
- DBGC ( &fdt, "FDT strings block at +[%#04x,%#04zx)\n",
- fdt.strings, ( fdt.strings + fdt.strings_len ) );
- if ( ( fdt.strings > fdt.len ) ||
- ( fdt.strings_len > ( fdt.len - fdt.strings ) ) ) {
- DBGC ( &fdt, "FDT strings block exceeds table\n" );
+ fdt->strings = be32_to_cpu ( hdr->off_dt_strings );
+ fdt->strings_len = be32_to_cpu ( hdr->size_dt_strings );
+ DBGC ( fdt, "FDT strings block at +[%#04x,%#04zx)\n",
+ fdt->strings, ( fdt->strings + fdt->strings_len ) );
+ if ( ( fdt->strings > fdt->len ) ||
+ ( fdt->strings_len > ( fdt->len - fdt->strings ) ) ) {
+ DBGC ( fdt, "FDT strings block exceeds table\n" );
goto err;
}
+ end = ( fdt->strings + fdt->strings_len );
+ if ( fdt->used < end )
+ fdt->used = end;
/* Shrink strings block to ensure NUL termination safety */
- end = ( fdt.raw + fdt.strings + fdt.strings_len );
- for ( ; fdt.strings_len ; fdt.strings_len-- ) {
- if ( *(--end) == '\0' )
+ nul = ( fdt->raw + fdt->strings + fdt->strings_len );
+ for ( ; fdt->strings_len ; fdt->strings_len-- ) {
+ if ( *(--nul) == '\0' )
break;
}
- if ( fdt.strings_len != be32_to_cpu ( hdr->size_dt_strings ) ) {
- DBGC ( &fdt, "FDT strings block shrunk to +[%#04x,%#04zx)\n",
- fdt.strings, ( fdt.strings + fdt.strings_len ) );
+ if ( fdt->strings_len != be32_to_cpu ( hdr->size_dt_strings ) ) {
+ DBGC ( fdt, "FDT strings block shrunk to +[%#04x,%#04zx)\n",
+ fdt->strings, ( fdt->strings + fdt->strings_len ) );
}
- /* Print model name (for debugging) */
- DBGC ( &fdt, "FDT model is \"%s\"\n", fdt_string ( 0, "model" ) );
+ /* Record memory reservation block location */
+ fdt->reservations = be32_to_cpu ( hdr->off_mem_rsvmap );
+ DBGC ( fdt, "FDT memory reservations at +[%#04x,...)\n",
+ fdt->reservations );
+ if ( fdt->used <= fdt->reservations ) {
+ /* No size field exists: assume whole table is used */
+ fdt->used = fdt->len;
+ }
+
+ /* Identify free space (if any) */
+ if ( fdt->used < fdt->len ) {
+ DBGC ( fdt, "FDT free space at +[%#04zx,%#04zx)\n",
+ fdt->used, fdt->len );
+ }
+
+ /* Print model name and boot arguments (for debugging) */
+ if ( DBG_LOG ) {
+ DBGC ( fdt, "FDT model is \"%s\"\n",
+ fdt_string ( fdt, 0, "model" ) );
+ if ( fdt_child ( fdt, 0, "chosen", &chosen ) == 0 ) {
+ DBGC ( fdt, "FDT boot arguments \"%s\"\n",
+ fdt_string ( fdt, chosen, "bootargs" ) );
+ }
+ }
return 0;
err:
- DBGC_HDA ( &fdt, 0, hdr, sizeof ( *hdr ) );
- fdt.hdr = NULL;
+ DBGC_HDA ( fdt, 0, hdr, sizeof ( *hdr ) );
+ memset ( fdt, 0, sizeof ( *fdt ) );
return -EINVAL;
}
-/* Drag in objects via register_fdt */
-REQUIRING_SYMBOL ( register_fdt );
+/**
+ * Parse device tree image
+ *
+ * @v fdt Device tree
+ * @v image Image
+ * @ret rc Return status code
+ */
+static int fdt_parse_image ( struct fdt *fdt, struct image *image ) {
+ int rc;
+
+ /* Parse image */
+ if ( ( rc = fdt_parse ( fdt, image->rwdata, image->len ) ) != 0 ) {
+ DBGC ( fdt, "FDT image \"%s\" is invalid: %s\n",
+ image->name, strerror ( rc ) );
+ return rc;
+ }
+
+ DBGC ( fdt, "FDT image is \"%s\"\n", image->name );
+ return 0;
+}
+
+/**
+ * Insert empty space
+ *
+ * @v fdt Device tree
+ * @v offset Offset at which to insert space
+ * @v len Length to insert (must be a multiple of FDT_MAX_ALIGN)
+ * @ret rc Return status code
+ */
+static int fdt_insert ( struct fdt *fdt, unsigned int offset, size_t len ) {
+ size_t free;
+ size_t new;
+ int rc;
+
+ /* Sanity checks */
+ assert ( offset <= fdt->used );
+ assert ( fdt->used <= fdt->len );
+ assert ( ( len % FDT_MAX_ALIGN ) == 0 );
+
+ /* Reallocate tree if necessary */
+ free = ( fdt->len - fdt->used );
+ if ( free < len ) {
+ if ( ! fdt->realloc ) {
+ DBGC ( fdt, "FDT is not reallocatable\n" );
+ return -ENOTSUP;
+ }
+ new = ( fdt->len + ( len - free ) + FDT_INSERT_PAD );
+ if ( ( rc = fdt->realloc ( fdt, new ) ) != 0 )
+ return rc;
+ }
+ assert ( ( fdt->used + len ) <= fdt->len );
+
+ /* Insert empty space */
+ memmove ( ( fdt->raw + offset + len ), ( fdt->raw + offset ),
+ ( fdt->used - offset ) );
+ memset ( ( fdt->raw + offset ), 0, len );
+ fdt->used += len;
+
+ /* Update offsets
+ *
+ * We assume that we never need to legitimately insert data at
+ * the start of a block, and therefore can unambiguously
+ * determine which block offsets need to be updated.
+ *
+ * It is the caller's responsibility to update the length (and
+ * contents) of the block into which it has inserted space.
+ */
+ if ( fdt->structure >= offset ) {
+ fdt->structure += len;
+ fdt->hdr->off_dt_struct = cpu_to_be32 ( fdt->structure );
+ DBGC ( fdt, "FDT structure block now at +[%#04x,%#04zx)\n",
+ fdt->structure,
+ ( fdt->structure + fdt->structure_len ) );
+ }
+ if ( fdt->strings >= offset ) {
+ fdt->strings += len;
+ fdt->hdr->off_dt_strings = cpu_to_be32 ( fdt->strings );
+ DBGC ( fdt, "FDT strings block now at +[%#04x,%#04zx)\n",
+ fdt->strings, ( fdt->strings + fdt->strings_len ) );
+ }
+ if ( fdt->reservations >= offset ) {
+ fdt->reservations += len;
+ fdt->hdr->off_mem_rsvmap = cpu_to_be32 ( fdt->reservations );
+ DBGC ( fdt, "FDT memory reservations now at +[%#04x,...)\n",
+ fdt->reservations );
+ }
+
+ return 0;
+}
+
+/**
+ * Fill space in structure block with FDT_NOP
+ *
+ * @v fdt Device tree
+ * @v offset Starting offset
+ * @v len Length (must be a multiple of FDT_STRUCTURE_ALIGN)
+ */
+static void fdt_nop ( struct fdt *fdt, unsigned int offset, size_t len ) {
+ fdt_token_t *token;
+ unsigned int count;
+
+ /* Sanity check */
+ assert ( ( len % FDT_STRUCTURE_ALIGN ) == 0 );
+
+ /* Fill with FDT_NOP */
+ token = ( fdt->raw + fdt->structure + offset );
+ count = ( len / sizeof ( *token ) );
+ while ( count-- )
+ *(token++) = cpu_to_be32 ( FDT_NOP );
+}
+
+/**
+ * Insert FDT_NOP padded space in structure block
+ *
+ * @v fdt Device tree
+ * @v offset Offset at which to insert space
+ * @v len Minimal length to insert
+ * @ret rc Return status code
+ */
+static int fdt_insert_nop ( struct fdt *fdt, unsigned int offset,
+ size_t len ) {
+ int rc;
+
+ /* Sanity check */
+ assert ( ( offset % FDT_STRUCTURE_ALIGN ) == 0 );
+
+ /* Round up inserted length to maximal alignment */
+ len = ( ( len + FDT_MAX_ALIGN - 1 ) & ~( FDT_MAX_ALIGN - 1 ) );
+
+ /* Insert empty space in structure block */
+ if ( ( rc = fdt_insert ( fdt, ( fdt->structure + offset ),
+ len ) ) != 0 )
+ return rc;
+
+ /* Fill with NOPs */
+ fdt_nop ( fdt, offset, len );
+
+ /* Update structure block size */
+ fdt->structure_len += len;
+ fdt->hdr->size_dt_struct = cpu_to_be32 ( fdt->structure_len );
+ DBGC ( fdt, "FDT structure block now at +[%#04x,%#04zx)\n",
+ fdt->structure, ( fdt->structure + fdt->structure_len ) );
+
+ return 0;
+}
+
+/**
+ * Insert string in strings block
+ *
+ * @v fdt Device tree
+ * @v string String
+ * @v offset String offset to fill in
+ * @ret rc Return status code
+ */
+static int fdt_insert_string ( struct fdt *fdt, const char *string,
+ unsigned int *offset ) {
+ size_t len = ( strlen ( string ) + 1 /* NUL */ );
+ int rc;
+
+ /* Round up inserted length to maximal alignment */
+ len = ( ( len + FDT_MAX_ALIGN - 1 ) & ~( FDT_MAX_ALIGN - 1 ) );
+
+ /* Insert space at end of strings block */
+ if ( ( rc = fdt_insert ( fdt, ( fdt->strings + fdt->strings_len ),
+ len ) ) != 0 )
+ return rc;
+
+ /* Append string to strings block */
+ *offset = fdt->strings_len;
+ strcpy ( ( fdt->raw + fdt->strings + *offset ), string );
+
+ /* Update strings block size */
+ fdt->strings_len += len;
+ fdt->hdr->size_dt_strings = cpu_to_be32 ( fdt->strings_len );
+ DBGC ( fdt, "FDT strings block now at +[%#04x,%#04zx)\n",
+ fdt->strings, ( fdt->strings + fdt->strings_len ) );
+
+ return 0;
+}
+
+/**
+ * Ensure child node exists
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v name New node name
+ * @v child Child node offset to fill in
+ * @ret rc Return status code
+ */
+static int fdt_ensure_child ( struct fdt *fdt, unsigned int offset,
+ const char *name, unsigned int *child ) {
+ size_t name_len = ( strlen ( name ) + 1 /* NUL */ );
+ fdt_token_t *token;
+ size_t len;
+ int rc;
+
+ /* Find existing child node, if any */
+ if ( ( rc = fdt_child ( fdt, offset, name, child ) ) == 0 )
+ return 0;
+
+ /* Find end of parent node */
+ if ( ( rc = fdt_end ( fdt, offset, child ) ) != 0 )
+ return rc;
+
+ /* Insert space for child node (with maximal alignment) */
+ len = ( sizeof ( fdt_token_t ) /* BEGIN_NODE */ + name_len +
+ sizeof ( fdt_token_t ) /* END_NODE */ );
+ if ( ( rc = fdt_insert_nop ( fdt, *child, len ) ) != 0 )
+ return rc;
+
+ /* Construct node */
+ token = ( fdt->raw + fdt->structure + *child );
+ *(token++) = cpu_to_be32 ( FDT_BEGIN_NODE );
+ memcpy ( token, name, name_len );
+ name_len = ( ( name_len + FDT_STRUCTURE_ALIGN - 1 ) &
+ ~( FDT_STRUCTURE_ALIGN - 1 ) );
+ token = ( ( ( void * ) token ) + name_len );
+ *(token++) = cpu_to_be32 ( FDT_END_NODE );
+ DBGC2 ( fdt, "FDT +%#04x created child \"%s\" at +%#04x\n",
+ offset, name, *child );
+
+ return 0;
+}
+
+/**
+ * Set property value
+ *
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v name Property name
+ * @v data Property data, or NULL to delete property
+ * @v len Length of property data
+ * @ret rc Return status code
+ */
+static int fdt_set ( struct fdt *fdt, unsigned int offset, const char *name,
+ const void *data, size_t len ) {
+ struct fdt_descriptor desc;
+ struct {
+ fdt_token_t token;
+ struct fdt_prop prop;
+ uint8_t data[0];
+ } __attribute__ (( packed )) *hdr;
+ unsigned int string;
+ size_t erase;
+ size_t insert;
+ int rc;
+
+ /* Find and reuse existing property, if any */
+ if ( ( rc = fdt_property ( fdt, offset, name, &desc ) ) == 0 ) {
+
+ /* Reuse existing name */
+ hdr = ( fdt->raw + fdt->structure + desc.offset );
+ string = be32_to_cpu ( hdr->prop.name_off );
+
+ /* Erase existing property */
+ erase = ( sizeof ( *hdr ) + desc.len );
+ erase = ( ( erase + FDT_STRUCTURE_ALIGN - 1 ) &
+ ~( FDT_STRUCTURE_ALIGN - 1 ) );
+ fdt_nop ( fdt, desc.offset, erase );
+ DBGC2 ( fdt, "FDT +%#04x erased property \"%s\"\n",
+ offset, name );
+
+ /* Calculate insertion position and length */
+ insert = ( ( desc.len < len ) ? ( len - desc.len ) : 0 );
+
+ } else {
+
+ /* Create name */
+ if ( ( rc = fdt_insert_string ( fdt, name, &string ) ) != 0 )
+ return rc;
+
+ /* Enter node */
+ if ( ( rc = fdt_enter ( fdt, offset, &desc ) ) != 0 )
+ return rc;
+ assert ( desc.depth > 0 );
+ desc.offset = desc.next;
+
+ /* Calculate insertion length */
+ insert = ( sizeof ( *hdr ) + len );
+ }
+
+ /* Leave property erased if applicable */
+ if ( ! data )
+ return 0;
+
+ /* Insert space */
+ if ( ( rc = fdt_insert_nop ( fdt, desc.offset, insert ) ) != 0 )
+ return rc;
+
+ /* Construct property */
+ hdr = ( fdt->raw + fdt->structure + desc.offset );
+ hdr->token = cpu_to_be32 ( FDT_PROP );
+ hdr->prop.len = cpu_to_be32 ( len );
+ hdr->prop.name_off = cpu_to_be32 ( string );
+ memset ( hdr->data, 0, ( ( len + FDT_STRUCTURE_ALIGN - 1 ) &
+ ~( FDT_STRUCTURE_ALIGN - 1 ) ) );
+ memcpy ( hdr->data, data, len );
+ DBGC2 ( fdt, "FDT +%#04x created property \"%s\"\n", offset, name );
+ DBGC2_HDA ( fdt, 0, hdr->data, len );
+
+ return 0;
+}
+
+/**
+ * Reallocate device tree via urealloc()
+ *
+ * @v fdt Device tree
+ * @v len New total length
+ * @ret rc Return status code
+ */
+static int fdt_urealloc ( struct fdt *fdt, size_t len ) {
+ void *new;
+
+ /* Sanity check */
+ assert ( len >= fdt->used );
+
+ /* Attempt reallocation */
+ new = urealloc ( fdt->raw, len );
+ if ( ! new ) {
+ DBGC ( fdt, "FDT could not reallocate from +%#04zx to "
+ "+%#04zx\n", fdt->len, len );
+ return -ENOMEM;
+ }
+ DBGC ( fdt, "FDT reallocated from +%#04zx to +%#04zx\n",
+ fdt->len, len );
+
+ /* Update device tree */
+ fdt->raw = new;
+ fdt->len = len;
+ fdt->hdr->totalsize = cpu_to_be32 ( len );
+
+ return 0;
+}
+
+/**
+ * Populate device tree with boot arguments
+ *
+ * @v fdt Device tree
+ * @v cmdline Command line, or NULL
+ * @v initrd Initial ramdisk address (or 0 for no initrd)
+ * @v initrd_len Initial ramdisk length (or 0 for no initrd)
+ * @ret rc Return status code
+ */
+static int fdt_bootargs ( struct fdt *fdt, const char *cmdline,
+ physaddr_t initrd, size_t initrd_len ) {
+ unsigned int chosen;
+ physaddr_t addr;
+ const void *data;
+ size_t len;
+ int rc;
+
+ /* Ensure "chosen" node exists */
+ if ( ( rc = fdt_ensure_child ( fdt, 0, "chosen", &chosen ) ) != 0 )
+ return rc;
+
+ /* Set or clear "bootargs" property */
+ len = ( cmdline ? ( strlen ( cmdline ) + 1 /* NUL */ ) : 0 );
+ if ( ( rc = fdt_set ( fdt, chosen, "bootargs", cmdline, len ) ) != 0 )
+ return rc;
+
+ /* Set or clear initrd properties */
+ data = ( initrd_len ? &addr : NULL );
+ len = ( initrd_len ? sizeof ( addr ) : 0 );
+ addr = initrd;
+ addr = ( ( sizeof ( addr ) == sizeof ( uint64_t ) ) ?
+ cpu_to_be64 ( addr ) : cpu_to_be32 ( addr ) );
+ if ( ( rc = fdt_set ( fdt, chosen, "linux,initrd-start", data,
+ len ) ) != 0 )
+ return rc;
+ addr = ( initrd + initrd_len );
+ addr = ( ( sizeof ( addr ) == sizeof ( uint64_t ) ) ?
+ cpu_to_be64 ( addr ) : cpu_to_be32 ( addr ) );
+ if ( ( rc = fdt_set ( fdt, chosen, "linux,initrd-end", data,
+ len ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Create device tree
+ *
+ * @v hdr Device tree header to fill in (may be set to NULL)
+ * @v cmdline Command line, or NULL
+ * @v initrd Initial ramdisk address (or 0 for no initrd)
+ * @v initrd_len Initial ramdisk length (or 0 for no initrd)
+ * @ret rc Return status code
+ */
+int fdt_create ( struct fdt_header **hdr, const char *cmdline,
+ physaddr_t initrd, size_t initrd_len ) {
+ struct image *image;
+ struct fdt fdt;
+ void *copy;
+ int rc;
+
+ /* Use system FDT as the base by default */
+ memcpy ( &fdt, &sysfdt, sizeof ( fdt ) );
+
+ /* If an FDT image exists, use this instead */
+ image = find_image_tag ( &fdt_image );
+ if ( image && ( ( rc = fdt_parse_image ( &fdt, image ) ) != 0 ) )
+ goto err_image;
+
+ /* Exit successfully if we have no base FDT */
+ if ( ! fdt.len ) {
+ DBGC ( &fdt, "FDT has no base tree\n" );
+ goto no_fdt;
+ }
+
+ /* Create modifiable copy */
+ copy = umalloc ( fdt.len );
+ if ( ! copy ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ memcpy ( copy, fdt.raw, fdt.len );
+ fdt.raw = copy;
+ fdt.realloc = fdt_urealloc;
+
+ /* Populate boot arguments */
+ if ( ( rc = fdt_bootargs ( &fdt, cmdline, initrd, initrd_len ) ) != 0 )
+ goto err_bootargs;
+
+ no_fdt:
+ *hdr = fdt.raw;
+ return 0;
+
+ err_bootargs:
+ ufree ( fdt.raw );
+ err_alloc:
+ err_image:
+ return rc;
+}
+
+/**
+ * Remove device tree
+ *
+ * @v hdr Device tree header, or NULL
+ */
+void fdt_remove ( struct fdt_header *hdr ) {
+
+ /* Free modifiable copy */
+ ufree ( hdr );
+}
+
+/* Drag in objects via fdt_describe() */
+REQUIRING_SYMBOL ( fdt_describe );
/* Drag in device tree configuration */
REQUIRE_OBJECT ( config_fdt );
diff --git a/src/core/fdtcon.c b/src/core/fdtcon.c
new file mode 100644
index 000000000..3dc71af6d
--- /dev/null
+++ b/src/core/fdtcon.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <ipxe/serial.h>
+#include <ipxe/devtree.h>
+#include <ipxe/fdt.h>
+
+/** @file
+ *
+ * Flattened Device Tree serial console
+ *
+ */
+
+#ifdef SERIAL_FDT
+#define SERIAL_PREFIX_fdt
+#else
+#define SERIAL_PREFIX_fdt __fdt_
+#endif
+
+/** FDT console parent device */
+static struct device fdtcon_parent = {
+ .name = "fdtcon",
+ .siblings = LIST_HEAD_INIT ( fdtcon_parent.siblings ),
+ .children = LIST_HEAD_INIT ( fdtcon_parent.children ),
+};
+
+/** Colour for debug messages */
+#define colour &fdtcon_parent
+
+/**
+ * Identify default serial console
+ *
+ * @ret uart Default serial console UART, or NULL
+ */
+static struct uart * fdtcon_default ( void ) {
+ unsigned int chosen;
+ unsigned int stdout;
+ const char *path;
+ struct uart *prev;
+ struct uart *uart;
+ int rc;
+
+ /* Record existing UART, if any */
+ prev = list_last_entry ( &uarts, struct uart, list );
+
+ /* Locate "/chosen" node */
+ if ( ( rc = fdt_path ( &sysfdt, "/chosen", &chosen ) ) != 0 ) {
+ DBGC ( colour, "FDTCON could not locate \"/chosen\": %s\n",
+ strerror ( rc ) );
+ return NULL;
+ }
+
+ /* Get console device path (or alias) */
+ path = fdt_string ( &sysfdt, chosen, "stdout-path" );
+ if ( ! path ) {
+ DBGC ( colour, "FDTCON has no console device\n" );
+ return NULL;
+ }
+ DBGC ( colour, "FDTCON console device is \"%s\"\n", path );
+
+ /* Locate device */
+ if ( ( ( rc = fdt_path ( &sysfdt, path, &stdout ) ) != 0 ) &&
+ ( ( rc = fdt_alias ( &sysfdt, path, &stdout ) ) != 0 ) ) {
+ DBGC ( colour, "FDTCON could not locate \"/%s\": %s\n",
+ path, strerror ( rc ) );
+ return NULL;
+ }
+
+ /* Probe device */
+ if ( ( rc = dt_probe_node ( &fdtcon_parent, stdout ) ) != 0 ) {
+ DBGC ( colour, "FDTCON could not probe \"%s\": %s\n",
+ path, strerror ( rc ) );
+ return NULL;
+ }
+
+ /* Use newly added UART, if any */
+ uart = list_last_entry ( &uarts, struct uart, list );
+ if ( uart == prev )
+ return NULL;
+
+ DBGC ( colour, "FDTCON using UART %s\n", uart->name );
+ return uart;
+}
+
+PROVIDE_SERIAL ( fdt, default_serial_console, fdtcon_default );
diff --git a/src/core/fdtmem.c b/src/core/fdtmem.c
new file mode 100644
index 000000000..97e3bb2f6
--- /dev/null
+++ b/src/core/fdtmem.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#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>
+
+/** @file
+ *
+ * Flattened Device Tree memory map
+ *
+ */
+
+/** Start address of the iPXE image */
+extern char _prefix[];
+
+/** End address of the iPXE image */
+extern char _end[];
+
+/** Total in-memory size (calculated by linker) */
+extern size_t ABS_SYMBOL ( _memsz );
+static size_t memsz = ABS_VALUE_INIT ( _memsz );
+
+/** 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 );
+
+/** In-use memory region for iPXE and system device tree copy */
+struct used_region fdtmem_used __used_region = {
+ .name = "iPXE/FDT",
+};
+
+/** Maximum accessible physical address */
+static physaddr_t fdtmem_max;
+
+/** Maximum 32-bit physical address */
+#define FDTMEM_MAX32 0xffffffff
+
+/**
+ * Update memory region descriptor based on device tree node
+ *
+ * @v region Memory region of interest to be updated
+ * @v fdt Device tree
+ * @v offset Starting node offset
+ * @v match Required device type (or NULL)
+ * @v flags Region flags
+ * @ret rc Return status code
+ */
+static int fdtmem_update_node ( struct memmap_region *region, struct fdt *fdt,
+ unsigned int offset, const char *match,
+ unsigned int flags ) {
+ struct fdt_descriptor desc;
+ struct fdt_reg_cells regs;
+ const char *devtype;
+ uint64_t start;
+ uint64_t size;
+ int depth;
+ int count;
+ int index;
+ int rc;
+
+ /* Parse region cell sizes */
+ fdt_reg_cells ( fdt, offset, &regs );
+
+ /* Scan through reservations */
+ for ( depth = -1 ; ; depth += desc.depth, offset = desc.next ) {
+
+ /* Describe token */
+ if ( ( rc = fdt_describe ( fdt, offset, &desc ) ) != 0 ) {
+ DBGC ( region, "FDTMEM has malformed node: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Terminate when we exit this node */
+ if ( ( depth == 0 ) && ( desc.depth < 0 ) )
+ break;
+
+ /* Ignore any non-immediate child nodes */
+ if ( ! ( ( depth == 0 ) && desc.name && ( ! desc.data ) ) )
+ continue;
+
+ /* Ignore any non-matching children */
+ if ( match ) {
+ devtype = fdt_string ( fdt, desc.offset,
+ "device_type" );
+ if ( ! devtype )
+ continue;
+ if ( strcmp ( devtype, match ) != 0 )
+ continue;
+ }
+
+ /* Count regions */
+ count = fdt_reg_count ( fdt, desc.offset, &regs );
+ if ( count < 0 ) {
+ if ( flags & MEMMAP_FL_RESERVED ) {
+ /* Assume this is a non-fixed reservation */
+ continue;
+ }
+ rc = count;
+ DBGC ( region, "FDTMEM has malformed region %s: %s\n",
+ desc.name, strerror ( rc ) );
+ continue;
+ }
+
+ /* Scan through this region */
+ for ( index = 0 ; index < count ; index++ ) {
+
+ /* Get region starting address and size */
+ if ( ( rc = fdt_reg_address ( fdt, desc.offset, &regs,
+ index, &start ) ) != 0 ){
+ 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, &regs,
+ index, &size ) ) != 0 ) {
+ DBGC ( region, "FDTMEM %s region %d has "
+ "malformed size: %s\n",
+ desc.name, index, strerror ( rc ) );
+ break;
+ }
+
+ /* Update memory region descriptor */
+ memmap_update ( region, start, size, flags,
+ desc.name );
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Update memory region descriptor based on device tree
+ *
+ * @v region Memory region of interest to be updated
+ * @v fdt Device tree
+ * @ret rc Return status code
+ */
+static int fdtmem_update_tree ( struct memmap_region *region,
+ struct fdt *fdt ) {
+ const struct fdt_reservation *rsv;
+ unsigned int offset;
+ int rc;
+
+ /* Update based on memory regions in the root node */
+ if ( ( rc = fdtmem_update_node ( region, fdt, 0, "memory",
+ MEMMAP_FL_MEMORY ) ) != 0 )
+ return rc;
+
+ /* Update based on memory reservations block */
+ for_each_fdt_reservation ( rsv, fdt ) {
+ 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 ( 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,
+ MEMMAP_FL_RESERVED ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Describe memory region
+ *
+ * @v min Minimum address
+ * @v max Maximum accessible physical address
+ * @v fdt Device tree
+ * @v region Region descriptor to fill in
+ */
+static void fdtmem_describe ( uint64_t min, uint64_t max, struct fdt *fdt,
+ struct memmap_region *region ) {
+ uint64_t inaccessible;
+
+ /* Initialise region */
+ memmap_init ( min, region );
+
+ /* Update region based on device tree */
+ fdtmem_update_tree ( region, fdt );
+
+ /* Treat inaccessible physical memory as such */
+ inaccessible = ( max + 1 );
+ 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 max Maximum accessible physical address
+ * @ret new New physical address for relocation
+ *
+ * Find a suitably aligned address towards the top of existent 32-bit
+ * memory to which iPXE may be relocated, along with a copy of the
+ * system device tree.
+ *
+ * This function may be called very early in initialisation, before
+ * .data is writable or .bss has been zeroed. Neither this function
+ * 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, physaddr_t max ) {
+ struct fdt fdt;
+ struct memmap_region region;
+ physaddr_t addr;
+ physaddr_t next;
+ physaddr_t old;
+ physaddr_t new;
+ physaddr_t try;
+ size_t len;
+ int rc;
+
+ /* Sanity check */
+ assert ( ( max_align & ( max_align - 1 ) ) == 0 );
+
+ /* Get current physical address */
+ old = virt_to_phys ( _prefix );
+
+ /* Parse FDT */
+ if ( ( rc = fdt_parse ( &fdt, hdr, -1UL ) ) != 0 ) {
+ DBGC ( hdr, "FDTMEM could not parse FDT: %s\n",
+ strerror ( rc ) );
+ /* Refuse relocation if we have no FDT */
+ return old;
+ }
+
+ /* Determine required length */
+ len = fdtmem_len ( &fdt );
+ assert ( len > 0 );
+ DBGC ( hdr, "FDTMEM requires %#zx + %#zx => %#zx bytes for "
+ "relocation\n", memsz, fdt.len, len );
+
+ /* Limit relocation to 32-bit address space
+ *
+ * Devices with only 32-bit DMA addressing are relatively
+ * common even on systems with 64-bit CPUs. Limit relocation
+ * of iPXE to 32-bit address space so that I/O buffers and
+ * other DMA allocations will be accessible by 32-bit devices.
+ */
+ if ( max > FDTMEM_MAX32 )
+ max = FDTMEM_MAX32;
+
+ /* Construct memory map and choose a relocation address */
+ new = old;
+ for ( addr = 0, next = 1 ; next ; addr = next ) {
+
+ /* Describe region and in-use memory */
+ fdtmem_describe ( addr, max, &fdt, &region );
+ memmap_update ( &region, old, memsz, MEMMAP_FL_USED, "iPXE" );
+ memmap_update ( &region, virt_to_phys ( hdr ), fdt.len,
+ MEMMAP_FL_RESERVED, "FDT" );
+ next = ( region.max + 1 );
+
+ /* Dump region descriptor (for debugging) */
+ DBGC_MEMMAP ( hdr, &region );
+ assert ( region.max >= region.min );
+
+ /* Use highest possible region */
+ if ( memmap_is_usable ( &region ) &&
+ ( ( next == 0 ) || ( next >= len ) ) ) {
+
+ /* Determine candidate address after alignment */
+ try = ( ( next - len ) & ~( max_align - 1 ) );
+
+ /* Use this address if within region */
+ if ( try >= addr )
+ new = try;
+ }
+ }
+
+ DBGC ( hdr, "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 max Maximum accessible physical address
+ * @ret rc Return status code
+ */
+int fdtmem_register ( struct fdt_header *hdr, physaddr_t max ) {
+ struct fdt_header *copy;
+ struct fdt fdt;
+ int rc;
+
+ /* Record maximum accessible physical address */
+ fdtmem_max = max;
+
+ /* Parse FDT to obtain length */
+ if ( ( rc = fdt_parse ( &fdt, hdr, -1UL ) ) != 0 ) {
+ DBGC ( hdr, "FDTMEM could not parse FDT: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy device tree to end of iPXE image */
+ 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 ( 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 min Minimum address
+ * @v hide Hide in-use regions from the memory map
+ * @v region Region descriptor to fill in
+ */
+static void fdtmem_describe_region ( uint64_t min, int hide,
+ struct memmap_region *region ) {
+
+ /* Describe memory region based on device tree */
+ fdtmem_describe ( min, fdtmem_max, &sysfdt, 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/core/fnrec.c b/src/core/fnrec.c
index 0430817f8..b63ffc1f5 100644
--- a/src/core/fnrec.c
+++ b/src/core/fnrec.c
@@ -177,6 +177,7 @@ static void fnrec_init ( void ) {
}
struct init_fn fnrec_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "fnrec",
.initialise = fnrec_init,
};
diff --git a/src/core/gdbserial.c b/src/core/gdbserial.c
index 0983f2557..61add5d47 100644
--- a/src/core/gdbserial.c
+++ b/src/core/gdbserial.c
@@ -25,20 +25,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdio.h>
-#include <stdlib.h>
#include <assert.h>
#include <ipxe/uart.h>
#include <ipxe/gdbstub.h>
#include <ipxe/gdbserial.h>
#include <config/serial.h>
-/* UART port number */
-#ifdef COMCONSOLE
-#define GDBSERIAL_PORT COMCONSOLE
-#else
-#define GDBSERIAL_PORT 0
-#endif
-
/* UART baud rate */
#ifdef COMPRESERVE
#define GDBSERIAL_BAUD 0
@@ -46,51 +38,37 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define GDBSERIAL_BAUD COMSPEED
#endif
-/* UART line control register value */
-#ifdef COMPRESERVE
-#define GDBSERIAL_LCR 0
-#else
-#define GDBSERIAL_LCR UART_LCR_WPS ( COMDATA, COMPARITY, COMSTOP )
-#endif
-
/** GDB serial UART */
-static struct uart gdbserial_uart;
+static struct uart *gdbserial_uart;
struct gdb_transport serial_gdb_transport __gdb_transport;
static size_t gdbserial_recv ( char *buf, size_t len ) {
assert ( len > 0 );
- while ( ! uart_data_ready ( &gdbserial_uart ) ) {}
- buf[0] = uart_receive ( &gdbserial_uart );
+ while ( ! uart_data_ready ( gdbserial_uart ) ) {}
+ buf[0] = uart_receive ( gdbserial_uart );
return 1;
}
static void gdbserial_send ( const char *buf, size_t len ) {
while ( len-- > 0 ) {
- uart_transmit ( &gdbserial_uart, *buf++ );
+ uart_transmit ( gdbserial_uart, *buf++ );
}
}
static int gdbserial_init ( int argc, char **argv ) {
- unsigned int port;
- char *endp;
-
- if ( argc == 0 ) {
- port = GDBSERIAL_PORT;
- } else if ( argc == 1 ) {
- port = strtoul ( argv[0], &endp, 10 );
- if ( *endp ) {
- printf ( "serial: invalid port\n" );
- return 1;
- }
+ const char *port;
+
+ if ( argc == 1 ) {
+ port = argv[0];
} else {
printf ( "serial: syntax <port>\n" );
return 1;
}
- if ( ! gdbserial_configure ( port, GDBSERIAL_BAUD, GDBSERIAL_LCR ) ) {
+ if ( ! gdbserial_configure ( port, GDBSERIAL_BAUD ) ) {
printf ( "serial: unable to configure\n" );
return 1;
}
@@ -105,14 +83,21 @@ struct gdb_transport serial_gdb_transport __gdb_transport = {
.send = gdbserial_send,
};
-struct gdb_transport * gdbserial_configure ( unsigned int port,
- unsigned int baud, uint8_t lcr ) {
+struct gdb_transport * gdbserial_configure ( const char *name,
+ unsigned int baud ) {
int rc;
- if ( ( rc = uart_select ( &gdbserial_uart, port ) ) != 0 )
+ uart_put ( gdbserial_uart );
+ gdbserial_uart = NULL;
+
+ gdbserial_uart = uart_find ( name );
+ if ( ! gdbserial_uart )
return NULL;
+ uart_get ( gdbserial_uart );
+
+ gdbserial_uart->baud = baud;
- if ( ( rc = uart_init ( &gdbserial_uart, baud, lcr ) ) != 0 )
+ if ( ( rc = uart_init ( gdbserial_uart ) ) != 0 )
return NULL;
return &serial_gdb_transport;
diff --git a/src/core/gdbstub.c b/src/core/gdbstub.c
index 8b57ddf56..d668f454a 100644
--- a/src/core/gdbstub.c
+++ b/src/core/gdbstub.c
@@ -36,7 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ctype.h>
#include <byteswap.h>
#include <ipxe/gdbstub.h>
-#include "gdbmach.h"
enum {
POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */
diff --git a/src/core/getkey.c b/src/core/getkey.c
index 0c280d23b..c952e0aea 100644
--- a/src/core/getkey.c
+++ b/src/core/getkey.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ctype.h>
#include <ipxe/console.h>
diff --git a/src/core/getopt.c b/src/core/getopt.c
index e6c3948d1..cb4cbf118 100644
--- a/src/core/getopt.c
+++ b/src/core/getopt.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
diff --git a/src/core/gpio.c b/src/core/gpio.c
new file mode 100644
index 000000000..f57139a11
--- /dev/null
+++ b/src/core/gpio.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ipxe/gpio.h>
+
+/** @file
+ *
+ * General purpose I/O
+ *
+ */
+
+/** List of GPIO controllers */
+static LIST_HEAD ( all_gpios );
+
+/**
+ * Allocate GPIO controller
+ *
+ * @v count Number of GPIO pins
+ * @v priv_len Size of driver-private data
+ * @ret gpios GPIO controller, or NULL
+ */
+struct gpios * alloc_gpios ( unsigned int count, size_t priv_len ) {
+ struct gpios *gpios;
+ struct gpio *gpio;
+ size_t len;
+ unsigned int i;
+
+ /* Allocate and initialise structure */
+ len = ( sizeof ( *gpios ) + ( count * sizeof ( *gpio ) ) + priv_len );
+ gpios = zalloc ( len );
+ if ( ! gpios )
+ return NULL;
+ gpios->count = count;
+ gpios->gpio = ( ( ( void * ) gpios ) + sizeof ( *gpios ) );
+ gpios->priv = ( ( ( void * ) gpios ) + sizeof ( *gpios ) +
+ ( count * sizeof ( *gpio ) ) );
+
+ /* Initialise GPIO pins */
+ for ( i = 0 ; i < count ; i++ ) {
+ gpio = &gpios->gpio[i];
+ gpio->gpios = gpios;
+ gpio->index = i;
+ }
+
+ return gpios;
+}
+
+/**
+ * Register GPIO controller
+ *
+ * @v gpios GPIO controller
+ * @ret rc Return status code
+ */
+int gpios_register ( struct gpios *gpios ) {
+
+ /* Add to list of GPIO controllers */
+ gpios_get ( gpios );
+ list_add_tail ( &gpios->list, &all_gpios );
+ DBGC ( gpios, "GPIO %s registered with %d GPIOs\n",
+ gpios->dev->name, gpios->count );
+
+ return 0;
+}
+
+/**
+ * Unregister GPIO controller
+ *
+ * @v gpios GPIO controller
+ */
+void gpios_unregister ( struct gpios *gpios ) {
+
+ /* Remove from list of GPIO controllers */
+ DBGC ( gpios, "GPIO %s unregistered\n", gpios->dev->name );
+ list_del ( &gpios->list );
+ gpios_put ( gpios );
+}
+
+/**
+ * Find GPIO controller
+ *
+ * @v bus_type Bus type
+ * @v location Bus location
+ * @ret gpios GPIO controller, or NULL
+ */
+struct gpios * gpios_find ( unsigned int bus_type, unsigned int location ) {
+ struct gpios *gpios;
+
+ /* Scan through list of registered GPIO controllers */
+ list_for_each_entry ( gpios, &all_gpios, list ) {
+ if ( ( gpios->dev->desc.bus_type == bus_type ) &&
+ ( gpios->dev->desc.location == location ) )
+ return gpios;
+ }
+
+ return NULL;
+}
+
+/**
+ * Get null GPIO input value
+ *
+ * @v gpios GPIO controller
+ * @v gpio GPIO pin
+ * @ret active Pin is in the active state
+ */
+static int null_gpio_in ( struct gpios *gpios __unused,
+ struct gpio *gpio __unused ) {
+ return 0;
+}
+
+/**
+ * Set null GPIO output value
+ *
+ * @v gpios GPIO controller
+ * @v gpio GPIO pin
+ * @v active Set pin to active state
+ */
+static void null_gpio_out ( struct gpios *gpios __unused,
+ struct gpio *gpio __unused, int active __unused ) {
+ /* Nothing to do */
+}
+
+/**
+ * Configure null GPIO pin
+ *
+ * @v gpios GPIO controller
+ * @v gpio GPIO pin
+ * @v config Configuration
+ * @ret rc Return status code
+ */
+static int null_gpio_config ( struct gpios *gpios __unused,
+ struct gpio *gpio __unused,
+ unsigned int config __unused ) {
+ return -ENODEV;
+}
+
+/** Null GPIO operations */
+struct gpio_operations null_gpio_operations = {
+ .in = null_gpio_in,
+ .out = null_gpio_out,
+ .config = null_gpio_config,
+};
diff --git a/src/core/image.c b/src/core/image.c
index bf0e4f756..7df125971 100644
--- a/src/core/image.c
+++ b/src/core/image.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <string.h>
@@ -33,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <libgen.h>
#include <syslog.h>
#include <ipxe/list.h>
+#include <ipxe/uaccess.h>
#include <ipxe/umalloc.h>
#include <ipxe/uri.h>
#include <ipxe/image.h>
@@ -76,22 +78,41 @@ static int require_trusted_images_permanent = 0;
* Free executable image
*
* @v refcnt Reference counter
+ *
+ * Image consumers must call image_put() rather than calling
+ * free_image() directly. This function is exposed for use only by
+ * static images.
*/
-static void free_image ( struct refcnt *refcnt ) {
+void free_image ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt );
struct image_tag *tag;
+ /* Sanity check: free_image() should not be called directly on
+ * dynamically allocated images.
+ */
+ assert ( refcnt->count < 0 );
DBGC ( image, "IMAGE %s freed\n", image->name );
+
+ /* Clear any tag weak references */
for_each_table_entry ( tag, IMAGE_TAGS ) {
if ( tag->image == image )
tag->image = NULL;
}
- free ( image->name );
+
+ /* Free dynamic allocations used by both static and dynamic images */
free ( image->cmdline );
uri_put ( image->uri );
- ufree ( image->data );
image_put ( image->replacement );
- free ( image );
+
+ /* Free image name, if dynamically allocated */
+ if ( ! ( image->flags & IMAGE_STATIC_NAME ) )
+ free ( image->name );
+
+ /* Free image data and image itself, if dynamically allocated */
+ if ( ! ( image->flags & IMAGE_STATIC ) ) {
+ ufree ( image->rwdata );
+ free ( image );
+ }
}
/**
@@ -165,14 +186,37 @@ int image_set_name ( struct image *image, const char *name ) {
if ( ! name_copy )
return -ENOMEM;
+ /* Free existing name, if not statically allocated */
+ if ( ! ( image->flags & IMAGE_STATIC_NAME ) )
+ free ( image->name );
+
/* Replace existing name */
- free ( image->name );
image->name = name_copy;
+ image->flags &= ~IMAGE_STATIC_NAME;
return 0;
}
/**
+ * Strip dot suffix from image name, if present
+ *
+ * @v image Image
+ * @ret sep Position of old dot separator, or NULL
+ */
+char * image_strip_suffix ( struct image *image ) {
+ char *dot;
+
+ /* Locate and strip suffix, if present */
+ if ( image->name &&
+ ( ( dot = strrchr ( image->name, '.' ) ) != NULL ) ) {
+ *dot = '\0';
+ return dot;
+ }
+
+ return NULL;
+}
+
+/**
* Set image command line
*
* @v image Image
@@ -199,13 +243,17 @@ int image_set_cmdline ( struct image *image, const char *cmdline ) {
* @ret rc Return status code
*/
int image_set_len ( struct image *image, size_t len ) {
- userptr_t new;
+ void *new;
+
+ /* Refuse to reallocate static images */
+ if ( image->flags & IMAGE_STATIC )
+ return -ENOTTY;
/* (Re)allocate image data */
- new = urealloc ( image->data, len );
+ new = urealloc ( image->rwdata, len );
if ( ! new )
return -ENOMEM;
- image->data = new;
+ image->rwdata = new;
image->len = len;
return 0;
@@ -219,7 +267,7 @@ int image_set_len ( struct image *image, size_t len ) {
* @v len Length of image data
* @ret rc Return status code
*/
-int image_set_data ( struct image *image, userptr_t data, size_t len ) {
+int image_set_data ( struct image *image, const void *data, size_t len ) {
int rc;
/* Set image length */
@@ -227,7 +275,7 @@ int image_set_data ( struct image *image, userptr_t data, size_t len ) {
return rc;
/* Copy in new image data */
- memcpy_user ( image->data, 0, data, 0, len );
+ memcpy ( image->rwdata, data, len );
return 0;
}
@@ -269,6 +317,13 @@ int register_image ( struct image *image ) {
char name[8]; /* "imgXXXX" */
int rc;
+ /* Sanity checks */
+ if ( image->flags & IMAGE_STATIC ) {
+ assert ( ( image->name == NULL ) ||
+ ( image->flags & IMAGE_STATIC_NAME ) );
+ assert ( image->cmdline == NULL );
+ }
+
/* Create image name if it doesn't already have one */
if ( ! image->name ) {
snprintf ( name, sizeof ( name ), "img%d", imgindex++ );
@@ -281,8 +336,8 @@ int register_image ( struct image *image ) {
image->flags |= IMAGE_REGISTERED;
list_add_tail ( &image->list, &images );
DBGC ( image, "IMAGE %s at [%lx,%lx) registered\n",
- image->name, user_to_phys ( image->data, 0 ),
- user_to_phys ( image->data, image->len ) );
+ image->name, virt_to_phys ( image->data ),
+ ( virt_to_phys ( image->data ) + image->len ) );
/* Try to detect image type, if applicable. Ignore failures,
* since we expect to handle some unrecognised images
@@ -428,6 +483,10 @@ int image_exec ( struct image *image ) {
if ( replacement )
assert ( replacement->flags & IMAGE_REGISTERED );
+ /* Clear any recorded replacement image */
+ image_put ( image->replacement );
+ image->replacement = NULL;
+
err:
/* Unregister image if applicable */
if ( image->flags & IMAGE_AUTO_UNREGISTER )
@@ -547,7 +606,8 @@ int image_set_trust ( int require_trusted, int permanent ) {
* @v len Length
* @ret image Image, or NULL on error
*/
-struct image * image_memory ( const char *name, userptr_t data, size_t len ) {
+struct image * image_memory ( const char *name, const void *data,
+ size_t len ) {
struct image *image;
int rc;
diff --git a/src/core/init.c b/src/core/init.c
index c13fd1667..2a32f5795 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/device.h>
#include <ipxe/console.h>
@@ -53,8 +54,10 @@ void initialise ( void ) {
struct init_fn *init_fn;
/* Call registered initialisation functions */
- for_each_table_entry ( init_fn, INIT_FNS )
+ for_each_table_entry ( init_fn, INIT_FNS ) {
+ DBGC ( colour, "INIT initialising %s...\n", init_fn->name );
init_fn->initialise ();
+ }
}
/**
diff --git a/src/core/interface.c b/src/core/interface.c
index ea0606893..0ebcc8e51 100644
--- a/src/core/interface.c
+++ b/src/core/interface.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <ipxe/interface.h>
diff --git a/src/core/iobuf.c b/src/core/iobuf.c
index c9970bc76..7e9a4156d 100644
--- a/src/core/iobuf.c
+++ b/src/core/iobuf.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <strings.h>
@@ -47,22 +48,26 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) {
struct io_buffer *iobuf;
+ size_t headroom;
+ size_t tailroom;
size_t padding;
size_t threshold;
unsigned int align_log2;
void *data;
- /* Calculate padding required below alignment boundary to
- * ensure that a correctly aligned inline struct io_buffer
- * could fit (regardless of the requested offset).
+ /* Round up requested alignment and calculate initial headroom
+ * and tailroom to ensure that no cachelines are shared
+ * between I/O buffer data and other data structures.
*/
- padding = ( sizeof ( *iobuf ) + __alignof__ ( *iobuf ) - 1 );
-
- /* Round up requested alignment to at least the size of the
- * padding, to simplify subsequent calculations.
- */
- if ( align < padding )
- align = padding;
+ if ( align < IOB_ZLEN )
+ align = IOB_ZLEN;
+ headroom = ( offset & ( IOB_ZLEN - 1 ) );
+ tailroom = ( ( - len - offset ) & ( IOB_ZLEN - 1 ) );
+ padding = ( headroom + tailroom );
+ offset -= headroom;
+ len += padding;
+ if ( len < padding )
+ return NULL;
/* Round up alignment to the nearest power of two, avoiding
* a potentially undefined shift operation.
@@ -73,8 +78,8 @@ struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) {
align = ( 1UL << align_log2 );
/* Calculate length threshold */
- assert ( align >= padding );
- threshold = ( align - padding );
+ assert ( align >= sizeof ( *iobuf ) );
+ threshold = ( align - sizeof ( *iobuf ) );
/* Allocate buffer plus an inline descriptor as a single unit,
* unless doing so would push the total size over the
@@ -82,11 +87,6 @@ struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) {
*/
if ( len <= threshold ) {
- /* Round up buffer length to ensure that struct
- * io_buffer is aligned.
- */
- len += ( ( - len - offset ) & ( __alignof__ ( *iobuf ) - 1 ) );
-
/* Allocate memory for buffer plus descriptor */
data = malloc_phys_offset ( len + sizeof ( *iobuf ), align,
offset );
@@ -111,7 +111,8 @@ struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) {
/* Populate descriptor */
memset ( &iobuf->map, 0, sizeof ( iobuf->map ) );
- iobuf->head = iobuf->data = iobuf->tail = data;
+ iobuf->head = data;
+ iobuf->data = iobuf->tail = ( data + headroom );
iobuf->end = ( data + len );
return iobuf;
@@ -124,18 +125,24 @@ struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) {
* @ret iobuf I/O buffer, or NULL if none available
*
* The I/O buffer will be physically aligned on its own size (rounded
- * up to the nearest power of two).
+ * up to the nearest power of two), up to a maximum of page-size
+ * alignment.
*/
struct io_buffer * alloc_iob ( size_t len ) {
+ size_t align;
/* Pad to minimum length */
if ( len < IOB_ZLEN )
len = IOB_ZLEN;
- /* Align buffer on its own size to avoid potential problems
- * with boundary-crossing DMA.
+ /* Align buffer on its own size (up to page size) to avoid
+ * potential problems with boundary-crossing DMA.
*/
- return alloc_iob_raw ( len, len, 0 );
+ align = len;
+ if ( align > PAGE_SIZE )
+ align = PAGE_SIZE;
+
+ return alloc_iob_raw ( len, align, 0 );
}
/**
@@ -260,7 +267,7 @@ struct io_buffer * iob_concatenate ( struct list_head *list ) {
len += iob_len ( iobuf );
/* Allocate new I/O buffer */
- concatenated = alloc_iob_raw ( len, __alignof__ ( *iobuf ), 0 );
+ concatenated = alloc_iob_raw ( len, IOB_ZLEN, 0 );
if ( ! concatenated )
return NULL;
diff --git a/src/core/isqrt.c b/src/core/isqrt.c
index c4d0571e7..b553c0935 100644
--- a/src/core/isqrt.c
+++ b/src/core/isqrt.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/job.c b/src/core/job.c
index 65df80056..f83ce0552 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
diff --git a/src/core/keymap.c b/src/core/keymap.c
index 36db7bd4c..e2244fdcb 100644
--- a/src/core/keymap.c
+++ b/src/core/keymap.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <ctype.h>
diff --git a/src/core/linebuf.c b/src/core/linebuf.c
index c197e383c..8995dca66 100644
--- a/src/core/linebuf.c
+++ b/src/core/linebuf.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/core/lineconsole.c b/src/core/lineconsole.c
index 0a72d1434..25eae39dd 100644
--- a/src/core/lineconsole.c
+++ b/src/core/lineconsole.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/list.c b/src/core/list.c
index 5175c84ec..8d38d690a 100644
--- a/src/core/list.c
+++ b/src/core/list.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/main.c b/src/core/main.c
index 3db836491..95e16132f 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -13,6 +13,7 @@ Literature dealing with the network protocols:
**************************************************************************/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdio.h>
diff --git a/src/core/malloc.c b/src/core/malloc.c
index 8499ab45a..3a9f23ee4 100644
--- a/src/core/malloc.c
+++ b/src/core/malloc.c
@@ -22,11 +22,11 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdint.h>
#include <string.h>
-#include <strings.h>
#include <ipxe/io.h>
#include <ipxe/list.h>
#include <ipxe/init.h>
@@ -59,8 +59,12 @@ struct memory_block {
struct list_head list;
};
-#define MIN_MEMBLOCK_SIZE \
- ( ( size_t ) ( 1 << ( fls ( sizeof ( struct memory_block ) - 1 ) ) ) )
+/** Physical address alignment maintained for free blocks of memory
+ *
+ * We keep memory blocks aligned on a power of two that is at least
+ * large enough to hold a @c struct @c memory_block.
+ */
+#define MIN_MEMBLOCK_ALIGN ( 4 * sizeof ( void * ) )
/** A block of allocated memory complete with size information */
struct autosized_block {
@@ -71,49 +75,24 @@ struct autosized_block {
};
/**
- * Address for zero-length memory blocks
- *
- * @c malloc(0) or @c realloc(ptr,0) will return the special value @c
- * NOWHERE. Calling @c free(NOWHERE) will have no effect.
+ * Heap area size
*
- * This is consistent with the ANSI C standards, which state that
- * "either NULL or a pointer suitable to be passed to free()" must be
- * returned in these cases. Using a special non-NULL value means that
- * the caller can take a NULL return value to indicate failure,
- * without first having to check for a requested size of zero.
- *
- * Code outside of malloc.c do not ever need to refer to the actual
- * value of @c NOWHERE; this is an internal definition.
+ * Currently fixed at 4MB.
*/
-#define NOWHERE ( ( void * ) ~( ( intptr_t ) 0 ) )
-
-/** List of free memory blocks */
-static LIST_HEAD ( free_blocks );
+#define HEAP_SIZE ( 4096 * 1024 )
-/** Total amount of free memory */
-size_t freemem;
+/** Heap area alignment */
+#define HEAP_ALIGN MIN_MEMBLOCK_ALIGN
-/** Total amount of used memory */
-size_t usedmem;
-
-/** Maximum amount of used memory */
-size_t maxusedmem;
-
-/**
- * Heap size
- *
- * Currently fixed at 512kB.
- */
-#define HEAP_SIZE ( 512 * 1024 )
-
-/** The heap itself */
-static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) )));
+/** The heap area */
+static char __attribute__ (( aligned ( HEAP_ALIGN ) )) heap_area[HEAP_SIZE];
/**
* Mark all blocks in free list as defined
*
+ * @v heap Heap
*/
-static inline void valgrind_make_blocks_defined ( void ) {
+static inline void valgrind_make_blocks_defined ( struct heap *heap ) {
struct memory_block *block;
/* Do nothing unless running under Valgrind */
@@ -126,18 +105,18 @@ static inline void valgrind_make_blocks_defined ( void ) {
*/
/* Mark block list itself as defined */
- VALGRIND_MAKE_MEM_DEFINED ( &free_blocks, sizeof ( free_blocks ) );
+ VALGRIND_MAKE_MEM_DEFINED ( &heap->blocks, sizeof ( heap->blocks ) );
/* Mark areas accessed by list_check() as defined */
- VALGRIND_MAKE_MEM_DEFINED ( &free_blocks.prev->next,
- sizeof ( free_blocks.prev->next ) );
- VALGRIND_MAKE_MEM_DEFINED ( free_blocks.next,
- sizeof ( *free_blocks.next ) );
- VALGRIND_MAKE_MEM_DEFINED ( &free_blocks.next->next->prev,
- sizeof ( free_blocks.next->next->prev ) );
+ VALGRIND_MAKE_MEM_DEFINED ( &heap->blocks.prev->next,
+ sizeof ( heap->blocks.prev->next ) );
+ VALGRIND_MAKE_MEM_DEFINED ( heap->blocks.next,
+ sizeof ( *heap->blocks.next ) );
+ VALGRIND_MAKE_MEM_DEFINED ( &heap->blocks.next->next->prev,
+ sizeof ( heap->blocks.next->next->prev ) );
/* Mark each block in list as defined */
- list_for_each_entry ( block, &free_blocks, list ) {
+ list_for_each_entry ( block, &heap->blocks, list ) {
/* Mark block as defined */
VALGRIND_MAKE_MEM_DEFINED ( block, sizeof ( *block ) );
@@ -153,8 +132,9 @@ static inline void valgrind_make_blocks_defined ( void ) {
/**
* Mark all blocks in free list as inaccessible
*
+ * @v heap Heap
*/
-static inline void valgrind_make_blocks_noaccess ( void ) {
+static inline void valgrind_make_blocks_noaccess ( struct heap *heap ) {
struct memory_block *block;
struct memory_block *prev = NULL;
@@ -168,7 +148,7 @@ static inline void valgrind_make_blocks_noaccess ( void ) {
*/
/* Mark each block in list as inaccessible */
- list_for_each_entry ( block, &free_blocks, list ) {
+ list_for_each_entry ( block, &heap->blocks, list ) {
/* Mark previous block (if any) as inaccessible. (Current
* block will be accessed by list_check().)
@@ -181,8 +161,8 @@ static inline void valgrind_make_blocks_noaccess ( void ) {
* accessing the first list item. Temporarily mark
* this area as defined.
*/
- VALGRIND_MAKE_MEM_DEFINED ( &free_blocks.next->prev,
- sizeof ( free_blocks.next->prev ) );
+ VALGRIND_MAKE_MEM_DEFINED ( &heap->blocks.next->prev,
+ sizeof ( heap->blocks.next->prev ));
}
/* Mark last block (if any) as inaccessible */
if ( prev )
@@ -191,32 +171,37 @@ static inline void valgrind_make_blocks_noaccess ( void ) {
/* Mark as inaccessible the area that was temporarily marked
* as defined to avoid errors from list_check().
*/
- VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks.next->prev,
- sizeof ( free_blocks.next->prev ) );
+ VALGRIND_MAKE_MEM_NOACCESS ( &heap->blocks.next->prev,
+ sizeof ( heap->blocks.next->prev ) );
/* Mark block list itself as inaccessible */
- VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks, sizeof ( free_blocks ) );
+ VALGRIND_MAKE_MEM_NOACCESS ( &heap->blocks, sizeof ( heap->blocks ) );
}
/**
* Check integrity of the blocks in the free list
*
+ * @v heap Heap
*/
-static inline void check_blocks ( void ) {
+static inline void check_blocks ( struct heap *heap ) {
struct memory_block *block;
struct memory_block *prev = NULL;
if ( ! ASSERTING )
return;
- list_for_each_entry ( block, &free_blocks, list ) {
+ list_for_each_entry ( block, &heap->blocks, list ) {
+
+ /* Check alignment */
+ assert ( ( virt_to_phys ( block ) &
+ ( heap->align - 1 ) ) == 0 );
/* Check that list structure is intact */
list_check ( &block->list );
/* Check that block size is not too small */
assert ( block->size >= sizeof ( *block ) );
- assert ( block->size >= MIN_MEMBLOCK_SIZE );
+ assert ( block->size >= heap->align );
/* Check that block does not wrap beyond end of address space */
assert ( ( ( void * ) block + block->size ) >
@@ -237,9 +222,10 @@ static inline void check_blocks ( void ) {
/**
* Discard some cached data
*
+ * @v size Failed allocation size
* @ret discarded Number of cached items discarded
*/
-static unsigned int discard_cache ( void ) {
+static unsigned int discard_cache ( size_t size __unused ) {
struct cache_discarder *discarder;
unsigned int discarded;
@@ -259,13 +245,14 @@ static void discard_all_cache ( void ) {
unsigned int discarded;
do {
- discarded = discard_cache();
+ discarded = discard_cache ( 0 );
} while ( discarded );
}
/**
* Allocate a memory block
*
+ * @v heap Heap
* @v size Requested size
* @v align Physical alignment
* @v offset Offset from physical alignment
@@ -276,28 +263,35 @@ static void discard_all_cache ( void ) {
*
* @c align must be a power of two. @c size may not be zero.
*/
-void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
+static void * heap_alloc_block ( struct heap *heap, size_t size, size_t align,
+ size_t offset ) {
struct memory_block *block;
+ size_t actual_offset;
size_t align_mask;
size_t actual_size;
size_t pre_size;
size_t post_size;
struct memory_block *pre;
struct memory_block *post;
- unsigned int discarded;
+ unsigned int grown;
void *ptr;
/* Sanity checks */
assert ( size != 0 );
assert ( ( align == 0 ) || ( ( align & ( align - 1 ) ) == 0 ) );
- valgrind_make_blocks_defined();
- check_blocks();
+ valgrind_make_blocks_defined ( heap );
+ check_blocks ( heap );
- /* Round up size to multiple of MIN_MEMBLOCK_SIZE and
- * calculate alignment mask.
- */
- actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) &
- ~( MIN_MEMBLOCK_SIZE - 1 ) );
+ /* Limit offset to requested alignment */
+ offset &= ( align ? ( align - 1 ) : 0 );
+
+ /* Calculate offset of memory block */
+ actual_offset = ( offset & ~( heap->align - 1 ) );
+ assert ( actual_offset <= offset );
+
+ /* Calculate size of memory block */
+ actual_size = ( ( size + offset - actual_offset + heap->align - 1 )
+ & ~( heap->align - 1 ) );
if ( ! actual_size ) {
/* The requested size is not permitted to be zero. A
* zero result at this point indicates that either the
@@ -308,14 +302,16 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
goto done;
}
assert ( actual_size >= size );
- align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) );
- DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n",
+ /* Calculate alignment mask */
+ align_mask = ( ( align - 1 ) | ( heap->align - 1 ) );
+
+ DBGC2 ( heap, "HEAP allocating %#zx (aligned %#zx+%#zx)\n",
size, align, offset );
while ( 1 ) {
/* Search through blocks for the first one with enough space */
- list_for_each_entry ( block, &free_blocks, list ) {
- pre_size = ( ( offset - virt_to_phys ( block ) )
+ list_for_each_entry ( block, &heap->blocks, list ) {
+ pre_size = ( ( actual_offset - virt_to_phys ( block ) )
& align_mask );
if ( ( block->size < pre_size ) ||
( ( block->size - pre_size ) < actual_size ) )
@@ -329,15 +325,17 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
pre = block;
block = ( ( ( void * ) pre ) + pre_size );
post = ( ( ( void * ) block ) + actual_size );
- DBGC2 ( &heap, "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre,
+ DBGC2 ( heap, "HEAP splitting [%p,%p) -> [%p,%p) "
+ "+ [%p,%p)\n", pre,
( ( ( void * ) pre ) + pre->size ), pre, block,
post, ( ( ( void * ) pre ) + pre->size ) );
/* If there is a "post" block, add it in to
- * the free list. Leak it if it is too small
- * (which can happen only at the very end of
- * the heap).
+ * the free list.
*/
- if ( post_size >= MIN_MEMBLOCK_SIZE ) {
+ if ( post_size ) {
+ assert ( post_size >= sizeof ( *block ) );
+ assert ( ( post_size &
+ ( heap->align - 1 ) ) == 0 );
VALGRIND_MAKE_MEM_UNDEFINED ( post,
sizeof ( *post ));
post->size = post_size;
@@ -349,38 +347,42 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
*/
pre->size = pre_size;
/* If there is no "pre" block, remove it from
- * the list. Also remove it (i.e. leak it) if
- * it is too small, which can happen only at
- * the very start of the heap.
+ * the list.
*/
- if ( pre_size < MIN_MEMBLOCK_SIZE ) {
+ if ( ! pre_size ) {
list_del ( &pre->list );
VALGRIND_MAKE_MEM_NOACCESS ( pre,
sizeof ( *pre ) );
+ } else {
+ assert ( pre_size >= sizeof ( *block ) );
+ assert ( ( pre_size &
+ ( heap->align - 1 ) ) == 0 );
}
/* Update memory usage statistics */
- freemem -= actual_size;
- usedmem += actual_size;
- if ( usedmem > maxusedmem )
- maxusedmem = usedmem;
+ heap->freemem -= actual_size;
+ heap->usedmem += actual_size;
+ if ( heap->usedmem > heap->maxusedmem )
+ heap->maxusedmem = heap->usedmem;
/* Return allocated block */
- DBGC2 ( &heap, "Allocated [%p,%p)\n", block,
- ( ( ( void * ) block ) + size ) );
- ptr = block;
+ ptr = ( ( ( void * ) block ) + offset - actual_offset );
+ DBGC2 ( heap, "HEAP allocated [%p,%p) within "
+ "[%p,%p)\n", ptr, ( ptr + size ), block,
+ ( ( ( void * ) block ) + actual_size ) );
VALGRIND_MAKE_MEM_UNDEFINED ( ptr, size );
goto done;
}
- /* Try discarding some cached data to free up memory */
- DBGC ( &heap, "Attempting discard for %#zx (aligned %#zx+%zx), "
- "used %zdkB\n", size, align, offset, ( usedmem >> 10 ) );
- valgrind_make_blocks_noaccess();
- discarded = discard_cache();
- valgrind_make_blocks_defined();
- check_blocks();
- if ( ! discarded ) {
- /* Nothing available to discard */
- DBGC ( &heap, "Failed to allocate %#zx (aligned "
+ /* Attempt to grow heap to satisfy allocation */
+ DBGC ( heap, "HEAP attempting to grow for %#zx (aligned "
+ "%#zx+%zx), used %zdkB\n", size, align, offset,
+ ( heap->usedmem >> 10 ) );
+ valgrind_make_blocks_noaccess ( heap );
+ grown = ( heap->grow ? heap->grow ( actual_size ) : 0 );
+ valgrind_make_blocks_defined ( heap );
+ check_blocks ( heap );
+ if ( ! grown ) {
+ /* Heap did not grow: fail allocation */
+ DBGC ( heap, "HEAP failed to allocate %#zx (aligned "
"%#zx)\n", size, align );
ptr = NULL;
goto done;
@@ -388,23 +390,25 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
}
done:
- check_blocks();
- valgrind_make_blocks_noaccess();
+ check_blocks ( heap );
+ valgrind_make_blocks_noaccess ( heap );
return ptr;
}
/**
* Free a memory block
*
- * @v ptr Memory allocated by alloc_memblock(), or NULL
+ * @v heap Heap
+ * @v ptr Memory allocated by heap_alloc_block(), or NULL
* @v size Size of the memory
*
* If @c ptr is NULL, no action is taken.
*/
-void free_memblock ( void *ptr, size_t size ) {
+static void heap_free_block ( struct heap *heap, void *ptr, size_t size ) {
struct memory_block *freeing;
struct memory_block *block;
struct memory_block *tmp;
+ size_t sub_offset;
size_t actual_size;
ssize_t gap_before;
ssize_t gap_after = -1;
@@ -415,29 +419,31 @@ void free_memblock ( void *ptr, size_t size ) {
VALGRIND_MAKE_MEM_NOACCESS ( ptr, size );
/* Sanity checks */
- valgrind_make_blocks_defined();
- check_blocks();
+ valgrind_make_blocks_defined ( heap );
+ check_blocks ( heap );
- /* Round up size to match actual size that alloc_memblock()
- * would have used.
+ /* Round up to match actual block that heap_alloc_block() would
+ * have allocated.
*/
assert ( size != 0 );
- actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) &
- ~( MIN_MEMBLOCK_SIZE - 1 ) );
- freeing = ptr;
+ sub_offset = ( virt_to_phys ( ptr ) & ( heap->align - 1 ) );
+ freeing = ( ptr - sub_offset );
+ actual_size = ( ( size + sub_offset + heap->align - 1 ) &
+ ~( heap->align - 1 ) );
+ DBGC2 ( heap, "HEAP freeing [%p,%p) within [%p,%p)\n",
+ ptr, ( ptr + size ), freeing,
+ ( ( ( void * ) freeing ) + actual_size ) );
VALGRIND_MAKE_MEM_UNDEFINED ( freeing, sizeof ( *freeing ) );
- DBGC2 ( &heap, "Freeing [%p,%p)\n",
- freeing, ( ( ( void * ) freeing ) + size ) );
/* Check that this block does not overlap the free list */
if ( ASSERTING ) {
- list_for_each_entry ( block, &free_blocks, list ) {
+ list_for_each_entry ( block, &heap->blocks, list ) {
if ( ( ( ( void * ) block ) <
( ( void * ) freeing + actual_size ) ) &&
( ( void * ) freeing <
( ( void * ) block + block->size ) ) ) {
assert ( 0 );
- DBGC ( &heap, "Double free of [%p,%p) "
+ DBGC ( heap, "HEAP double free of [%p,%p) "
"overlapping [%p,%p) detected from %p\n",
freeing,
( ( ( void * ) freeing ) + size ), block,
@@ -449,7 +455,7 @@ void free_memblock ( void *ptr, size_t size ) {
/* Insert/merge into free list */
freeing->size = actual_size;
- list_for_each_entry_safe ( block, tmp, &free_blocks, list ) {
+ list_for_each_entry_safe ( block, tmp, &heap->blocks, list ) {
/* Calculate gaps before and after the "freeing" block */
gap_before = ( ( ( void * ) freeing ) -
( ( ( void * ) block ) + block->size ) );
@@ -457,7 +463,8 @@ void free_memblock ( void *ptr, size_t size ) {
( ( ( void * ) freeing ) + freeing->size ) );
/* Merge with immediately preceding block, if possible */
if ( gap_before == 0 ) {
- DBGC2 ( &heap, "[%p,%p) + [%p,%p) -> [%p,%p)\n", block,
+ DBGC2 ( heap, "HEAP merging [%p,%p) + [%p,%p) -> "
+ "[%p,%p)\n", block,
( ( ( void * ) block ) + block->size ), freeing,
( ( ( void * ) freeing ) + freeing->size ),
block,
@@ -477,13 +484,13 @@ void free_memblock ( void *ptr, size_t size ) {
* possible, merge the following block into the "freeing"
* block.
*/
- DBGC2 ( &heap, "[%p,%p)\n",
+ DBGC2 ( heap, "HEAP freed [%p,%p)\n",
freeing, ( ( ( void * ) freeing ) + freeing->size ) );
list_add_tail ( &freeing->list, &block->list );
if ( gap_after == 0 ) {
- DBGC2 ( &heap, "[%p,%p) + [%p,%p) -> [%p,%p)\n", freeing,
- ( ( ( void * ) freeing ) + freeing->size ), block,
- ( ( ( void * ) block ) + block->size ), freeing,
+ DBGC2 ( heap, "HEAP merging [%p,%p) + [%p,%p) -> [%p,%p)\n",
+ freeing, ( ( ( void * ) freeing ) + freeing->size ),
+ block, ( ( ( void * ) block ) + block->size ), freeing,
( ( ( void * ) block ) + block->size ) );
freeing->size += block->size;
list_del ( &block->list );
@@ -491,17 +498,26 @@ void free_memblock ( void *ptr, size_t size ) {
}
/* Update memory usage statistics */
- freemem += actual_size;
- usedmem -= actual_size;
+ heap->freemem += actual_size;
+ heap->usedmem -= actual_size;
+
+ /* Allow heap to shrink */
+ if ( heap->shrink && heap->shrink ( freeing, freeing->size ) ) {
+ list_del ( &freeing->list );
+ heap->freemem -= freeing->size;
+ VALGRIND_MAKE_MEM_UNDEFINED ( freeing, freeing->size );
+ }
- check_blocks();
- valgrind_make_blocks_noaccess();
+ /* Sanity checks */
+ check_blocks ( heap );
+ valgrind_make_blocks_noaccess ( heap );
}
/**
* Reallocate memory
*
- * @v old_ptr Memory previously allocated by malloc(), or NULL
+ * @v heap Heap
+ * @v old_ptr Memory previously allocated by heap_realloc(), or NULL
* @v new_size Requested size
* @ret new_ptr Allocated memory, or NULL
*
@@ -515,26 +531,27 @@ void free_memblock ( void *ptr, size_t size ) {
* If allocation fails the previously allocated block is left
* untouched and NULL is returned.
*
- * Calling realloc() with a new size of zero is a valid way to free a
- * memory block.
+ * Calling heap_realloc() with a new size of zero is a valid way to
+ * free a memory block.
*/
-void * realloc ( void *old_ptr, size_t new_size ) {
+void * heap_realloc ( struct heap *heap, void *old_ptr, size_t new_size ) {
struct autosized_block *old_block;
struct autosized_block *new_block;
size_t old_total_size;
size_t new_total_size;
size_t old_size;
+ size_t offset = offsetof ( struct autosized_block, data );
void *new_ptr = NOWHERE;
/* Allocate new memory if necessary. If allocation fails,
* return without touching the old block.
*/
if ( new_size ) {
- new_total_size = ( new_size +
- offsetof ( struct autosized_block, data ) );
+ new_total_size = ( new_size + offset );
if ( new_total_size < new_size )
return NULL;
- new_block = alloc_memblock ( new_total_size, 1, 0 );
+ new_block = heap_alloc_block ( heap, new_total_size,
+ heap->ptr_align, -offset );
if ( ! new_block )
return NULL;
new_block->size = new_total_size;
@@ -542,8 +559,10 @@ void * realloc ( void *old_ptr, size_t new_size ) {
sizeof ( new_block->size ) );
new_ptr = &new_block->data;
VALGRIND_MALLOCLIKE_BLOCK ( new_ptr, new_size, 0, 0 );
+ assert ( ( ( ( intptr_t ) new_ptr ) &
+ ( heap->ptr_align - 1 ) ) == 0 );
}
-
+
/* Copy across relevant part of the old data region (if any),
* then free it. Note that at this point either (a) new_ptr
* is valid, or (b) new_size is 0; either way, the memcpy() is
@@ -556,21 +575,40 @@ void * realloc ( void *old_ptr, size_t new_size ) {
sizeof ( old_block->size ) );
old_total_size = old_block->size;
assert ( old_total_size != 0 );
- old_size = ( old_total_size -
- offsetof ( struct autosized_block, data ) );
+ old_size = ( old_total_size - offset );
memcpy ( new_ptr, old_ptr,
( ( old_size < new_size ) ? old_size : new_size ) );
VALGRIND_FREELIKE_BLOCK ( old_ptr, 0 );
- free_memblock ( old_block, old_total_size );
+ heap_free_block ( heap, old_block, old_total_size );
}
if ( ASSERTED ) {
- DBGC ( &heap, "Possible memory corruption detected from %p\n",
- __builtin_return_address ( 0 ) );
+ DBGC ( heap, "HEAP detected possible memory corruption "
+ "from %p\n", __builtin_return_address ( 0 ) );
}
return new_ptr;
}
+/** The global heap */
+static struct heap heap = {
+ .blocks = LIST_HEAD_INIT ( heap.blocks ),
+ .align = MIN_MEMBLOCK_ALIGN,
+ .ptr_align = sizeof ( void * ),
+ .grow = discard_cache,
+};
+
+/**
+ * Reallocate memory
+ *
+ * @v old_ptr Memory previously allocated by malloc(), or NULL
+ * @v new_size Requested size
+ * @ret new_ptr Allocated memory, or NULL
+ */
+void * realloc ( void *old_ptr, size_t new_size ) {
+
+ return heap_realloc ( &heap, old_ptr, new_size );
+}
+
/**
* Allocate memory
*
@@ -585,8 +623,8 @@ void * malloc ( size_t size ) {
ptr = realloc ( NULL, size );
if ( ASSERTED ) {
- DBGC ( &heap, "Possible memory corruption detected from %p\n",
- __builtin_return_address ( 0 ) );
+ DBGC ( &heap, "HEAP detected possible memory corruption "
+ "from %p\n", __builtin_return_address ( 0 ) );
}
return ptr;
}
@@ -605,8 +643,8 @@ void free ( void *ptr ) {
realloc ( ptr, 0 );
if ( ASSERTED ) {
- DBGC ( &heap, "Possible memory corruption detected from %p\n",
- __builtin_return_address ( 0 ) );
+ DBGC ( &heap, "HEAP detected possible memory corruption "
+ "from %p\n", __builtin_return_address ( 0 ) );
}
}
@@ -628,35 +666,87 @@ void * zalloc ( size_t size ) {
if ( data )
memset ( data, 0, size );
if ( ASSERTED ) {
- DBGC ( &heap, "Possible memory corruption detected from %p\n",
- __builtin_return_address ( 0 ) );
+ DBGC ( &heap, "HEAP detected possible memory corruption "
+ "from %p\n", __builtin_return_address ( 0 ) );
}
return data;
}
/**
+ * Allocate memory with specified physical alignment and offset
+ *
+ * @v size Requested size
+ * @v align Physical alignment
+ * @v offset Offset from physical alignment
+ * @ret ptr Memory, or NULL
+ *
+ * @c align must be a power of two. @c size may not be zero.
+ */
+void * malloc_phys_offset ( size_t size, size_t phys_align, size_t offset ) {
+ void * ptr;
+
+ ptr = heap_alloc_block ( &heap, size, phys_align, offset );
+ if ( ptr && size ) {
+ assert ( ( phys_align == 0 ) ||
+ ( ( ( virt_to_phys ( ptr ) ^ offset ) &
+ ( phys_align - 1 ) ) == 0 ) );
+ VALGRIND_MALLOCLIKE_BLOCK ( ptr, size, 0, 0 );
+ }
+ return ptr;
+}
+
+/**
+ * Allocate memory with specified physical alignment
+ *
+ * @v size Requested size
+ * @v align Physical alignment
+ * @ret ptr Memory, or NULL
+ *
+ * @c align must be a power of two. @c size may not be zero.
+ */
+void * malloc_phys ( size_t size, size_t phys_align ) {
+
+ return malloc_phys_offset ( size, phys_align, 0 );
+}
+
+/**
+ * Free memory allocated with malloc_phys()
+ *
+ * @v ptr Memory allocated by malloc_phys(), or NULL
+ * @v size Size of memory, as passed to malloc_phys()
+ *
+ * Memory allocated with malloc_phys() can only be freed with
+ * free_phys(); it cannot be freed with the standard free().
+ *
+ * If @c ptr is NULL, no action is taken.
+ */
+void free_phys ( void *ptr, size_t size ) {
+
+ VALGRIND_FREELIKE_BLOCK ( ptr, 0 );
+ heap_free_block ( &heap, ptr, size );
+}
+
+/**
* Add memory to allocation pool
*
+ * @v heap Heap
* @v start Start address
- * @v end End address
+ * @v len Length of memory
*
- * Adds a block of memory [start,end) to the allocation pool. This is
- * a one-way operation; there is no way to reclaim this memory.
- *
- * @c start must be aligned to at least a multiple of sizeof(void*).
+ * Adds a block of memory to the allocation pool. The memory must be
+ * aligned to the heap's required free memory block alignment.
*/
-void mpopulate ( void *start, size_t len ) {
+void heap_populate ( struct heap *heap, void *start, size_t len ) {
- /* Prevent free_memblock() from rounding up len beyond the end
- * of what we were actually given...
- */
- len &= ~( MIN_MEMBLOCK_SIZE - 1 );
+ /* Sanity checks */
+ assert ( ( virt_to_phys ( start ) & ( heap->align - 1 ) ) == 0 );
+ assert ( ( len & ( heap->align - 1 ) ) == 0 );
/* Add to allocation pool */
- free_memblock ( start, len );
+ heap_free_block ( heap, start, len );
/* Fix up memory usage statistics */
- usedmem += len;
+ heap->usedmem += len;
}
/**
@@ -664,13 +754,19 @@ void mpopulate ( void *start, size_t len ) {
*
*/
static void init_heap ( void ) {
- VALGRIND_MAKE_MEM_NOACCESS ( heap, sizeof ( heap ) );
- VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks, sizeof ( free_blocks ) );
- mpopulate ( heap, sizeof ( heap ) );
+
+ /* Sanity check */
+ build_assert ( MIN_MEMBLOCK_ALIGN >= sizeof ( struct memory_block ) );
+
+ /* Populate heap */
+ VALGRIND_MAKE_MEM_NOACCESS ( heap_area, sizeof ( heap_area ) );
+ VALGRIND_MAKE_MEM_NOACCESS ( &heap.blocks, sizeof ( heap.blocks ) );
+ heap_populate ( &heap, heap_area, sizeof ( heap_area ) );
}
/** Memory allocator initialisation function */
struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = {
+ .name = "heap",
.initialise = init_heap,
};
@@ -680,7 +776,8 @@ struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = {
*/
static void shutdown_cache ( int booting __unused ) {
discard_all_cache();
- DBGC ( &heap, "Maximum heap usage %zdkB\n", ( maxusedmem >> 10 ) );
+ DBGC ( &heap, "HEAP maximum usage %zdkB\n",
+ ( heap.maxusedmem >> 10 ) );
}
/** Memory allocator shutdown function */
@@ -689,19 +786,17 @@ struct startup_fn heap_startup_fn __startup_fn ( STARTUP_EARLY ) = {
.shutdown = shutdown_cache,
};
-#if 0
-#include <stdio.h>
/**
- * Dump free block list
+ * Dump free block list (for debugging)
*
*/
-void mdumpfree ( void ) {
+void heap_dump ( struct heap *heap ) {
struct memory_block *block;
- printf ( "Free block list:\n" );
- list_for_each_entry ( block, &free_blocks, list ) {
- printf ( "[%p,%p] (size %#zx)\n", block,
- ( ( ( void * ) block ) + block->size ), block->size );
+ dbg_printf ( "HEAP free block list:\n" );
+ list_for_each_entry ( block, &heap->blocks, list ) {
+ dbg_printf ( "...[%p,%p] (size %#zx)\n", block,
+ ( ( ( void * ) block ) + block->size ),
+ block->size );
}
}
-#endif
diff --git a/src/core/memmap.c b/src/core/memmap.c
new file mode 100644
index 000000000..4a96055e6
--- /dev/null
+++ b/src/core/memmap.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <assert.h>
+#include <ipxe/io.h>
+#include <ipxe/memmap.h>
+
+/** @file
+ *
+ * System memory map
+ *
+ */
+
+/**
+ * Update memory region descriptor
+ *
+ * @v region Memory region of interest to be updated
+ * @v start Start address of known region
+ * @v size Size of known region
+ * @v flags Flags for known region
+ * @v name Name of known region (for debugging)
+ *
+ * Update a memory region descriptor based on a known existent region.
+ */
+void memmap_update ( struct memmap_region *region, uint64_t start,
+ uint64_t size, unsigned int flags, const char *name ) {
+ uint64_t max;
+
+ /* Sanity check */
+ assert ( region->max >= region->min );
+
+ /* Ignore empty regions */
+ if ( ! size )
+ return;
+
+ /* Calculate max addresses (and truncate if necessary) */
+ max = ( start + size - 1 );
+ if ( max < start ) {
+ max = ~( ( uint64_t ) 0 );
+ DBGC ( region, "MEMMAP [%#08llx,%#08llx] %s truncated "
+ "(invalid size %#08llx)\n",
+ ( ( unsigned long long ) start ),
+ ( ( unsigned long long ) max ), name,
+ ( ( unsigned long long ) size ) );
+ }
+
+ /* Ignore regions entirely below the region of interest */
+ if ( max < region->min )
+ return;
+
+ /* Ignore regions entirely above the region of interest */
+ if ( start > region->max )
+ return;
+
+ /* Update region of interest as applicable */
+ if ( start <= region->min ) {
+
+ /* Record this region as covering the region of interest */
+ region->flags |= flags;
+ if ( name )
+ region->name = name;
+
+ /* Update max address if no closer boundary exists */
+ if ( max < region->max )
+ region->max = max;
+
+ } else if ( start < region->max ) {
+
+ /* Update max address if no closer boundary exists */
+ region->max = ( start - 1 );
+ }
+
+ /* Sanity check */
+ assert ( region->max >= region->min );
+}
+
+/**
+ * Update memory region descriptor based on all in-use memory regions
+ *
+ * @v region Memory region of interest to be updated
+ */
+void memmap_update_used ( struct memmap_region *region ) {
+ struct used_region *used;
+
+ /* Update descriptor to hide all in-use regions */
+ for_each_table_entry ( used, USED_REGIONS ) {
+ memmap_update ( region, used->start, used->size,
+ MEMMAP_FL_USED, used->name );
+ }
+}
+
+/**
+ * Find largest usable memory region
+ *
+ * @v start Start address to fill in
+ * @ret len Length of region
+ */
+size_t memmap_largest ( physaddr_t *start ) {
+ struct memmap_region region;
+ size_t largest;
+ size_t size;
+
+ /* Find largest usable region */
+ DBGC ( &region, "MEMMAP finding largest usable region\n" );
+ *start = 0;
+ largest = 0;
+ for_each_memmap ( &region, 1 ) {
+ DBGC_MEMMAP ( &region, &region );
+ if ( ! memmap_is_usable ( &region ) )
+ continue;
+ size = memmap_size ( &region );
+ if ( size > largest ) {
+ DBGC ( &region, "...new largest region found\n" );
+ largest = size;
+ *start = region.min;
+ }
+ }
+ return largest;
+}
+
+PROVIDE_MEMMAP_INLINE ( null, memmap_describe );
+PROVIDE_MEMMAP_INLINE ( null, memmap_sync );
diff --git a/src/core/memmap_settings.c b/src/core/memmap_settings.c
index c620a0343..f54de9150 100644
--- a/src/core/memmap_settings.c
+++ b/src/core/memmap_settings.c
@@ -28,7 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <byteswap.h>
#include <ipxe/init.h>
#include <ipxe/settings.h>
-#include <ipxe/io.h>
+#include <ipxe/memmap.h>
/** @file
*
@@ -139,16 +139,15 @@ static int memmap_settings_applies ( struct settings *settings __unused,
static int memmap_settings_fetch ( struct settings *settings,
struct setting *setting,
void *data, size_t len ) {
- struct memory_map memmap;
- struct memory_region *region;
+ struct memmap_region region;
uint64_t result = 0;
+ unsigned int index = 0;
unsigned int start;
unsigned int count;
unsigned int scale;
int include_start;
int include_length;
int ignore_nonexistent;
- unsigned int i;
/* Parse settings tag */
start = MEMMAP_START ( setting->tag );
@@ -163,35 +162,40 @@ static int memmap_settings_fetch ( struct settings *settings,
( include_length ? "length" : "" ),
( ignore_nonexistent ? " ignore" : "" ), scale );
- /* Fetch memory map */
- get_memmap ( &memmap );
-
/* Extract results from memory map */
- for ( i = start ; count-- ; i++ ) {
-
- /* Check that region exists */
- if ( i >= memmap.count ) {
- if ( ignore_nonexistent ) {
- continue;
- } else {
- DBGC ( settings, "MEMMAP region %d does not "
- "exist\n", i );
- return -ENOENT;
- }
- }
+ for_each_memmap ( &region, 0 ) {
+
+ /* Skip non-memory regions */
+ if ( ! ( region.flags & MEMMAP_FL_MEMORY ) )
+ continue;
+
+ /* Ignore unwanted regions */
+ if ( index++ < start )
+ continue;
/* Extract results from this region */
- region = &memmap.regions[i];
if ( include_start ) {
- result += region->start;
- DBGC ( settings, "MEMMAP %d start %08llx\n",
- i, region->start );
+ result += region.min;
+ DBGC ( settings, "MEMMAP %d start %#08llx\n", index,
+ ( ( unsigned long long ) region.min ) );
}
if ( include_length ) {
- result += ( region->end - region->start );
- DBGC ( settings, "MEMMAP %d length %08llx\n",
- i, ( region->end - region->start ) );
+ result += memmap_size ( &region );
+ DBGC ( settings, "MEMMAP %d length %#08llx\n", index,
+ ( ( unsigned long long )
+ memmap_size ( &region ) ) );
}
+
+ /* Stop when we have accumulated sufficient regions */
+ if ( --count == 0 )
+ break;
+ }
+
+ /* Check for nonexistent regions */
+ if ( count && ( ! ignore_nonexistent ) ) {
+ DBGC ( settings, "MEMMAP regions %d-%d do not exist\n",
+ index, ( index + count - 1 ) );
+ return -ENOENT;
}
/* Scale result */
@@ -239,6 +243,7 @@ static void memmap_settings_init ( void ) {
/** Memory map settings initialiser */
struct init_fn memmap_settings_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "memmap",
.initialise = memmap_settings_init,
};
diff --git a/src/core/menu.c b/src/core/menu.c
deleted file mode 100644
index abad59999..000000000
--- a/src/core/menu.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-/** @file
- *
- * Menu selection
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <ipxe/list.h>
-#include <ipxe/menu.h>
-
-/** List of all menus */
-static LIST_HEAD ( menus );
-
-/**
- * Create menu
- *
- * @v name Menu name, or NULL
- * @v title Menu title, or NULL
- * @ret menu Menu, or NULL on failure
- */
-struct menu * create_menu ( const char *name, const char *title ) {
- size_t name_len;
- size_t title_len;
- size_t len;
- struct menu *menu;
- char *name_copy;
- char *title_copy;
-
- /* Destroy any existing menu of this name */
- menu = find_menu ( name );
- if ( menu )
- destroy_menu ( menu );
-
- /* Use empty title if none given */
- if ( ! title )
- title = "";
-
- /* Allocate menu */
- name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 );
- title_len = ( strlen ( title ) + 1 /* NUL */ );
- len = ( sizeof ( *menu ) + name_len + title_len );
- menu = zalloc ( len );
- if ( ! menu )
- return NULL;
- name_copy = ( ( void * ) ( menu + 1 ) );
- title_copy = ( name_copy + name_len );
-
- /* Initialise menu */
- if ( name ) {
- strcpy ( name_copy, name );
- menu->name = name_copy;
- }
- strcpy ( title_copy, title );
- menu->title = title_copy;
- INIT_LIST_HEAD ( &menu->items );
- INIT_LIST_HEAD ( &menu->hidden_items );
-
- /* Add to list of menus */
- list_add_tail ( &menu->list, &menus );
-
- DBGC ( menu, "MENU %s created with title \"%s\"\n",
- menu->name, menu->title );
-
- return menu;
-}
-
-/**
- * Add menu item
- *
- * @v menu Menu
- * @v label Label, or NULL
- * @v text Text, or NULL
- * @v shortcut Shortcut key
- * @v is_default Item is the default item
- * @ret item Menu item, or NULL on failure
- */
-struct menu_item * add_menu_item ( struct menu *menu, const char *label,
- const char *text, int shortcut,
- int is_default, int is_hidden ) {
- size_t label_len;
- size_t text_len;
- size_t len;
- struct menu_item *item;
- char *label_copy;
- char *text_copy;
-
- /* Use empty text if none given */
- if ( ! text )
- text = "";
-
- /* Allocate item */
- label_len = ( label ? ( strlen ( label ) + 1 /* NUL */ ) : 0 );
- text_len = ( strlen ( text ) + 1 /* NUL */ );
- len = ( sizeof ( *item ) + label_len + text_len );
- item = zalloc ( len );
- if ( ! item )
- return NULL;
- label_copy = ( ( void * ) ( item + 1 ) );
- text_copy = ( label_copy + label_len );
-
- /* Initialise item */
- if ( label ) {
- strcpy ( label_copy, label );
- item->label = label_copy;
- }
- strcpy ( text_copy, text );
- item->text = text_copy;
- item->shortcut = shortcut;
- item->is_default = is_default;
-
- /* Add to list of items */
- if ( is_hidden ) {
- list_add_tail ( &item->list, &menu->hidden_items );
- } else {
- list_add_tail ( &item->list, &menu->items );
- }
-
- return item;
-}
-
-/**
- * Destroy menu
- *
- * @v menu Menu
- */
-void destroy_menu ( struct menu *menu ) {
- struct menu_item *item;
- struct menu_item *tmp;
-
- /* Remove from list of menus */
- list_del ( &menu->list );
-
- /* Free items */
- list_for_each_entry_safe ( item, tmp, &menu->items, list ) {
- list_del ( &item->list );
- free ( item );
- }
- list_for_each_entry_safe ( item, tmp, &menu->hidden_items, list ) {
- list_del ( &item->list );
- free ( item );
- }
-
- /* Free menu */
- free ( menu );
-}
-
-/**
- * Find menu
- *
- * @v name Menu name, or NULL
- * @ret menu Menu, or NULL if not found
- */
-struct menu * find_menu ( const char *name ) {
- struct menu *menu;
-
- list_for_each_entry ( menu, &menus, list ) {
- if ( ( menu->name == name ) ||
- ( strcmp ( menu->name, name ) == 0 ) ) {
- return menu;
- }
- }
-
- return NULL;
-}
diff --git a/src/core/monojob.c b/src/core/monojob.c
index 2f066331c..ff22b4ac8 100644
--- a/src/core/monojob.c
+++ b/src/core/monojob.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <stdio.h>
diff --git a/src/core/netbios.c b/src/core/netbios.c
index 0d4e2086f..299e0d599 100644
--- a/src/core/netbios.c
+++ b/src/core/netbios.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/null_pci.c b/src/core/null_pci.c
new file mode 100644
index 000000000..196eabb6f
--- /dev/null
+++ b/src/core/null_pci.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * Null PCI API
+ *
+ */
+
+#include <ipxe/pci.h>
+
+PROVIDE_PCIAPI_INLINE ( null, pci_can_probe );
+PROVIDE_PCIAPI_INLINE ( null, pci_discover );
+PROVIDE_PCIAPI_INLINE ( null, pci_read_config_byte );
+PROVIDE_PCIAPI_INLINE ( null, pci_read_config_word );
+PROVIDE_PCIAPI_INLINE ( null, pci_read_config_dword );
+PROVIDE_PCIAPI_INLINE ( null, pci_write_config_byte );
+PROVIDE_PCIAPI_INLINE ( null, pci_write_config_word );
+PROVIDE_PCIAPI_INLINE ( null, pci_write_config_dword );
+PROVIDE_PCIAPI_INLINE ( null, pci_ioremap );
diff --git a/src/core/null_reboot.c b/src/core/null_reboot.c
index 7be5612a3..63b6e127e 100644
--- a/src/core/null_reboot.c
+++ b/src/core/null_reboot.c
@@ -37,9 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Reboot system
*
- * @v warm Perform a warm reboot
+ * @v flags Reboot flags
*/
-static void null_reboot ( int warm __unused ) {
+static void null_reboot ( int flags __unused ) {
printf ( "Cannot reboot; not implemented\n" );
while ( 1 ) {}
diff --git a/src/core/null_smbios.c b/src/core/null_smbios.c
new file mode 100644
index 000000000..d46aab3b5
--- /dev/null
+++ b/src/core/null_smbios.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * Null SMBIOS API
+ *
+ */
+
+#include <errno.h>
+#include <ipxe/smbios.h>
+
+/**
+ * Find SMBIOS
+ *
+ * @v smbios SMBIOS entry point descriptor structure to fill in
+ * @ret rc Return status code
+ */
+static int null_find_smbios ( struct smbios *smbios __unused ) {
+
+ return -ENOTSUP;
+}
+
+PROVIDE_SMBIOS ( null, find_smbios, null_find_smbios );
diff --git a/src/core/null_time.c b/src/core/null_time.c
index 90041a456..15d8a57a5 100644
--- a/src/core/null_time.c
+++ b/src/core/null_time.c
@@ -30,5 +30,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <ipxe/time.h>
+#include <ipxe/timer.h>
-PROVIDE_TIME_INLINE ( null, time_now );
+/**
+ * Get current time in seconds
+ *
+ * @ret time Time, in seconds
+ */
+static time_t null_now ( void ) {
+
+ /* Provide a non-absolute timer bassed on currticks() */
+ return ( currticks() / TICKS_PER_SEC );
+}
+
+PROVIDE_TIME ( null, time_now, null_now );
diff --git a/src/core/nvo.c b/src/core/nvo.c
index d2c9b5e73..8e500f816 100644
--- a/src/core/nvo.c
+++ b/src/core/nvo.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/core/open.c b/src/core/open.c
index f9198c9d9..8daa90f55 100644
--- a/src/core/open.c
+++ b/src/core/open.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdarg.h>
#include <string.h>
diff --git a/src/core/params.c b/src/core/params.c
index 58c829f62..d3fffc312 100644
--- a/src/core/params.c
+++ b/src/core/params.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/parseopt.c b/src/core/parseopt.c
index 8410e6e90..b920a7d84 100644
--- a/src/core/parseopt.c
+++ b/src/core/parseopt.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdint.h>
@@ -33,7 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <getopt.h>
#include <ipxe/uuid.h>
#include <ipxe/netdevice.h>
-#include <ipxe/menu.h>
+#include <ipxe/dynui.h>
#include <ipxe/settings.h>
#include <ipxe/params.h>
#include <ipxe/timer.h>
@@ -194,21 +195,21 @@ int parse_netdev_configurator ( char *text,
}
/**
- * Parse menu name
+ * Parse dynamic user interface name
*
* @v text Text
- * @ret menu Menu
+ * @ret dynui Dynamic user interface
* @ret rc Return status code
*/
-int parse_menu ( char *text, struct menu **menu ) {
+int parse_dynui ( char *text, struct dynamic_ui **dynui ) {
- /* Find menu */
- *menu = find_menu ( text );
- if ( ! *menu ) {
+ /* Find user interface */
+ *dynui = find_dynui ( text );
+ if ( ! *dynui ) {
if ( text ) {
- printf ( "\"%s\": no such menu\n", text );
+ printf ( "\"%s\": no such user interface\n", text );
} else {
- printf ( "No default menu\n" );
+ printf ( "No default user interface\n" );
}
return -ENOENT;
}
diff --git a/src/core/pending.c b/src/core/pending.c
index 96d0cf197..4a1dd6a34 100644
--- a/src/core/pending.c
+++ b/src/core/pending.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <errno.h>
#include <ipxe/process.h>
diff --git a/src/core/pinger.c b/src/core/pinger.c
index 0ff7bb9f2..bbfa83f8d 100644
--- a/src/core/pinger.c
+++ b/src/core/pinger.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <string.h>
diff --git a/src/core/pixbuf.c b/src/core/pixbuf.c
index 641a0fb53..df187f93d 100644
--- a/src/core/pixbuf.c
+++ b/src/core/pixbuf.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -30,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <ipxe/umalloc.h>
#include <ipxe/image.h>
@@ -65,7 +67,8 @@ struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) {
ref_init ( &pixbuf->refcnt, free_pixbuf );
pixbuf->width = width;
pixbuf->height = height;
- pixbuf->len = ( width * height * sizeof ( uint32_t ) );
+ pixbuf->pixels = ( width * height );
+ pixbuf->len = ( pixbuf->pixels * sizeof ( uint32_t ) );
/* Check for multiplication overflow */
if ( ( width != 0 ) &&
diff --git a/src/core/pool.c b/src/core/pool.c
index 0163405f7..daf761aa3 100644
--- a/src/core/pool.c
+++ b/src/core/pool.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/core/posix_io.c b/src/core/posix_io.c
index 35b52beeb..9112798ec 100644
--- a/src/core/posix_io.c
+++ b/src/core/posix_io.c
@@ -254,15 +254,14 @@ int select ( fd_set *readfds, int wait ) {
/**
* Read data from file
*
- * @v buffer Data buffer
- * @v offset Starting offset within data buffer
- * @v len Maximum length to read
+ * @v buf Data buffer
+ * @v max_len Maximum length to read
* @ret len Actual length read, or negative error number
*
* This call is non-blocking; if no data is available to read then
* -EWOULDBLOCK will be returned.
*/
-ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
+ssize_t read ( int fd, void *buf, size_t max_len ) {
struct posix_file *file;
struct io_buffer *iobuf;
size_t len;
@@ -281,7 +280,7 @@ ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
len = iob_len ( iobuf );
if ( len > max_len )
len = max_len;
- copy_to_user ( buffer, offset, iobuf->data, len );
+ memcpy ( buf, iobuf->data, len );
iob_pull ( iobuf, len );
if ( ! iob_len ( iobuf ) ) {
list_del ( &iobuf->list );
diff --git a/src/core/process.c b/src/core/process.c
index 69852c416..883469dc5 100644
--- a/src/core/process.c
+++ b/src/core/process.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/list.h>
#include <ipxe/init.h>
@@ -133,5 +134,6 @@ static void init_processes ( void ) {
/** Process initialiser */
struct init_fn process_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "process",
.initialise = init_processes,
};
diff --git a/src/core/profile.c b/src/core/profile.c
index 3655108ea..27d481d45 100644
--- a/src/core/profile.c
+++ b/src/core/profile.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdio.h>
diff --git a/src/core/quiesce.c b/src/core/quiesce.c
index 5d2a919d0..9c4e37849 100644
--- a/src/core/quiesce.c
+++ b/src/core/quiesce.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/core/random.c b/src/core/random.c
index e3251964b..e8fbe6966 100644
--- a/src/core/random.c
+++ b/src/core/random.c
@@ -5,6 +5,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdlib.h>
diff --git a/src/core/refcnt.c b/src/core/refcnt.c
index 47c975a0b..a66511291 100644
--- a/src/core/refcnt.c
+++ b/src/core/refcnt.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <ipxe/refcnt.h>
diff --git a/src/core/resolv.c b/src/core/resolv.c
index fab8def4b..0fc02ccf4 100644
--- a/src/core/resolv.c
+++ b/src/core/resolv.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/core/sanboot.c b/src/core/sanboot.c
index e49a3f92d..45cd5eff3 100644
--- a/src/core/sanboot.c
+++ b/src/core/sanboot.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -32,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/xfer.h>
@@ -424,10 +426,10 @@ int sandev_reopen ( struct san_device *sandev ) {
struct san_command_rw_params {
/** SAN device read/write operation */
int ( * block_rw ) ( struct interface *control, struct interface *data,
- uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len );
+ uint64_t lba, unsigned int count, void *buffer,
+ size_t len );
/** Data buffer */
- userptr_t buffer;
+ void *buffer;
/** Starting LBA */
uint64_t lba;
/** Block count */
@@ -594,11 +596,11 @@ int sandev_reset ( struct san_device *sandev ) {
* @ret rc Return status code
*/
static int sandev_rw ( struct san_device *sandev, uint64_t lba,
- unsigned int count, userptr_t buffer,
+ unsigned int count, void *buffer,
int ( * block_rw ) ( struct interface *control,
struct interface *data,
uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len ) ) {
+ void *buffer, size_t len ) ) {
union san_command_params params;
unsigned int remaining;
size_t frag_len;
@@ -625,7 +627,7 @@ static int sandev_rw ( struct san_device *sandev, uint64_t lba,
/* Move to next fragment */
frag_len = ( sandev->capacity.blksize * params.rw.count );
- params.rw.buffer = userptr_add ( params.rw.buffer, frag_len );
+ params.rw.buffer += frag_len;
params.rw.lba += params.rw.count;
remaining -= params.rw.count;
}
@@ -643,11 +645,12 @@ static int sandev_rw ( struct san_device *sandev, uint64_t lba,
* @ret rc Return status code
*/
int sandev_read ( struct san_device *sandev, uint64_t lba,
- unsigned int count, userptr_t buffer ) {
+ unsigned int count, void *buffer ) {
int rc;
/* Read from device */
- if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_read ) ) != 0 )
+ if ( ( rc = sandev_rw ( sandev, lba, count, buffer,
+ block_read ) ) != 0 )
return rc;
return 0;
@@ -663,11 +666,12 @@ int sandev_read ( struct san_device *sandev, uint64_t lba,
* @ret rc Return status code
*/
int sandev_write ( struct san_device *sandev, uint64_t lba,
- unsigned int count, userptr_t buffer ) {
+ unsigned int count, void *buffer ) {
int rc;
/* Write to device */
- if ( ( rc = sandev_rw ( sandev, lba, count, buffer, block_write ) ) != 0 )
+ if ( ( rc = sandev_rw ( sandev, lba, count, buffer,
+ block_write ) ) != 0 )
return rc;
/* Quiesce system. This is a heuristic designed to ensure
@@ -799,8 +803,7 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) {
}
/* Read primary volume descriptor */
- if ( ( rc = sandev_read ( sandev, lba, count,
- virt_to_user ( scratch ) ) ) != 0 ) {
+ if ( ( rc = sandev_read ( sandev, lba, count, scratch ) ) != 0 ) {
DBGC ( sandev->drive, "SAN %#02x could not read ISO9660 "
"primary volume descriptor: %s\n",
sandev->drive, strerror ( rc ) );
diff --git a/src/core/serial.c b/src/core/serial.c
index bef9ccbab..728ad7785 100644
--- a/src/core/serial.c
+++ b/src/core/serial.c
@@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/uart.h>
#include <ipxe/console.h>
#include <ipxe/serial.h>
+#include <ipxe/ns16550.h>
#include <config/console.h>
#include <config/serial.h>
@@ -44,29 +45,44 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define CONSOLE_SERIAL ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
#endif
-/* UART port number */
-#ifdef COMCONSOLE
-#define CONSOLE_PORT COMCONSOLE
+#ifdef SERIAL_FIXED
+#define SERIAL_PREFIX_fixed
#else
-#define CONSOLE_PORT 0
+#define SERIAL_PREFIX_fixed __fixed_
#endif
-/* UART baud rate */
-#ifdef COMPRESERVE
-#define CONSOLE_BAUD 0
-#else
-#define CONSOLE_BAUD COMSPEED
+/* Serial console UART */
+#ifndef COMCONSOLE
+#define COMCONSOLE NULL
#endif
-/* UART line control register value */
-#ifdef COMPRESERVE
-#define CONSOLE_LCR 0
-#else
-#define CONSOLE_LCR UART_LCR_WPS ( COMDATA, COMPARITY, COMSTOP )
+/* Serial console baud rate */
+#ifndef COMSPEED
+#define COMSPEED 0
#endif
-/** Serial console UART */
-struct uart serial_console;
+/** Active serial console UART
+ *
+ * Explicitly initialised to @c NULL since this variable may be
+ * accessed before .bss has been zeroed.
+ */
+struct uart *serial_console = NULL;
+
+/**
+ * Get fixed serial console UART
+ *
+ * @ret uart Serial console UART, or NULL
+ */
+struct uart * fixed_serial_console ( void ) {
+ struct uart *uart = COMCONSOLE;
+ unsigned int baud = COMSPEED;
+
+ /* Set default baud rate, if applicable */
+ if ( uart && baud )
+ uart->baud = baud;
+
+ return uart;
+}
/**
* Print a character to serial console
@@ -76,11 +92,11 @@ struct uart serial_console;
static void serial_putchar ( int character ) {
/* Do nothing if we have no UART */
- if ( ! serial_console.base )
+ if ( ! serial_console )
return;
/* Transmit character */
- uart_transmit ( &serial_console, character );
+ uart_transmit ( serial_console, character );
}
/**
@@ -92,14 +108,14 @@ static int serial_getchar ( void ) {
uint8_t data;
/* Do nothing if we have no UART */
- if ( ! serial_console.base )
+ if ( ! serial_console )
return 0;
/* Wait for data to be ready */
- while ( ! uart_data_ready ( &serial_console ) ) {}
+ while ( ! uart_data_ready ( serial_console ) ) {}
/* Receive data */
- data = uart_receive ( &serial_console );
+ data = uart_receive ( serial_console );
/* Strip any high bit and convert DEL to backspace */
data &= 0x7f;
@@ -118,11 +134,11 @@ static int serial_getchar ( void ) {
static int serial_iskey ( void ) {
/* Do nothing if we have no UART */
- if ( ! serial_console.base )
+ if ( ! serial_console )
return 0;
/* Check UART */
- return uart_data_ready ( &serial_console );
+ return uart_data_ready ( serial_console );
}
/** Serial console */
@@ -135,26 +151,24 @@ struct console_driver serial_console_driver __console_driver = {
/** Initialise serial console */
static void serial_init ( void ) {
+ struct uart *uart;
int rc;
- /* Do nothing if we have no default port */
- if ( ! CONSOLE_PORT )
- return;
-
- /* Select UART */
- if ( ( rc = uart_select ( &serial_console, CONSOLE_PORT ) ) != 0 ) {
- DBG ( "Could not select UART %d: %s\n",
- CONSOLE_PORT, strerror ( rc ) );
+ /* Get default serial console, if any */
+ uart = default_serial_console();
+ if ( ! uart )
return;
- }
/* Initialise UART */
- if ( ( rc = uart_init ( &serial_console, CONSOLE_BAUD,
- CONSOLE_LCR ) ) != 0 ) {
- DBG ( "Could not initialise UART %d baud %d LCR %#02x: %s\n",
- CONSOLE_PORT, CONSOLE_BAUD, CONSOLE_LCR, strerror ( rc ));
+ if ( ( rc = uart_init ( uart ) ) != 0 ) {
+ DBGC ( uart, "SERIAL could not initialise %s: %s\n",
+ uart->name, strerror ( rc ) );
return;
}
+
+ /* Record UART as serial console */
+ serial_console = uart;
+ DBGC ( uart, "SERIAL using %s\n", uart->name );
}
/**
@@ -165,17 +179,18 @@ static void serial_init ( void ) {
static void serial_shutdown ( int flags __unused ) {
/* Do nothing if we have no UART */
- if ( ! serial_console.base )
+ if ( ! serial_console )
return;
/* Flush any pending output */
- uart_flush ( &serial_console );
+ uart_flush ( serial_console );
/* Leave console enabled; it's still usable */
}
/** Serial console initialisation function */
struct init_fn serial_console_init_fn __init_fn ( INIT_CONSOLE ) = {
+ .name = "serial",
.initialise = serial_init,
};
@@ -184,3 +199,6 @@ struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = {
.name = "serial",
.shutdown = serial_shutdown,
};
+
+PROVIDE_SERIAL_INLINE ( null, default_serial_console );
+PROVIDE_SERIAL ( fixed, default_serial_console, fixed_serial_console );
diff --git a/src/core/settings.c b/src/core/settings.c
index aa4bbae2a..ae6cf850c 100644
--- a/src/core/settings.c
+++ b/src/core/settings.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
@@ -2866,5 +2867,6 @@ static void builtin_init ( void ) {
/** Built-in settings initialiser */
struct init_fn builtin_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "builtin",
.initialise = builtin_init,
};
diff --git a/src/core/spcr.c b/src/core/spcr.c
new file mode 100644
index 000000000..b1cee1608
--- /dev/null
+++ b/src/core/spcr.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <errno.h>
+#include <ipxe/serial.h>
+#include <ipxe/pci.h>
+#include <ipxe/ns16550.h>
+#include <ipxe/spcr.h>
+
+/** @file
+ *
+ * ACPI Serial Port Console Redirection (SPCR)
+ *
+ */
+
+#ifdef SERIAL_SPCR
+#define SERIAL_PREFIX_spcr
+#else
+#define SERIAL_PREFIX_spcr __spcr_
+#endif
+
+/** SPCR-defined UART */
+static struct uart spcr_uart = {
+ .refcnt = REF_INIT ( ref_no_free ),
+ .name = "SPCR",
+};
+
+/** SPCR-defined 16550 UART */
+static struct ns16550_uart spcr_ns16550 = {
+ .clock = NS16550_CLK_DEFAULT,
+};
+
+/** Base baud rate for SPCR divisors */
+#define SPCR_BAUD_BASE 115200
+
+/** SPCR baud rate divisors */
+static const uint8_t spcr_baud_divisor[SPCR_BAUD_MAX] = {
+ [SPCR_BAUD_2400] = ( SPCR_BAUD_BASE / 2400 ),
+ [SPCR_BAUD_4800] = ( SPCR_BAUD_BASE / 4800 ),
+ [SPCR_BAUD_9600] = ( SPCR_BAUD_BASE / 9600 ),
+ [SPCR_BAUD_19200] = ( SPCR_BAUD_BASE / 19200 ),
+ [SPCR_BAUD_38400] = ( SPCR_BAUD_BASE / 38400 ),
+ [SPCR_BAUD_57600] = ( SPCR_BAUD_BASE / 57600 ),
+ [SPCR_BAUD_115200] = ( SPCR_BAUD_BASE / 115200 ),
+};
+
+/**
+ * Configure 16550-based serial console
+ *
+ * @v spcr SPCR table
+ * @v uart UART to configure
+ * @ret rc Return status code
+ */
+static int spcr_16550 ( struct spcr_table *spcr, struct uart *uart ) {
+ struct ns16550_uart *ns16550 = &spcr_ns16550;
+
+ /* Set base address */
+ ns16550->base = acpi_ioremap ( &spcr->base, NS16550_LEN );
+ if ( ! ns16550->base ) {
+ DBGC ( uart, "SPCR could not map registers\n" );
+ return -ENODEV;
+ }
+
+ /* Set clock frequency, if specified */
+ if ( spcr->clock )
+ ns16550->clock = le32_to_cpu ( spcr->clock );
+
+ /* Configure UART as a 16550 */
+ uart->op = &ns16550_operations;
+ uart->priv = ns16550;
+
+ return 0;
+}
+
+/**
+ * Identify default serial console
+ *
+ * @ret uart Default serial console UART, or NULL
+ */
+static struct uart * spcr_console ( void ) {
+ struct uart *uart = &spcr_uart;
+ struct spcr_table *spcr;
+ unsigned int baud;
+ int rc;
+
+ /* Locate SPCR table */
+ spcr = container_of ( acpi_table ( SPCR_SIGNATURE, 0 ),
+ struct spcr_table, acpi );
+ if ( ! spcr ) {
+ DBGC ( uart, "SPCR found no table\n" );
+ goto err_table;
+ }
+ DBGC2 ( uart, "SPCR found table:\n" );
+ DBGC2_HDA ( uart, 0, spcr, sizeof ( *spcr ) );
+ DBGC ( uart, "SPCR is type %d at %02x:%08llx\n",
+ spcr->type, spcr->base.type,
+ ( ( unsigned long long ) le64_to_cpu ( spcr->base.address ) ) );
+ if ( spcr->pci_vendor_id != cpu_to_le16 ( PCI_ANY_ID ) ) {
+ DBGC ( uart, "SPCR is PCI " PCI_FMT " (%04x:%04x)\n",
+ spcr->pci_segment, spcr->pci_bus, spcr->pci_dev,
+ spcr->pci_func, le16_to_cpu ( spcr->pci_vendor_id ),
+ le16_to_cpu ( spcr->pci_device_id ) );
+ }
+
+ /* Get baud rate */
+ baud = 0;
+ if ( le32_to_cpu ( spcr->acpi.length ) >=
+ ( offsetof ( typeof ( *spcr ), precise ) +
+ sizeof ( spcr->precise ) ) ) {
+ baud = le32_to_cpu ( spcr->precise );
+ if ( baud )
+ DBGC ( uart, "SPCR has precise baud rate %d\n", baud );
+ }
+ if ( ( ! baud ) && spcr->baud && ( spcr->baud < SPCR_BAUD_MAX ) ) {
+ baud = ( SPCR_BAUD_BASE / spcr_baud_divisor[spcr->baud] );
+ DBGC ( uart, "SPCR has baud rate %d\n", baud );
+ }
+ uart->baud = baud;
+
+ /* Initialise according to type */
+ switch ( spcr->type ) {
+ case SPCR_TYPE_16550:
+ case SPCR_TYPE_16450:
+ case SPCR_TYPE_16550_GAS:
+ if ( ( rc = spcr_16550 ( spcr, uart ) ) != 0 )
+ goto err_type;
+ break;
+ default:
+ DBGC ( uart, "SPCR unsupported type %d\n", spcr->type );
+ goto err_type;
+ }
+
+ return uart;
+
+ err_type:
+ err_table:
+ /* Fall back to using fixed serial console */
+ return fixed_serial_console();
+}
+
+PROVIDE_SERIAL ( spcr, default_serial_console, spcr_console );
diff --git a/src/core/string.c b/src/core/string.c
index 9a1b9b72a..2af19b7fe 100644
--- a/src/core/string.c
+++ b/src/core/string.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdint.h>
@@ -321,9 +322,9 @@ char * strstr ( const char *haystack, const char *needle ) {
*
* @v dest Destination string
* @v src Source string
- * @ret dest Destination string
+ * @ret dnul Terminating NUL of destination string
*/
-char * strcpy ( char *dest, const char *src ) {
+char * stpcpy ( char *dest, const char *src ) {
const uint8_t *src_bytes = ( ( const uint8_t * ) src );
uint8_t *dest_bytes = ( ( uint8_t * ) dest );
@@ -333,6 +334,19 @@ char * strcpy ( char *dest, const char *src ) {
if ( ! *dest_bytes )
break;
}
+ return ( ( char * ) dest_bytes );
+}
+
+/**
+ * Copy string
+ *
+ * @v dest Destination string
+ * @v src Source string
+ * @ret dest Destination string
+ */
+char * strcpy ( char *dest, const char *src ) {
+
+ stpcpy ( dest, src );
return dest;
}
diff --git a/src/core/time.c b/src/core/time.c
index c353ac5bd..6d33f6caf 100644
--- a/src/core/time.c
+++ b/src/core/time.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <time.h>
diff --git a/src/core/timer.c b/src/core/timer.c
index 24745cef7..db0f32cf1 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <ipxe/process.h>
@@ -170,6 +171,7 @@ static void timer_probe ( void ) {
/** Timer initialisation function */
struct init_fn timer_init_fn __init_fn ( INIT_EARLY ) = {
+ .name = "timer",
.initialise = timer_probe,
};
diff --git a/src/core/uaccess.c b/src/core/uaccess.c
new file mode 100644
index 000000000..ae873d109
--- /dev/null
+++ b/src/core/uaccess.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/uaccess.h>
+
+/** @file
+ *
+ * iPXE user access API
+ *
+ */
+
+/* Flat address space user access API */
+PROVIDE_UACCESS_INLINE ( flat, phys_to_virt );
+PROVIDE_UACCESS_INLINE ( flat, virt_to_phys );
+
+/* Virtual address offset user access API */
+PROVIDE_UACCESS_INLINE ( offset, phys_to_virt );
+PROVIDE_UACCESS_INLINE ( offset, virt_to_phys );
diff --git a/src/core/uart.c b/src/core/uart.c
index b85fe0767..5bff9fdd5 100644
--- a/src/core/uart.c
+++ b/src/core/uart.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -25,129 +25,138 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
- * 16550-compatible UART
+ * Generic UARTs
*
*/
-#include <unistd.h>
+#include <stdlib.h>
+#include <strings.h>
#include <errno.h>
#include <ipxe/uart.h>
-/** Timeout for transmit holding register to become empty */
-#define UART_THRE_TIMEOUT_MS 100
+/** List of registered UARTs */
+LIST_HEAD ( uarts );
-/** Timeout for transmitter to become empty */
-#define UART_TEMT_TIMEOUT_MS 1000
+static void null_uart_transmit ( struct uart *uart __unused,
+ uint8_t byte __unused ) {
+}
+
+static int null_uart_data_ready ( struct uart *uart __unused ) {
+ return 0;
+}
+
+static uint8_t null_uart_receive ( struct uart *uart __unused ) {
+ return 0;
+}
+
+static int null_uart_init ( struct uart *uart __unused ) {
+ return 0;
+}
+
+static void null_uart_flush ( struct uart *uart __unused ) {
+}
+
+/** Null UART operations */
+struct uart_operations null_uart_operations = {
+ .transmit = null_uart_transmit,
+ .data_ready = null_uart_data_ready,
+ .receive = null_uart_receive,
+ .init = null_uart_init,
+ .flush = null_uart_flush,
+};
/**
- * Transmit data
+ * Allocate UART
*
- * @v uart UART
- * @v data Data
+ * @v priv_len Length of private data
+ * @ret uart UART, or NULL on error
*/
-void uart_transmit ( struct uart *uart, uint8_t data ) {
- unsigned int i;
- uint8_t lsr;
-
- /* Wait for transmitter holding register to become empty */
- for ( i = 0 ; i < UART_THRE_TIMEOUT_MS ; i++ ) {
- lsr = uart_read ( uart, UART_LSR );
- if ( lsr & UART_LSR_THRE )
- break;
- mdelay ( 1 );
- }
+struct uart * alloc_uart ( size_t priv_len ) {
+ struct uart *uart;
+
+ /* Allocate and initialise UART */
+ uart = zalloc ( sizeof ( *uart ) + priv_len );
+ if ( ! uart )
+ return NULL;
+ uart->priv = ( ( ( void * ) uart ) + sizeof ( *uart ) );
- /* Transmit data (even if we timed out) */
- uart_write ( uart, UART_THR, data );
+ return uart;
}
/**
- * Flush data
+ * Register fixed UARTs (when not provided by platform)
*
- * @v uart UART
+ * @ret rc Return status code
*/
-void uart_flush ( struct uart *uart ) {
- unsigned int i;
- uint8_t lsr;
-
- /* Wait for transmitter and receiver to become empty */
- for ( i = 0 ; i < UART_TEMT_TIMEOUT_MS ; i++ ) {
- uart_read ( uart, UART_RBR );
- lsr = uart_read ( uart, UART_LSR );
- if ( ( lsr & UART_LSR_TEMT ) && ! ( lsr & UART_LSR_DR ) )
- break;
- }
+__weak int uart_register_fixed ( void ) {
+
+ return 0;
}
/**
- * Check for existence of UART
+ * Register UART
*
* @v uart UART
* @ret rc Return status code
*/
-int uart_exists ( struct uart *uart ) {
-
- /* Fail if no UART port is defined */
- if ( ! uart->base )
- return -ENODEV;
+int uart_register ( struct uart *uart ) {
- /* Fail if UART scratch register seems not to be present */
- uart_write ( uart, UART_SCR, 0x18 );
- if ( uart_read ( uart, UART_SCR ) != 0x18 )
- return -ENODEV;
- uart_write ( uart, UART_SCR, 0xae );
- if ( uart_read ( uart, UART_SCR ) != 0xae )
- return -ENODEV;
+ /* Add to list of registered UARTs */
+ uart_get ( uart );
+ list_add_tail ( &uart->list, &uarts );
+ DBGC ( uart, "UART %s registered\n", uart->name );
return 0;
}
/**
- * Initialise UART
+ * Unregister UART
*
* @v uart UART
- * @v baud Baud rate, or zero to leave unchanged
- * @v lcr Line control register value, or zero to leave unchanged
- * @ret rc Return status code
*/
-int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr ) {
- uint8_t dlm;
- uint8_t dll;
+void uart_unregister ( struct uart *uart ) {
+
+ /* Remove from list of registered UARTs */
+ list_del ( &uart->list );
+ uart_put ( uart );
+}
+
+/**
+ * Find named UART
+ *
+ * @v name UART name
+ * @ret uart UART, or NULL if not found
+ */
+struct uart * uart_find ( const char *name ) {
+ struct uart *uart;
+ unsigned int index;
+ char *endp;
int rc;
- /* Check for existence of UART */
- if ( ( rc = uart_exists ( uart ) ) != 0 )
- return rc;
-
- /* Configure divisor and line control register, if applicable */
- if ( ! lcr )
- lcr = uart_read ( uart, UART_LCR );
- uart->lcr = lcr;
- uart_write ( uart, UART_LCR, ( lcr | UART_LCR_DLAB ) );
- if ( baud ) {
- uart->divisor = ( UART_MAX_BAUD / baud );
- dlm = ( ( uart->divisor >> 8 ) & 0xff );
- dll = ( ( uart->divisor >> 0 ) & 0xff );
- uart_write ( uart, UART_DLM, dlm );
- uart_write ( uart, UART_DLL, dll );
- } else {
- dlm = uart_read ( uart, UART_DLM );
- dll = uart_read ( uart, UART_DLL );
- uart->divisor = ( ( dlm << 8 ) | dll );
+ /* Register fixed platform UARTs if not already registered */
+ if ( list_empty ( &uarts ) ) {
+ if ( ( rc = uart_register_fixed() ) != 0 ) {
+ DBGC ( &uarts, "UART could not register fixed UARTs: "
+ "%s\n", strerror ( rc ) );
+ /* Continue anyway */
+ }
}
- uart_write ( uart, UART_LCR, ( lcr & ~UART_LCR_DLAB ) );
- /* Disable interrupts */
- uart_write ( uart, UART_IER, 0 );
+ /* Try parsing name as a numeric index */
+ index = strtoul ( name, &endp, 10 );
- /* Enable FIFOs */
- uart_write ( uart, UART_FCR, UART_FCR_FE );
+ /* Find matching UART, if any */
+ list_for_each_entry ( uart, &uarts, list ) {
- /* Assert DTR and RTS */
- uart_write ( uart, UART_MCR, ( UART_MCR_DTR | UART_MCR_RTS ) );
+ /* Check for a matching name */
+ if ( strcasecmp ( name, uart->name ) == 0 )
+ return uart;
- /* Flush any stale data */
- uart_flush ( uart );
+ /* Check for a matching numeric index */
+ if ( ( *endp == '\0' ) && ( index-- == 0 ) )
+ return uart;
+ }
- return 0;
+ DBGC ( &uarts, "UART %s not found\n", name );
+ return NULL;
}
diff --git a/src/core/uheap.c b/src/core/uheap.c
new file mode 100644
index 000000000..3f93bc030
--- /dev/null
+++ b/src/core/uheap.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/io.h>
+#include <ipxe/memmap.h>
+#include <ipxe/malloc.h>
+#include <ipxe/umalloc.h>
+
+/** @file
+ *
+ * External ("user") heap
+ *
+ * This file implements an external heap (for umalloc()) that grows
+ * downwards from the top of the largest contiguous accessible block
+ * in the system memory map.
+ */
+
+/**
+ * Alignment for external heap allocations
+ *
+ * Historically, umalloc() has produced page-aligned allocations, and
+ * the hidden region in the system memory map has been aligned to a
+ * page boundary. Preserve this behaviour, to avoid needing to
+ * inspect and update large amounts of driver code, and also because
+ * it keeps the resulting memory maps easy to read.
+ */
+#define UHEAP_ALIGN PAGE_SIZE
+
+static struct heap uheap;
+
+/** Minimum possible start of external heap */
+physaddr_t uheap_limit;
+
+/** Start of external heap */
+physaddr_t uheap_start;
+
+/** End of external heap */
+physaddr_t uheap_end;
+
+/** In-use memory region */
+struct used_region uheap_used __used_region = {
+ .name = "uheap",
+};
+
+/**
+ * Adjust size of external heap in-use memory region
+ *
+ * @v delta Size change
+ */
+static void uheap_resize ( ssize_t delta ) {
+
+ /* Update in-use memory region */
+ assert ( ( delta & ( UHEAP_ALIGN - 1 ) ) == 0 );
+ uheap_start -= delta;
+ assert ( uheap_limit <= uheap_start );
+ assert ( uheap_start <= uheap_end );
+ assert ( ( uheap_limit & ( UHEAP_ALIGN - 1 ) ) == 0 );
+ assert ( ( uheap_start & ( UHEAP_ALIGN - 1 ) ) == 0 );
+ assert ( ( uheap_end & ( UHEAP_ALIGN - 1 ) ) == 0 );
+ memmap_use ( &uheap_used, uheap_start, ( uheap_end - uheap_start ) );
+ DBGC ( &uheap, "UHEAP now at (%#08lx)...[%#08lx,%#08lx)\n",
+ uheap_limit, uheap_start, uheap_end );
+ memmap_dump_all ( 1 );
+}
+
+/**
+ * Find an external heap region
+ *
+ */
+static void uheap_find ( void ) {
+ physaddr_t start;
+ physaddr_t end;
+ size_t before;
+ size_t after;
+ size_t strip;
+ size_t size;
+
+ /* Sanity checks */
+ assert ( uheap_start == uheap_end );
+ assert ( uheap_limit == uheap_end );
+ assert ( uheap_used.size == 0 );
+
+ /* Find the largest region within the system memory map */
+ size = memmap_largest ( &start );
+ end = ( start + size );
+ DBGC ( &uheap, "UHEAP largest region is [%#08lx,%#08lx)\n",
+ start, end );
+
+ /* Align start and end addresses, and prevent overflow to zero */
+ after = ( end ? ( end & ( UHEAP_ALIGN - 1 ) ) : UHEAP_ALIGN );
+ before = ( ( -start ) & ( UHEAP_ALIGN - 1 ) );
+ strip = ( before + after );
+ if ( strip > size )
+ return;
+ start += before;
+ end -= after;
+ size -= strip;
+ assert ( ( end - start ) == size );
+
+ /* Record region */
+ uheap_limit = start;
+ uheap_start = end;
+ uheap_end = end;
+ uheap_resize ( 0 );
+}
+
+/**
+ * Attempt to grow external heap
+ *
+ * @v size Failed allocation size
+ * @ret grown Heap has grown: retry allocations
+ */
+static unsigned int uheap_grow ( size_t size ) {
+ void *new;
+
+ /* Initialise heap, if it does not yet exist */
+ if ( uheap_limit == uheap_end )
+ uheap_find();
+
+ /* Fail if insufficient space remains */
+ if ( size > ( uheap_start - uheap_limit ) )
+ return 0;
+
+ /* Grow heap */
+ new = ( phys_to_virt ( uheap_start ) - size );
+ heap_populate ( &uheap, new, size );
+ uheap_resize ( size );
+
+ return 1;
+}
+
+/**
+ * Allow external heap to shrink
+ *
+ * @v ptr Start of free block
+ * @v size Size of free block
+ * @ret shrunk Heap has shrunk: discard block
+ */
+static unsigned int uheap_shrink ( void *ptr, size_t size ) {
+
+ /* Do nothing unless this is the lowest block in the heap */
+ if ( virt_to_phys ( ptr ) != uheap_start )
+ return 0;
+
+ /* Shrink heap */
+ uheap_resize ( -size );
+
+ return 1;
+}
+
+/** The external heap */
+static struct heap uheap = {
+ .blocks = LIST_HEAD_INIT ( uheap.blocks ),
+ .align = UHEAP_ALIGN,
+ .ptr_align = UHEAP_ALIGN,
+ .grow = uheap_grow,
+ .shrink = uheap_shrink,
+};
+
+/**
+ * Reallocate external memory
+ *
+ * @v old_ptr Memory previously allocated by umalloc(), or NULL
+ * @v new_size Requested size
+ * @ret new_ptr Allocated memory, or NULL
+ *
+ * Calling urealloc() with a new size of zero is a valid way to free a
+ * memory block.
+ */
+static void * uheap_realloc ( void *old_ptr, size_t new_size ) {
+
+ return heap_realloc ( &uheap, old_ptr, new_size );
+}
+
+PROVIDE_UMALLOC ( uheap, urealloc, uheap_realloc );
diff --git a/src/core/uri.c b/src/core/uri.c
index b82472ef0..9da5e298b 100644
--- a/src/core/uri.c
+++ b/src/core/uri.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/core/utf8.c b/src/core/utf8.c
index 4ee01baf9..871044fec 100644
--- a/src/core/utf8.c
+++ b/src/core/utf8.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <assert.h>
diff --git a/src/core/uuid.c b/src/core/uuid.c
index b6600af71..0f93e9f8f 100644
--- a/src/core/uuid.c
+++ b/src/core/uuid.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdio.h>
diff --git a/src/core/version.c b/src/core/version.c
index 22f444065..75f3160db 100644
--- a/src/core/version.c
+++ b/src/core/version.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -49,16 +50,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH );
/** Build timestamp (generated by linker) */
-extern char _build_timestamp[];
+extern unsigned long ABS_SYMBOL ( _build_timestamp );
/** Build ID (generated by linker) */
-extern char _build_id[];
+extern unsigned long ABS_SYMBOL ( _build_id );
/** Build timestamp */
-unsigned long build_timestamp = ( ( unsigned long ) _build_timestamp );
+unsigned long build_timestamp = ABS_VALUE_INIT ( _build_timestamp );
/** Build ID */
-unsigned long build_id = ( ( unsigned long ) _build_id );
+unsigned long build_id = ABS_VALUE_INIT ( _build_id );
/** Product major version */
const int product_major_version = VERSION_MAJOR;
diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c
index 9d3a97c2d..f6032014a 100644
--- a/src/core/vsprintf.c
+++ b/src/core/vsprintf.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdarg.h>
diff --git a/src/core/wchar.c b/src/core/wchar.c
index 860322820..27a608bf4 100644
--- a/src/core/wchar.c
+++ b/src/core/wchar.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -36,12 +37,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Calculate length of wide-character string
*
* @v string String
- * @ret len Length (excluding terminating NUL)
+ * @v max Maximum length (in wide characters)
+ * @ret len Length (in wide characters, excluding terminating NUL)
*/
-size_t wcslen ( const wchar_t *string ) {
+size_t wcsnlen ( const wchar_t *string, size_t max ) {
size_t len = 0;
- while ( *(string++) )
+ while ( max-- && *(string++) )
len++;
return len;
}
+
+/**
+ * Calculate length of wide-character string
+ *
+ * @v string String
+ * @ret len Length (in wide characters, excluding terminating NUL)
+ */
+size_t wcslen ( const wchar_t *string ) {
+
+ return wcsnlen ( string, ( ( ~( ( size_t ) 0 ) ) /
+ sizeof ( string[0] ) ) );
+}
diff --git a/src/core/xfer.c b/src/core/xfer.c
index 269359e15..5ab303bc7 100644
--- a/src/core/xfer.c
+++ b/src/core/xfer.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <stdlib.h>
diff --git a/src/core/xferbuf.c b/src/core/xferbuf.c
index 240118557..ca3baaab5 100644
--- a/src/core/xferbuf.c
+++ b/src/core/xferbuf.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <string.h>
@@ -51,6 +52,21 @@ static struct profiler xferbuf_read_profiler __profiler =
{ .name = "xferbuf.read" };
/**
+ * Detach data from data transfer buffer
+ *
+ * @v xferbuf Data transfer buffer
+ *
+ * The caller assumes responsibility for eventually freeing the data
+ * previously owned by the data transfer buffer.
+ */
+void xferbuf_detach ( struct xfer_buffer *xferbuf ) {
+
+ xferbuf->data = NULL;
+ xferbuf->len = 0;
+ xferbuf->pos = 0;
+}
+
+/**
* Free data transfer buffer
*
* @v xferbuf Data transfer buffer
@@ -58,8 +74,7 @@ static struct profiler xferbuf_read_profiler __profiler =
void xferbuf_free ( struct xfer_buffer *xferbuf ) {
xferbuf->op->realloc ( xferbuf, 0 );
- xferbuf->len = 0;
- xferbuf->pos = 0;
+ xferbuf_detach ( xferbuf );
}
/**
@@ -109,9 +124,13 @@ int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset,
if ( ( rc = xferbuf_ensure_size ( xferbuf, max_len ) ) != 0 )
return rc;
+ /* Check that buffer is non-void */
+ if ( len && ( ! xferbuf->data ) )
+ return -ENOTTY;
+
/* Copy data to buffer */
profile_start ( &xferbuf_write_profiler );
- xferbuf->op->write ( xferbuf, offset, data, len );
+ memcpy ( ( xferbuf->data + offset ), data, len );
profile_stop ( &xferbuf_write_profiler );
return 0;
@@ -133,9 +152,13 @@ int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset,
( len > ( xferbuf->len - offset ) ) )
return -ENOENT;
+ /* Check that buffer is non-void */
+ if ( len && ( ! xferbuf->data ) )
+ return -ENOTTY;
+
/* Copy data from buffer */
profile_start ( &xferbuf_read_profiler );
- xferbuf->op->read ( xferbuf, offset, data, len );
+ memcpy ( data, ( xferbuf->data + offset ), len );
profile_stop ( &xferbuf_read_profiler );
return 0;
@@ -178,7 +201,7 @@ int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf,
}
/**
- * Reallocate malloc()-based data buffer
+ * Reallocate malloc()-based data transfer buffer
*
* @v xferbuf Data transfer buffer
* @v len New length (or zero to free buffer)
@@ -194,94 +217,76 @@ static int xferbuf_malloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) {
return 0;
}
-/**
- * Write data to malloc()-based data buffer
- *
- * @v xferbuf Data transfer buffer
- * @v offset Starting offset
- * @v data Data to copy
- * @v len Length of data
- */
-static void xferbuf_malloc_write ( struct xfer_buffer *xferbuf, size_t offset,
- const void *data, size_t len ) {
-
- memcpy ( ( xferbuf->data + offset ), data, len );
-}
-
-/**
- * Read data from malloc()-based data buffer
- *
- * @v xferbuf Data transfer buffer
- * @v offset Starting offset
- * @v data Data to read
- * @v len Length of data
- */
-static void xferbuf_malloc_read ( struct xfer_buffer *xferbuf, size_t offset,
- void *data, size_t len ) {
-
- memcpy ( data, ( xferbuf->data + offset ), len );
-}
-
/** malloc()-based data buffer operations */
struct xfer_buffer_operations xferbuf_malloc_operations = {
.realloc = xferbuf_malloc_realloc,
- .write = xferbuf_malloc_write,
- .read = xferbuf_malloc_read,
};
/**
- * Reallocate umalloc()-based data buffer
+ * Reallocate umalloc()-based data transfer buffer
*
* @v xferbuf Data transfer buffer
* @v len New length (or zero to free buffer)
* @ret rc Return status code
*/
static int xferbuf_umalloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) {
- userptr_t *udata = xferbuf->data;
- userptr_t new_udata;
+ void *new_udata;
- new_udata = urealloc ( *udata, len );
+ new_udata = urealloc ( xferbuf->data, len );
if ( ! new_udata )
return -ENOSPC;
- *udata = new_udata;
+ xferbuf->data = new_udata;
return 0;
}
+/** umalloc()-based data buffer operations */
+struct xfer_buffer_operations xferbuf_umalloc_operations = {
+ .realloc = xferbuf_umalloc_realloc,
+};
+
/**
- * Write data to umalloc()-based data buffer
+ * Reallocate fixed-size data transfer buffer
*
* @v xferbuf Data transfer buffer
- * @v offset Starting offset
- * @v data Data to copy
- * @v len Length of data
+ * @v len New length (or zero to free buffer)
+ * @ret rc Return status code
*/
-static void xferbuf_umalloc_write ( struct xfer_buffer *xferbuf, size_t offset,
- const void *data, size_t len ) {
- userptr_t *udata = xferbuf->data;
+static int xferbuf_fixed_realloc ( struct xfer_buffer *xferbuf, size_t len ) {
+
+ /* Refuse to allocate extra space */
+ if ( len > xferbuf->len ) {
+ /* Note that EFI relies upon this error mapping to
+ * EFI_BUFFER_TOO_SMALL.
+ */
+ return -ERANGE;
+ }
- copy_to_user ( *udata, offset, data, len );
+ return 0;
}
+/** Fixed-size data buffer operations */
+struct xfer_buffer_operations xferbuf_fixed_operations = {
+ .realloc = xferbuf_fixed_realloc,
+};
+
/**
- * Read data from umalloc()-based data buffer
+ * Reallocate void data transfer buffer
*
* @v xferbuf Data transfer buffer
- * @v offset Starting offset
- * @v data Data to read
- * @v len Length of data
+ * @v len New length (or zero to free buffer)
+ * @ret rc Return status code
*/
-static void xferbuf_umalloc_read ( struct xfer_buffer *xferbuf, size_t offset,
- void *data, size_t len ) {
- userptr_t *udata = xferbuf->data;
+static int xferbuf_void_realloc ( struct xfer_buffer *xferbuf,
+ size_t len __unused ) {
- copy_from_user ( data, *udata, offset, len );
+ /* Succeed without ever allocating data */
+ assert ( xferbuf->data == NULL );
+ return 0;
}
-/** umalloc()-based data buffer operations */
-struct xfer_buffer_operations xferbuf_umalloc_operations = {
- .realloc = xferbuf_umalloc_realloc,
- .write = xferbuf_umalloc_write,
- .read = xferbuf_umalloc_read,
+/** Void data buffer operations */
+struct xfer_buffer_operations xferbuf_void_operations = {
+ .realloc = xferbuf_void_realloc,
};
/**