diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/acpi.c | 22 | ||||
| -rw-r--r-- | src/core/acpi_settings.c | 2 | ||||
| -rw-r--r-- | src/core/acpimac.c | 153 | ||||
| -rw-r--r-- | src/core/dynkeymap.c | 131 | ||||
| -rw-r--r-- | src/core/fbcon.c | 5 | ||||
| -rw-r--r-- | src/core/image.c | 7 | ||||
| -rw-r--r-- | src/core/keymap.c | 131 | ||||
| -rw-r--r-- | src/core/settings.c | 33 | ||||
| -rw-r--r-- | src/core/uri.c | 144 | ||||
| -rw-r--r-- | src/core/utf8.c | 137 | ||||
| -rw-r--r-- | src/core/version.c | 30 |
11 files changed, 695 insertions, 100 deletions
diff --git a/src/core/acpi.c b/src/core/acpi.c index aa486da93..526bf8555 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -38,6 +38,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Colour for debug messages */ #define colour FADT_SIGNATURE +/** ACPI table finder + * + * May be overridden at link time to inject tables for testing. + */ +typeof ( acpi_find ) *acpi_finder __attribute__ (( weak )) = acpi_find; + /****************************************************************************** * * Utility functions @@ -83,6 +89,18 @@ void acpi_fix_checksum ( struct acpi_header *acpi ) { } /** + * Locate ACPI table + * + * @v signature Requested table signature + * @v index Requested index of table with this signature + * @ret table Table, or UNULL if not found + */ +userptr_t acpi_table ( uint32_t signature, unsigned int index ) { + + return ( *acpi_finder ) ( signature, index ); +} + +/** * Locate ACPI table via RSDT * * @v signature Requested table signature @@ -230,7 +248,7 @@ int acpi_extract ( uint32_t signature, void *data, int rc; /* Try DSDT first */ - fadt = acpi_find ( FADT_SIGNATURE, 0 ); + fadt = acpi_table ( FADT_SIGNATURE, 0 ); if ( fadt ) { copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); dsdt = phys_to_user ( fadtab.dsdt ); @@ -241,7 +259,7 @@ int acpi_extract ( uint32_t signature, void *data, /* Try all SSDTs */ for ( i = 0 ; ; i++ ) { - ssdt = acpi_find ( SSDT_SIGNATURE, i ); + ssdt = acpi_table ( SSDT_SIGNATURE, i ); if ( ! ssdt ) break; if ( ( rc = acpi_zsdt ( ssdt, signature, data, diff --git a/src/core/acpi_settings.c b/src/core/acpi_settings.c index 7ba2e979f..b9e2b7f61 100644 --- a/src/core/acpi_settings.c +++ b/src/core/acpi_settings.c @@ -88,7 +88,7 @@ static int acpi_settings_fetch ( struct settings *settings, acpi_name ( tag_signature ), tag_index, tag_offset, tag_len ); /* Locate ACPI table */ - table = acpi_find ( tag_signature, tag_index ); + table = acpi_table ( tag_signature, tag_index ); if ( ! table ) return -ENOENT; diff --git a/src/core/acpimac.c b/src/core/acpimac.c index 1cc8220b1..5920480dd 100644 --- a/src/core/acpimac.c +++ b/src/core/acpimac.c @@ -46,11 +46,79 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** MACA signature */ #define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' ) -/** Maximum number of bytes to skip after AMAC/MACA signature +/** RTMA signature */ +#define RTMA_SIGNATURE ACPI_SIGNATURE ( 'R', 'T', 'M', 'A' ) + +/** Maximum number of bytes to skip after ACPI signature * * This is entirely empirical. */ -#define AUXMAC_MAX_SKIP 8 +#define ACPIMAC_MAX_SKIP 8 + +/** An ACPI MAC extraction mechanism */ +struct acpimac_extractor { + /** Prefix string */ + const char *prefix; + /** Encoded MAC length */ + size_t len; + /** Decode MAC + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ + int ( * decode ) ( const char *mac, uint8_t *hw_addr ); +}; + +/** + * Decode Base16-encoded MAC address + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ +static int acpimac_decode_base16 ( const char *mac, uint8_t *hw_addr ) { + int len; + int rc; + + /* Attempt to base16-decode MAC address */ + len = base16_decode ( mac, hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + DBGC ( colour, "ACPI could not decode base16 MAC \"%s\": %s\n", + mac, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Decode raw MAC address + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ +static int acpimac_decode_raw ( const char *mac, uint8_t *hw_addr ) { + + memcpy ( hw_addr, mac, ETH_ALEN ); + return 0; +} + +/** "_AUXMAC_" extraction mechanism */ +static struct acpimac_extractor acpimac_auxmac = { + .prefix = "_AUXMAC_#", + .len = ( ETH_ALEN * 2 ), + .decode = acpimac_decode_base16, +}; + +/** "_RTXMAC_" extraction mechanism */ +static struct acpimac_extractor acpimac_rtxmac = { + .prefix = "_RTXMAC_#", + .len = ETH_ALEN, + .decode = acpimac_decode_raw, +}; /** * Extract MAC address from DSDT/SSDT @@ -59,6 +127,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v len Length of DSDT/SSDT * @v offset Offset of signature within DSDT/SSDT * @v data Data buffer + * @v extractor ACPI MAC address extractor * @ret rc Return status code * * Some vendors provide a "system MAC address" within the DSDT/SSDT, @@ -72,51 +141,44 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * string that appears shortly after an "AMAC" or "MACA" signature. * This should work for most implementations encountered in practice. */ -static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset, - void *data ) { - static const char prefix[9] = "_AUXMAC_#"; +static int acpimac_extract ( userptr_t 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; - char auxmac[ sizeof ( prefix ) /* "_AUXMAC_#" */ + - ( ETH_ALEN * 2 ) /* MAC */ + 1 /* "#" */ + 1 /* NUL */ ]; - char *mac = &auxmac[ sizeof ( prefix ) ]; - int decoded_len; + char buf[ prefix_len + extractor->len + 1 /* "#" */ + 1 /* NUL */ ]; + char *mac = &buf[prefix_len]; int rc; /* Skip signature and at least one tag byte */ offset += ( 4 /* signature */ + 1 /* tag byte */ ); - /* Scan for "_AUXMAC_#.....#" close to signature */ + /* Scan for suitable string close to signature */ for ( skip = 0 ; - ( ( skip < AUXMAC_MAX_SKIP ) && - ( offset + skip + sizeof ( auxmac ) ) < len ) ; + ( ( skip < ACPIMAC_MAX_SKIP ) && + ( offset + skip + sizeof ( buf ) ) <= len ) ; skip++ ) { /* Read value */ - copy_from_user ( auxmac, zsdt, ( offset + skip ), - sizeof ( auxmac ) ); + copy_from_user ( buf, zsdt, ( offset + skip ), + sizeof ( buf ) ); /* Check for expected format */ - if ( memcmp ( auxmac, prefix, sizeof ( prefix ) ) != 0 ) + if ( memcmp ( buf, extractor->prefix, prefix_len ) != 0 ) continue; - if ( auxmac[ sizeof ( auxmac ) - 2 ] != '#' ) + if ( buf[ sizeof ( buf ) - 2 ] != '#' ) continue; - if ( auxmac[ sizeof ( auxmac ) - 1 ] != '\0' ) + if ( buf[ sizeof ( buf ) - 1 ] != '\0' ) continue; - DBGC ( colour, "ACPI found MAC string \"%s\"\n", auxmac ); + DBGC ( colour, "ACPI found MAC:\n" ); + DBGC_HDA ( colour, ( offset + skip ), buf, sizeof ( buf ) ); /* Terminate MAC address string */ - mac = &auxmac[ sizeof ( prefix ) ]; - mac[ ETH_ALEN * 2 ] = '\0'; + mac[extractor->len] = '\0'; /* Decode MAC address */ - decoded_len = base16_decode ( mac, hw_addr, ETH_ALEN ); - if ( decoded_len < 0 ) { - rc = decoded_len; - DBGC ( colour, "ACPI could not decode MAC \"%s\": %s\n", - mac, strerror ( rc ) ); + if ( ( rc = extractor->decode ( mac, hw_addr ) ) != 0 ) return rc; - } /* Check MAC address validity */ if ( ! is_valid_ether_addr ( hw_addr ) ) { @@ -132,6 +194,36 @@ static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset, } /** + * Extract "_AUXMAC_" MAC address from DSDT/SSDT + * + * @v zsdt DSDT or SSDT + * @v len Length of DSDT/SSDT + * @v offset Offset of signature within DSDT/SSDT + * @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 ) { + + return acpimac_extract ( zsdt, len, offset, data, &acpimac_auxmac ); +} + +/** + * Extract "_RTXMAC_" MAC address from DSDT/SSDT + * + * @v zsdt DSDT or SSDT + * @v len Length of DSDT/SSDT + * @v offset Offset of signature within DSDT/SSDT + * @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 ) { + + return acpimac_extract ( zsdt, len, offset, data, &acpimac_rtxmac ); +} + +/** * Extract MAC address from DSDT/SSDT * * @v hw_addr MAC address to fill in @@ -142,12 +234,17 @@ int acpi_mac ( uint8_t *hw_addr ) { /* Look for an "AMAC" address */ if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr, - acpi_extract_mac ) ) == 0 ) + acpimac_extract_auxmac ) ) == 0 ) return 0; /* Look for a "MACA" address */ if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr, - acpi_extract_mac ) ) == 0 ) + acpimac_extract_auxmac ) ) == 0 ) + return 0; + + /* Look for a "RTMA" address */ + if ( ( rc = acpi_extract ( RTMA_SIGNATURE, hw_addr, + acpimac_extract_rtxmac ) ) == 0 ) return 0; return -ENOENT; diff --git a/src/core/dynkeymap.c b/src/core/dynkeymap.c new file mode 100644 index 000000000..2f7c49937 --- /dev/null +++ b/src/core/dynkeymap.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 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 + * + * Dynamic keyboard mappings + * + */ + +#include <stdlib.h> +#include <errno.h> +#include <ipxe/settings.h> +#include <ipxe/keymap.h> + +/** + * Require a keyboard map + * + * @v name Keyboard map name + */ +#define REQUIRE_KEYMAP( name ) REQUIRE_OBJECT ( keymap_ ## name ) + +/** Keyboard map setting */ +const struct setting keymap_setting __setting ( SETTING_MISC, keymap ) = { + .name = "keymap", + .description = "Keyboard map", + .type = &setting_type_string, +}; + +/** + * Apply keyboard map settings + * + * @ret rc Return status code + */ +static int keymap_apply ( void ) { + struct keymap *keymap; + char *name; + int rc; + + /* Fetch keyboard map name */ + fetch_string_setting_copy ( NULL, &keymap_setting, &name ); + + /* Identify keyboard map */ + if ( name ) { + /* Identify named keyboard map */ + keymap = keymap_find ( name ); + if ( ! keymap ) { + DBGC ( &keymap_setting, "KEYMAP could not identify " + "\"%s\"\n", name ); + rc = -ENOENT; + goto err_unknown; + } + } else { + /* Use default keyboard map */ + keymap = NULL; + } + + /* Set keyboard map */ + keymap_set ( keymap ); + + /* Success */ + rc = 0; + + err_unknown: + free ( name ); + return rc; +} + +/** Keyboard map setting applicator */ +struct settings_applicator keymap_applicator __settings_applicator = { + .apply = keymap_apply, +}; + +/* Provide virtual "dynamic" keyboard map for linker */ +PROVIDE_SYMBOL ( obj_keymap_dynamic ); + +/* Drag in keyboard maps via keymap_setting */ +REQUIRING_SYMBOL ( keymap_setting ); + +/* Require all known keyboard maps */ +REQUIRE_KEYMAP ( al ); +REQUIRE_KEYMAP ( by ); +REQUIRE_KEYMAP ( cf ); +REQUIRE_KEYMAP ( cz ); +REQUIRE_KEYMAP ( de ); +REQUIRE_KEYMAP ( dk ); +REQUIRE_KEYMAP ( es ); +REQUIRE_KEYMAP ( et ); +REQUIRE_KEYMAP ( fi ); +REQUIRE_KEYMAP ( fr ); +REQUIRE_KEYMAP ( gr ); +REQUIRE_KEYMAP ( hu ); +REQUIRE_KEYMAP ( il ); +REQUIRE_KEYMAP ( it ); +REQUIRE_KEYMAP ( lt ); +REQUIRE_KEYMAP ( mk ); +REQUIRE_KEYMAP ( mt ); +REQUIRE_KEYMAP ( nl ); +REQUIRE_KEYMAP ( no ); +REQUIRE_KEYMAP ( no_latin1 ); +REQUIRE_KEYMAP ( pl ); +REQUIRE_KEYMAP ( pt ); +REQUIRE_KEYMAP ( ro ); +REQUIRE_KEYMAP ( ru ); +REQUIRE_KEYMAP ( se ); +REQUIRE_KEYMAP ( sg ); +REQUIRE_KEYMAP ( sr_latin ); +REQUIRE_KEYMAP ( ua ); +REQUIRE_KEYMAP ( uk ); +REQUIRE_KEYMAP ( us ); diff --git a/src/core/fbcon.c b/src/core/fbcon.c index f64bab7ce..056b164cd 100644 --- a/src/core/fbcon.c +++ b/src/core/fbcon.c @@ -446,6 +446,11 @@ void fbcon_putchar ( struct fbcon *fbcon, int character ) { if ( character < 0 ) return; + /* Accumulate Unicode characters */ + character = utf8_accumulate ( &fbcon->utf8, character ); + if ( character == 0 ) + return; + /* Handle control characters */ switch ( character ) { case '\r': diff --git a/src/core/image.c b/src/core/image.c index ce8cf868b..3e236ca60 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -338,9 +338,12 @@ int image_exec ( struct image *image ) { /* Sanity check */ assert ( image->flags & IMAGE_REGISTERED ); - /* Switch current working directory to be that of the image itself */ + /* Switch current working directory to be that of the image + * itself, if applicable + */ old_cwuri = uri_get ( cwuri ); - churi ( image->uri ); + if ( image->uri ) + churi ( image->uri ); /* Preserve record of any currently-running image */ saved_current_image = current_image; diff --git a/src/core/keymap.c b/src/core/keymap.c new file mode 100644 index 000000000..36db7bd4c --- /dev/null +++ b/src/core/keymap.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 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 <string.h> +#include <ctype.h> +#include <ipxe/keys.h> +#include <ipxe/keymap.h> + +/** @file + * + * Keyboard mappings + * + */ + +/** ASCII character mask */ +#define ASCII_MASK 0x7f + +/** Control character mask */ +#define CTRL_MASK 0x1f + +/** Upper case character mask */ +#define UPPER_MASK 0x5f + +/** Case toggle bit */ +#define CASE_TOGGLE ( ASCII_MASK & ~UPPER_MASK ) + +/** Default keyboard mapping */ +static TABLE_START ( keymap_start, KEYMAP ); + +/** Current keyboard mapping */ +static struct keymap *keymap_current = keymap_start; + +/** + * Remap a key + * + * @v character Character read from console + * @ret mapped Mapped character + */ +unsigned int key_remap ( unsigned int character ) { + struct keymap *keymap = keymap_current; + unsigned int mapped = ( character & KEYMAP_MASK ); + struct keymap_key *key; + + /* Invert case before remapping if applicable */ + if ( ( character & KEYMAP_CAPSLOCK_UNDO ) && isalpha ( mapped ) ) + mapped ^= CASE_TOGGLE; + + /* Select remapping table */ + key = ( ( character & KEYMAP_ALTGR ) ? keymap->altgr : keymap->basic ); + + /* Remap via table */ + for ( ; key->from ; key++ ) { + if ( mapped == key->from ) { + mapped = key->to; + break; + } + } + + /* Handle Ctrl-<key> and CapsLock */ + if ( isalpha ( mapped ) ) { + if ( character & KEYMAP_CTRL ) { + mapped &= CTRL_MASK; + } else if ( character & KEYMAP_CAPSLOCK ) { + mapped ^= CASE_TOGGLE; + } + } + + /* Clear flags */ + mapped &= ASCII_MASK; + + DBGC2 ( &keymap_current, "KEYMAP mapped %04x => %02x\n", + character, mapped ); + return mapped; +} + +/** + * Find keyboard map by name + * + * @v name Keyboard map name + * @ret keymap Keyboard map, or NULL if not found + */ +struct keymap * keymap_find ( const char *name ) { + struct keymap *keymap; + + /* Find matching keyboard map */ + for_each_table_entry ( keymap, KEYMAP ) { + if ( strcmp ( keymap->name, name ) == 0 ) + return keymap; + } + + return NULL; +} + +/** + * Set keyboard map + * + * @v keymap Keyboard map, or NULL to use default + */ +void keymap_set ( struct keymap *keymap ) { + + /* Use default keymap if none specified */ + if ( ! keymap ) + keymap = keymap_start; + + /* Set new keyboard map */ + if ( keymap != keymap_current ) + DBGC ( &keymap_current, "KEYMAP using \"%s\"\n", keymap->name ); + keymap_current = keymap; +} diff --git a/src/core/settings.c b/src/core/settings.c index d6ab6b202..1037b06a9 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -412,9 +412,8 @@ struct settings * find_settings ( const char *name ) { /** * Apply all settings * - * @ret rc Return status code */ -static int apply_settings ( void ) { +static void apply_settings ( void ) { struct settings_applicator *applicator; int rc; @@ -423,11 +422,9 @@ static int apply_settings ( void ) { if ( ( rc = applicator->apply() ) != 0 ) { DBG ( "Could not apply settings using applicator " "%p: %s\n", applicator, strerror ( rc ) ); - return rc; + /* Continue to apply remaining settings */ } } - - return 0; } /** @@ -645,8 +642,7 @@ int store_setting ( struct settings *settings, const struct setting *setting, */ for ( ; settings ; settings = settings->parent ) { if ( settings == &settings_root ) { - if ( ( rc = apply_settings() ) != 0 ) - return rc; + apply_settings(); break; } } @@ -2246,7 +2242,7 @@ const struct setting_type setting_type_base64 __setting_type = { }; /** - * Format UUID setting value + * Format UUID/GUID setting value * * @v type Setting type * @v raw Raw setting value @@ -2255,17 +2251,24 @@ const struct setting_type setting_type_base64 __setting_type = { * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_uuid_setting ( const struct setting_type *type __unused, +static int format_uuid_setting ( const struct setting_type *type, const void *raw, size_t raw_len, char *buf, size_t len ) { - const union uuid *uuid = raw; + union uuid uuid; /* Range check */ - if ( raw_len != sizeof ( *uuid ) ) + if ( raw_len != sizeof ( uuid ) ) return -ERANGE; + /* Copy value */ + memcpy ( &uuid, raw, sizeof ( uuid ) ); + + /* Mangle GUID byte ordering */ + if ( type == &setting_type_guid ) + uuid_mangle ( &uuid ); + /* Format value */ - return snprintf ( buf, len, "%s", uuid_ntoa ( uuid ) ); + return snprintf ( buf, len, "%s", uuid_ntoa ( &uuid ) ); } /** UUID setting type */ @@ -2274,6 +2277,12 @@ const struct setting_type setting_type_uuid __setting_type = { .format = format_uuid_setting, }; +/** GUID setting type */ +const struct setting_type setting_type_guid __setting_type = { + .name = "guid", + .format = format_uuid_setting, +}; + /** * Format PCI bus:dev.fn setting value * diff --git a/src/core/uri.c b/src/core/uri.c index e9e512ab4..b82472ef0 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -79,12 +79,10 @@ size_t uri_decode ( const char *encoded, void *buf, size_t len ) { /** * Decode URI field in-place * - * @v uri URI - * @v field URI field index + * @v encoded Encoded field, or NULL */ -static void uri_decode_inplace ( struct uri *uri, unsigned int field ) { - const char *encoded = uri_field ( uri, field ); - char *decoded = ( ( char * ) encoded ); +static void uri_decode_inplace ( char *encoded ) { + char *decoded = encoded; size_t len; /* Do nothing if field is not present */ @@ -150,7 +148,7 @@ static int uri_character_escaped ( char c, unsigned int field ) { * parser but for any other URI parsers (e.g. HTTP query * string parsers, which care about '=' and '&'). */ - static const char *escaped[URI_FIELDS] = { + static const char *escaped[URI_EPATH] = { /* Scheme or default: escape everything */ [URI_SCHEME] = "/#:@?=&", /* Opaque part: escape characters which would affect @@ -172,20 +170,21 @@ static int uri_character_escaped ( char c, unsigned int field ) { * appears within paths. */ [URI_PATH] = "#:@?", - /* Query: escape everything except '/', which - * sometimes appears within queries. - */ - [URI_QUERY] = "#:@?", - /* Fragment: escape everything */ - [URI_FRAGMENT] = "/#:@?", }; - return ( /* Always escape non-printing characters and whitespace */ - ( ! isprint ( c ) ) || ( c == ' ' ) || - /* Always escape '%' */ - ( c == '%' ) || - /* Escape field-specific characters */ - strchr ( escaped[field], c ) ); + /* Always escape non-printing characters and whitespace */ + if ( ( ! isprint ( c ) ) || ( c == ' ' ) ) + return 1; + + /* Escape nothing else in already-escaped fields */ + if ( field >= URI_EPATH ) + return 0; + + /* Escape '%' and any field-specific characters */ + if ( ( c == '%' ) || strchr ( escaped[field], c ) ) + return 1; + + return 0; } /** @@ -262,10 +261,12 @@ static void uri_dump ( const struct uri *uri ) { DBGC ( uri, " port \"%s\"", uri->port ); if ( uri->path ) DBGC ( uri, " path \"%s\"", uri->path ); - if ( uri->query ) - DBGC ( uri, " query \"%s\"", uri->query ); - if ( uri->fragment ) - DBGC ( uri, " fragment \"%s\"", uri->fragment ); + if ( uri->epath ) + DBGC ( uri, " epath \"%s\"", uri->epath ); + if ( uri->equery ) + DBGC ( uri, " equery \"%s\"", uri->equery ); + if ( uri->efragment ) + DBGC ( uri, " efragment \"%s\"", uri->efragment ); if ( uri->params ) DBGC ( uri, " params \"%s\"", uri->params->name ); } @@ -298,17 +299,19 @@ struct uri * parse_uri ( const char *uri_string ) { char *raw; char *tmp; char *path; + char *epath; char *authority; size_t raw_len; unsigned int field; - /* Allocate space for URI struct and a copy of the string */ + /* Allocate space for URI struct and two copies of the string */ raw_len = ( strlen ( uri_string ) + 1 /* NUL */ ); - uri = zalloc ( sizeof ( *uri ) + raw_len ); + uri = zalloc ( sizeof ( *uri ) + ( 2 * raw_len ) ); if ( ! uri ) return NULL; ref_init ( &uri->refcnt, uri_free ); raw = ( ( ( void * ) uri ) + sizeof ( *uri ) ); + path = ( raw + raw_len ); /* Copy in the raw string */ memcpy ( raw, uri_string, raw_len ); @@ -328,57 +331,62 @@ struct uri * parse_uri ( const char *uri_string ) { /* Chop off the fragment, if it exists */ if ( ( tmp = strchr ( raw, '#' ) ) ) { *(tmp++) = '\0'; - uri->fragment = tmp; + uri->efragment = tmp; } - /* Identify absolute/relative URI */ - if ( ( tmp = strchr ( raw, ':' ) ) ) { + /* Identify absolute URIs */ + epath = raw; + for ( tmp = raw ; ; tmp++ ) { + /* Possible scheme character (for our URI schemes) */ + if ( isalpha ( *tmp ) || ( *tmp == '-' ) || ( *tmp == '_' ) ) + continue; + /* Invalid scheme character or NUL: is a relative URI */ + if ( *tmp != ':' ) + break; /* Absolute URI: identify hierarchical/opaque */ uri->scheme = raw; *(tmp++) = '\0'; if ( *tmp == '/' ) { /* Absolute URI with hierarchical part */ - path = tmp; + epath = tmp; } else { /* Absolute URI with opaque part */ uri->opaque = tmp; - path = NULL; + epath = NULL; } - } else { - /* Relative URI */ - path = raw; + break; } /* If we don't have a path (i.e. we have an absolute URI with * an opaque portion, we're already finished processing */ - if ( ! path ) + if ( ! epath ) goto done; /* Chop off the query, if it exists */ - if ( ( tmp = strchr ( path, '?' ) ) ) { + if ( ( tmp = strchr ( epath, '?' ) ) ) { *(tmp++) = '\0'; - uri->query = tmp; + uri->equery = tmp; } /* If we have no path remaining, then we're already finished * processing. */ - if ( ! path[0] ) + if ( ! epath[0] ) goto done; /* Identify net/absolute/relative path */ - if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) { + if ( uri->scheme && ( strncmp ( epath, "//", 2 ) == 0 ) ) { /* Net path. If this is terminated by the first '/' * of an absolute path, then we have no space for a * terminator after the authority field, so shuffle * the authority down by one byte, overwriting one of * the two slashes. */ - authority = ( path + 2 ); + authority = ( epath + 2 ); if ( ( tmp = strchr ( authority, '/' ) ) ) { /* Shuffle down */ - uri->path = tmp; + uri->epath = tmp; memmove ( ( authority - 1 ), authority, ( tmp - authority ) ); authority--; @@ -386,10 +394,16 @@ struct uri * parse_uri ( const char *uri_string ) { } } else { /* Absolute/relative path */ - uri->path = path; + uri->epath = epath; authority = NULL; } + /* Create copy of path for decoding */ + if ( uri->epath ) { + strcpy ( path, uri->epath ); + uri->path = path; + } + /* If we don't have an authority (i.e. we have a non-net * path), we're already finished processing */ @@ -421,8 +435,8 @@ struct uri * parse_uri ( const char *uri_string ) { done: /* Decode fields in-place */ - for ( field = 0 ; field < URI_FIELDS ; field++ ) - uri_decode_inplace ( uri, field ); + for ( field = 0 ; field < URI_EPATH ; field++ ) + uri_decode_inplace ( ( char * ) uri_field ( uri, field ) ); DBGC ( uri, "URI parsed \"%s\" to", uri_string ); uri_dump ( uri ); @@ -458,8 +472,8 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { static const char prefixes[URI_FIELDS] = { [URI_PASSWORD] = ':', [URI_PORT] = ':', - [URI_QUERY] = '?', - [URI_FRAGMENT] = '#', + [URI_EQUERY] = '?', + [URI_EFRAGMENT] = '#', }; char prefix; size_t used = 0; @@ -480,6 +494,10 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { if ( ! uri_field ( uri, field ) ) continue; + /* Skip path field if encoded path is present */ + if ( ( field == URI_PATH ) && uri->epath ) + continue; + /* Prefix this field, if applicable */ prefix = prefixes[field]; if ( ( field == URI_HOST ) && ( uri->user != NULL ) ) @@ -676,6 +694,7 @@ char * resolve_path ( const char *base_path, struct uri * resolve_uri ( const struct uri *base_uri, struct uri *relative_uri ) { struct uri tmp_uri; + char *tmp_epath = NULL; char *tmp_path = NULL; struct uri *new_uri; @@ -685,20 +704,27 @@ struct uri * resolve_uri ( const struct uri *base_uri, /* Mangle URI */ memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) ); - if ( relative_uri->path ) { - tmp_path = resolve_path ( ( base_uri->path ? - base_uri->path : "/" ), - relative_uri->path ); + if ( relative_uri->epath ) { + tmp_epath = resolve_path ( ( base_uri->epath ? + base_uri->epath : "/" ), + relative_uri->epath ); + if ( ! tmp_epath ) + goto err_epath; + tmp_path = strdup ( tmp_epath ); + if ( ! tmp_path ) + goto err_path; + uri_decode_inplace ( tmp_path ); + tmp_uri.epath = tmp_epath; tmp_uri.path = tmp_path; - tmp_uri.query = relative_uri->query; - tmp_uri.fragment = relative_uri->fragment; + tmp_uri.equery = relative_uri->equery; + tmp_uri.efragment = relative_uri->efragment; tmp_uri.params = relative_uri->params; - } else if ( relative_uri->query ) { - tmp_uri.query = relative_uri->query; - tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->equery ) { + tmp_uri.equery = relative_uri->equery; + tmp_uri.efragment = relative_uri->efragment; tmp_uri.params = relative_uri->params; - } else if ( relative_uri->fragment ) { - tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->efragment ) { + tmp_uri.efragment = relative_uri->efragment; tmp_uri.params = relative_uri->params; } else if ( relative_uri->params ) { tmp_uri.params = relative_uri->params; @@ -707,7 +733,14 @@ struct uri * resolve_uri ( const struct uri *base_uri, /* Create demangled URI */ new_uri = uri_dup ( &tmp_uri ); free ( tmp_path ); + free ( tmp_epath ); return new_uri; + + free ( tmp_path ); + err_path: + free ( tmp_epath ); + err_epath: + return NULL; } /** @@ -746,6 +779,7 @@ static struct uri * tftp_uri ( struct sockaddr *sa_server, if ( asprintf ( &path, "/%s", filename ) < 0 ) goto err_path; tmp.path = path; + tmp.epath = path; /* Demangle URI */ uri = uri_dup ( &tmp ); diff --git a/src/core/utf8.c b/src/core/utf8.c new file mode 100644 index 000000000..4ee01baf9 --- /dev/null +++ b/src/core/utf8.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 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 <stdint.h> +#include <assert.h> +#include <ipxe/utf8.h> + +/** @file + * + * UTF-8 Unicode encoding + * + */ + +/** + * Accumulate Unicode character from UTF-8 byte sequence + * + * @v utf8 UTF-8 accumulator + * @v byte UTF-8 byte + * @ret character Unicode character, or 0 if incomplete + */ +unsigned int utf8_accumulate ( struct utf8_accumulator *utf8, uint8_t byte ) { + static unsigned int min[] = { + UTF8_MIN_TWO, + UTF8_MIN_THREE, + UTF8_MIN_FOUR, + }; + unsigned int shift; + unsigned int len; + uint8_t tmp; + + /* Handle continuation bytes */ + if ( UTF8_IS_CONTINUATION ( byte ) ) { + + /* Fail if this is an unexpected continuation byte */ + if ( utf8->remaining == 0 ) { + DBGC ( utf8, "UTF8 %p unexpected %02x\n", utf8, byte ); + return UTF8_INVALID; + } + + /* Apply continuation byte */ + utf8->character <<= UTF8_CONTINUATION_BITS; + utf8->character |= ( byte & UTF8_CONTINUATION_MASK ); + + /* Return 0 if more continuation bytes are expected */ + if ( --utf8->remaining != 0 ) + return 0; + + /* Fail if sequence is illegal */ + if ( utf8->character < utf8->min ) { + DBGC ( utf8, "UTF8 %p illegal %02x\n", utf8, + utf8->character ); + return UTF8_INVALID; + } + + /* Sanity check */ + assert ( utf8->character != 0 ); + + /* Return completed character */ + DBGC2 ( utf8, "UTF8 %p accumulated %02x\n", + utf8, utf8->character ); + return utf8->character; + } + + /* Reset state and report failure if this is an unexpected + * non-continuation byte. Do not return UTF8_INVALID since + * doing so could cause us to drop a valid ASCII character. + */ + if ( utf8->remaining != 0 ) { + shift = ( utf8->remaining * UTF8_CONTINUATION_BITS ); + DBGC ( utf8, "UTF8 %p unexpected %02x (partial %02x/%02x)\n", + utf8, byte, ( utf8->character << shift ), + ( ( 1 << shift ) - 1 ) ); + utf8->remaining = 0; + } + + /* Handle initial bytes */ + if ( ! UTF8_IS_ASCII ( byte ) ) { + + /* Sanity check */ + assert ( utf8->remaining == 0 ); + + /* Count total number of bytes in sequence */ + tmp = byte; + len = 0; + while ( tmp & UTF8_HIGH_BIT ) { + tmp <<= 1; + len++; + } + + /* Check for illegal length */ + if ( len > UTF8_MAX_LEN ) { + DBGC ( utf8, "UTF8 %p illegal %02x length %d\n", + utf8, byte, len ); + return UTF8_INVALID; + } + + /* Store initial bits of character */ + utf8->character = ( tmp >> len ); + + /* Store number of bytes remaining */ + len--; + utf8->remaining = len; + assert ( utf8->remaining > 0 ); + + /* Store minimum legal value */ + utf8->min = min[ len - 1 ]; + assert ( utf8->min > 0 ); + + /* Await continuation bytes */ + return 0; + } + + /* Handle ASCII bytes */ + return byte; +} diff --git a/src/core/version.c b/src/core/version.c index c984335c2..22f444065 100644 --- a/src/core/version.c +++ b/src/core/version.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <wchar.h> #include <ipxe/features.h> #include <ipxe/version.h> +#include <ipxe/sbat.h> #include <config/general.h> #include <config/branding.h> @@ -92,3 +93,32 @@ const wchar_t build_wname[] = WSTRING ( BUILD_NAME ); /** Copy of build name string within ".prefix" */ const char build_name_prefix[] __attribute__ (( section ( ".prefix.name" ) )) = BUILD_NAME; + +/** SBAT upstream iPXE line + * + * This line represents the security generation of the upstream + * codebase from which this build is derived. + */ +#define SBAT_IPXE \ + SBAT_LINE ( "ipxe", IPXE_SBAT_GENERATION, \ + "iPXE", BUILD_NAME, VERSION, "https://ipxe.org" ) + +/** SBAT local build line + * + * This line states the security generation of the local build, which + * may include non-default features or non-upstreamed modifications. + */ +#if PRODUCT_SBAT_GENERATION +#define SBAT_PRODUCT \ + SBAT_LINE ( "ipxe." PRODUCT_SBAT_NAME, PRODUCT_SBAT_GENERATION, \ + PRODUCT_SHORT_NAME, BUILD_NAME, VERSION, \ + PRODUCT_URI ) +#else +#define SBAT_PRODUCT "" +#endif + +/** SBAT data */ +#define SBAT_DATA SBAT_HEADER "" SBAT_IPXE "" SBAT_PRODUCT + +/** SBAT data (without any NUL terminator) */ +const char sbat[ sizeof ( SBAT_DATA ) - 1 ] __sbat = SBAT_DATA; |
