summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorSimon Rettberg2022-05-11 10:41:01 +0200
committerSimon Rettberg2022-05-11 10:41:01 +0200
commita12e3c379cf2e5946c7316259ef46736cdd5f222 (patch)
tree49638dad528a4490e293ea4a0f87e39ce862a75b /src/core
parentLocal UEFI disk boot support (diff)
parent[cloud] Allow aws-import script to run on Python 3.6 (diff)
downloadipxe-a12e3c379cf2e5946c7316259ef46736cdd5f222.tar.gz
ipxe-a12e3c379cf2e5946c7316259ef46736cdd5f222.tar.xz
ipxe-a12e3c379cf2e5946c7316259ef46736cdd5f222.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src/core')
-rw-r--r--src/core/acpi.c22
-rw-r--r--src/core/acpi_settings.c2
-rw-r--r--src/core/acpimac.c153
-rw-r--r--src/core/dynkeymap.c131
-rw-r--r--src/core/fbcon.c5
-rw-r--r--src/core/image.c7
-rw-r--r--src/core/keymap.c131
-rw-r--r--src/core/settings.c33
-rw-r--r--src/core/uri.c144
-rw-r--r--src/core/utf8.c137
-rw-r--r--src/core/version.c30
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;