diff options
Diffstat (limited to 'src/core')
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", + ®s->address_cells ) ) != 0 ) { + regs->address_cells = FDT_DEFAULT_ADDRESS_CELLS; + } + + /* Read #size-cells, if present */ + if ( ( rc = fdt_u32 ( fdt, offset, "#size-cells", + ®s->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, ®s ) ) != 0 ) + return rc; + + /* Get first region address */ + if ( ( rc = fdt_reg_address ( fdt, offset, ®s, 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, ®s ); + + /* 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, ®s ); + 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, ®s, + 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, ®s, + 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, ®ion ); + memmap_update ( ®ion, old, memsz, MEMMAP_FL_USED, "iPXE" ); + memmap_update ( ®ion, virt_to_phys ( hdr ), fdt.len, + MEMMAP_FL_RESERVED, "FDT" ); + next = ( region.max + 1 ); + + /* Dump region descriptor (for debugging) */ + DBGC_MEMMAP ( hdr, ®ion ); + assert ( region.max >= region.min ); + + /* Use highest possible region */ + if ( memmap_is_usable ( ®ion ) && + ( ( 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 ( ®ion, "MEMMAP finding largest usable region\n" ); + *start = 0; + largest = 0; + for_each_memmap ( ®ion, 1 ) { + DBGC_MEMMAP ( ®ion, ®ion ); + if ( ! memmap_is_usable ( ®ion ) ) + continue; + size = memmap_size ( ®ion ); + if ( size > largest ) { + DBGC ( ®ion, "...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 ( ®ion, 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 ( ®ion ); + DBGC ( settings, "MEMMAP %d length %#08llx\n", index, + ( ( unsigned long long ) + memmap_size ( ®ion ) ) ); } + + /* 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, }; /** |
