summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Rettberg2021-10-05 11:32:08 +0200
committerSimon Rettberg2021-10-05 11:32:08 +0200
commit207263d53a2c9ab9fe6e394ca0163eee3552984e (patch)
treef5ec9df709f2223007a5a6e5d505a929f66d4dc4 /src
parentMerge branch 'master' into openslx (diff)
parent[readline] Extend maximum read line length to 1024 characters (diff)
downloadipxe-207263d53a2c9ab9fe6e394ca0163eee3552984e.tar.gz
ipxe-207263d53a2c9ab9fe6e394ca0163eee3552984e.tar.xz
ipxe-207263d53a2c9ab9fe6e394ca0163eee3552984e.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.efi3
-rw-r--r--src/arch/x86/interface/pcbios/acpipwr.c70
-rw-r--r--src/arch/x86/interface/pcbios/bios_cachedhcp.c3
-rw-r--r--src/core/acpi.c94
-rw-r--r--src/core/acpimac.c154
-rw-r--r--src/core/cachedhcp.c175
-rw-r--r--src/drivers/net/ecm.c20
-rw-r--r--src/drivers/net/ecm.h2
-rw-r--r--src/drivers/net/ncm.c2
-rw-r--r--src/hci/readline.c24
-rw-r--r--src/include/ipxe/acpi.h4
-rw-r--r--src/include/ipxe/acpimac.h14
-rw-r--r--src/include/ipxe/cachedhcp.h9
-rw-r--r--src/include/ipxe/dhcppkt.h2
-rw-r--r--src/include/ipxe/errfile.h1
-rw-r--r--src/interface/efi/efi_cachedhcp.c29
-rwxr-xr-xsrc/util/genfsimg2
17 files changed, 472 insertions, 136 deletions
diff --git a/src/Makefile.efi b/src/Makefile.efi
index 11e29dd58..bd479b3da 100644
--- a/src/Makefile.efi
+++ b/src/Makefile.efi
@@ -43,7 +43,8 @@ $(BIN)/%.drv.efi : $(BIN)/%.efidrv
$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM)
$(QM)$(ECHO) " [FINISH] $@"
- $(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) -c $< $@
+ $(Q)$(EFIROM) -v $(firstword $(TGT_PCI_VENDOR) 0) \
+ -d $(firstword $(TGT_PCI_DEVICE) 0) -c $< $@
$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined
$(QM)$(ECHO) " [CAB] $@"
diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c
index dc164c7d5..3dac6b605 100644
--- a/src/arch/x86/interface/pcbios/acpipwr.c
+++ b/src/arch/x86/interface/pcbios/acpipwr.c
@@ -43,6 +43,69 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define S5_SIGNATURE ACPI_SIGNATURE ( '_', 'S', '5', '_' )
/**
+ * Extract \_Sx value 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
+ *
+ * In theory, extracting the \_Sx value from the DSDT/SSDT requires a
+ * full ACPI parser plus some heuristics to work around the various
+ * broken encodings encountered in real ACPI implementations.
+ *
+ * In practice, we can get the same result by scanning through the
+ * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
+ * four bytes, removing any bytes with bit 3 set, and treating
+ * whatever is left as a little-endian value. This is one of the
+ * uglier hacks I have ever implemented, but it's still prettier than
+ * the ACPI specification itself.
+ */
+static int acpi_extract_sx ( userptr_t zsdt, size_t len, size_t offset,
+ void *data ) {
+ unsigned int *sx = data;
+ uint8_t bytes[4];
+ uint8_t *byte;
+
+ /* Skip signature and package header */
+ offset += ( 4 /* signature */ + 3 /* package header */ );
+
+ /* Sanity check */
+ if ( ( offset + sizeof ( bytes ) /* value */ ) > len ) {
+ return -EINVAL;
+ }
+
+ /* Read first four bytes of value */
+ copy_from_user ( bytes, zsdt, offset, sizeof ( bytes ) );
+ DBGC ( colour, "ACPI found \\_Sx containing %02x:%02x:%02x:%02x\n",
+ bytes[0], bytes[1], bytes[2], bytes[3] );
+
+ /* Extract \Sx value. There are three potential encodings
+ * that we might encounter:
+ *
+ * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
+ *
+ * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
+ *
+ * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
+ *
+ * Since <byteprefix> and <dwordprefix> both have bit 3 set,
+ * and valid SLP_TYPx must have bit 3 clear (since SLP_TYPx is
+ * a 3-bit field), we can just skip any bytes with bit 3 set.
+ */
+ byte = bytes;
+ if ( *byte & 0x08 )
+ byte++;
+ *sx = *(byte++);
+ if ( *byte & 0x08 )
+ byte++;
+ *sx |= ( *byte << 8 );
+
+ return 0;
+}
+
+/**
* Power off the computer using ACPI
*
* @ret rc Return status code
@@ -56,7 +119,7 @@ int acpi_poweroff ( void ) {
unsigned int pm1b_cnt;
unsigned int slp_typa;
unsigned int slp_typb;
- int s5;
+ unsigned int s5;
int rc;
/* Locate FADT */
@@ -74,9 +137,8 @@ int acpi_poweroff ( void ) {
pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT );
/* Extract \_S5 from DSDT or any SSDT */
- s5 = acpi_sx ( S5_SIGNATURE );
- if ( s5 < 0 ) {
- rc = s5;
+ if ( ( rc = acpi_extract ( S5_SIGNATURE, &s5,
+ acpi_extract_sx ) ) != 0 ) {
DBGC ( colour, "ACPI could not extract \\_S5: %s\n",
strerror ( rc ) );
return rc;
diff --git a/src/arch/x86/interface/pcbios/bios_cachedhcp.c b/src/arch/x86/interface/pcbios/bios_cachedhcp.c
index 3d38699f7..277c40d6f 100644
--- a/src/arch/x86/interface/pcbios/bios_cachedhcp.c
+++ b/src/arch/x86/interface/pcbios/bios_cachedhcp.c
@@ -59,7 +59,8 @@ static void cachedhcp_init ( void ) {
}
/* Record cached DHCPACK */
- if ( ( rc = cachedhcp_record ( phys_to_user ( cached_dhcpack_phys ),
+ if ( ( rc = cachedhcp_record ( &cached_dhcpack,
+ phys_to_user ( cached_dhcpack_phys ),
sizeof ( BOOTPLAYER_t ) ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",
strerror ( rc ) );
diff --git a/src/core/acpi.c b/src/core/acpi.c
index 52eb63a04..aa486da93 100644
--- a/src/core/acpi.c
+++ b/src/core/acpi.c
@@ -169,33 +169,22 @@ userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index ) {
}
/**
- * Extract \_Sx value from DSDT/SSDT
+ * Extract value from DSDT/SSDT
*
* @v zsdt DSDT or SSDT
* @v signature Signature (e.g. "_S5_")
- * @ret sx \_Sx value, or negative error
- *
- * In theory, extracting the \_Sx value from the DSDT/SSDT requires a
- * full ACPI parser plus some heuristics to work around the various
- * broken encodings encountered in real ACPI implementations.
- *
- * In practice, we can get the same result by scanning through the
- * DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
- * four bytes, removing any bytes with bit 3 set, and treating
- * whatever is left as a little-endian value. This is one of the
- * uglier hacks I have ever implemented, but it's still prettier than
- * the ACPI specification itself.
+ * @v data Data buffer
+ * @v extract Extraction method
+ * @ret rc Return status code
*/
-static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
+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;
- union {
- uint32_t dword;
- uint8_t byte[4];
- } buf;
+ uint32_t buf;
size_t offset;
size_t len;
- unsigned int sx;
- uint8_t *byte;
+ int rc;
/* Read table header */
copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
@@ -203,75 +192,51 @@ static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
/* Locate signature */
for ( offset = sizeof ( acpi ) ;
- ( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
- + sizeof ( buf ) /* value */ ) < len ) ;
+ ( ( offset + sizeof ( buf ) /* signature */ ) < len ) ;
offset++ ) {
/* Check signature */
copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
- if ( buf.dword != cpu_to_le32 ( signature ) )
+ 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 ),
offset );
- offset += sizeof ( buf );
-
- /* Read first four bytes of value */
- copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
- sizeof ( buf ) );
- DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
- "%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
- acpi_name ( signature ), buf.byte[0], buf.byte[1],
- buf.byte[2], buf.byte[3] );
-
- /* Extract \Sx value. There are three potential
- * encodings that we might encounter:
- *
- * - SLP_TYPa, SLP_TYPb, rsvd, rsvd
- *
- * - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
- *
- * - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
- *
- * Since <byteprefix> and <dwordprefix> both have bit
- * 3 set, and valid SLP_TYPx must have bit 3 clear
- * (since SLP_TYPx is a 3-bit field), we can just skip
- * any bytes with bit 3 set.
- */
- byte = &buf.byte[0];
- if ( *byte & 0x08 )
- byte++;
- sx = *(byte++);
- if ( *byte & 0x08 )
- byte++;
- sx |= ( *byte << 8 );
- return sx;
+
+ /* Attempt to extract data */
+ if ( ( rc = extract ( zsdt, len, offset, data ) ) == 0 )
+ return 0;
}
return -ENOENT;
}
/**
- * Extract \_Sx value from DSDT/SSDT
+ * Extract value from DSDT/SSDT
*
* @v signature Signature (e.g. "_S5_")
- * @ret sx \_Sx value, or negative error
+ * @v data Data buffer
+ * @v extract Extraction method
+ * @ret rc Return status code
*/
-int acpi_sx ( uint32_t signature ) {
+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;
unsigned int i;
- int sx;
+ int rc;
/* Try DSDT first */
fadt = acpi_find ( FADT_SIGNATURE, 0 );
if ( fadt ) {
copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
dsdt = phys_to_user ( fadtab.dsdt );
- if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 )
- return sx;
+ if ( ( rc = acpi_zsdt ( dsdt, signature, data,
+ extract ) ) == 0 )
+ return 0;
}
/* Try all SSDTs */
@@ -279,11 +244,12 @@ int acpi_sx ( uint32_t signature ) {
ssdt = acpi_find ( SSDT_SIGNATURE, i );
if ( ! ssdt )
break;
- if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
- return sx;
+ if ( ( rc = acpi_zsdt ( ssdt, signature, data,
+ extract ) ) == 0 )
+ return 0;
}
- DBGC ( colour, "ACPI could not find \\_Sx \"%s\"\n",
+ DBGC ( colour, "ACPI could not find \"%s\"\n",
acpi_name ( signature ) );
return -ENOENT;
}
diff --git a/src/core/acpimac.c b/src/core/acpimac.c
new file mode 100644
index 000000000..1cc8220b1
--- /dev/null
+++ b/src/core/acpimac.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 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 <errno.h>
+#include <ipxe/acpi.h>
+#include <ipxe/base16.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/acpimac.h>
+
+/** @file
+ *
+ * ACPI MAC address
+ *
+ */
+
+/** Colour for debug messages */
+#define colour FADT_SIGNATURE
+
+/** AMAC signature */
+#define AMAC_SIGNATURE ACPI_SIGNATURE ( 'A', 'M', 'A', 'C' )
+
+/** MACA signature */
+#define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' )
+
+/** Maximum number of bytes to skip after AMAC/MACA signature
+ *
+ * This is entirely empirical.
+ */
+#define AUXMAC_MAX_SKIP 8
+
+/**
+ * Extract 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
+ *
+ * Some vendors provide a "system MAC address" within the DSDT/SSDT,
+ * to be used to override the MAC address for a USB docking station.
+ *
+ * A full implementation would require an ACPI bytecode interpreter,
+ * since at least one OEM allows the MAC address to be constructed by
+ * executable ACPI bytecode (rather than a fixed data structure).
+ *
+ * We instead attempt to extract a plausible-looking "_AUXMAC_#.....#"
+ * 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_#";
+ 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;
+ int rc;
+
+ /* Skip signature and at least one tag byte */
+ offset += ( 4 /* signature */ + 1 /* tag byte */ );
+
+ /* Scan for "_AUXMAC_#.....#" close to signature */
+ for ( skip = 0 ;
+ ( ( skip < AUXMAC_MAX_SKIP ) &&
+ ( offset + skip + sizeof ( auxmac ) ) < len ) ;
+ skip++ ) {
+
+ /* Read value */
+ copy_from_user ( auxmac, zsdt, ( offset + skip ),
+ sizeof ( auxmac ) );
+
+ /* Check for expected format */
+ if ( memcmp ( auxmac, prefix, sizeof ( prefix ) ) != 0 )
+ continue;
+ if ( auxmac[ sizeof ( auxmac ) - 2 ] != '#' )
+ continue;
+ if ( auxmac[ sizeof ( auxmac ) - 1 ] != '\0' )
+ continue;
+ DBGC ( colour, "ACPI found MAC string \"%s\"\n", auxmac );
+
+ /* Terminate MAC address string */
+ mac = &auxmac[ sizeof ( prefix ) ];
+ mac[ ETH_ALEN * 2 ] = '\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 ) );
+ return rc;
+ }
+
+ /* Check MAC address validity */
+ if ( ! is_valid_ether_addr ( hw_addr ) ) {
+ DBGC ( colour, "ACPI has invalid MAC %s\n",
+ eth_ntoa ( hw_addr ) );
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * Extract MAC address from DSDT/SSDT
+ *
+ * @v hw_addr MAC address to fill in
+ * @ret rc Return status code
+ */
+int acpi_mac ( uint8_t *hw_addr ) {
+ int rc;
+
+ /* Look for an "AMAC" address */
+ if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr,
+ acpi_extract_mac ) ) == 0 )
+ return 0;
+
+ /* Look for a "MACA" address */
+ if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr,
+ acpi_extract_mac ) ) == 0 )
+ return 0;
+
+ return -ENOENT;
+}
diff --git a/src/core/cachedhcp.c b/src/core/cachedhcp.c
index 0e7da4bf2..2fa9b0c73 100644
--- a/src/core/cachedhcp.c
+++ b/src/core/cachedhcp.c
@@ -37,29 +37,121 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+/** A cached DHCP packet */
+struct cached_dhcp_packet {
+ /** Settings block name */
+ const char *name;
+ /** DHCP packet (if any) */
+ struct dhcp_packet *dhcppkt;
+};
+
/** Cached DHCPACK */
-static struct dhcp_packet *cached_dhcpack;
+struct cached_dhcp_packet cached_dhcpack = {
+ .name = DHCP_SETTINGS_NAME,
+};
+
+/** Cached ProxyDHCPOFFER */
+struct cached_dhcp_packet cached_proxydhcp = {
+ .name = PROXYDHCP_SETTINGS_NAME,
+};
+
+/** Cached PXEBSACK */
+struct cached_dhcp_packet cached_pxebs = {
+ .name = PXEBS_SETTINGS_NAME,
+};
+
+/** List of cached DHCP packets */
+static struct cached_dhcp_packet *cached_packets[] = {
+ &cached_dhcpack,
+ &cached_proxydhcp,
+ &cached_pxebs,
+};
/** Colour for debug messages */
#define colour &cached_dhcpack
/**
- * Record cached DHCPACK
+ * Free cached DHCP packet
*
+ * @v cache Cached DHCP packet
+ */
+static void cachedhcp_free ( struct cached_dhcp_packet *cache ) {
+
+ dhcppkt_put ( cache->dhcppkt );
+ cache->dhcppkt = NULL;
+}
+
+/**
+ * Apply cached DHCP packet settings
+ *
+ * @v cache Cached DHCP packet
+ * @v netdev Network device, or NULL
+ * @ret rc Return status code
+ */
+static int cachedhcp_apply ( struct cached_dhcp_packet *cache,
+ struct net_device *netdev ) {
+ struct settings *settings;
+ int rc;
+
+ /* Do nothing if cache is empty */
+ if ( ! cache->dhcppkt )
+ return 0;
+
+ /* Do nothing unless cached packet's MAC address matches this
+ * network device, if specified.
+ */
+ if ( netdev ) {
+ if ( memcmp ( netdev->ll_addr, cache->dhcppkt->dhcphdr->chaddr,
+ netdev->ll_protocol->ll_addr_len ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP %s does not match %s\n",
+ cache->name, netdev->name );
+ return 0;
+ }
+ DBGC ( colour, "CACHEDHCP %s is for %s\n",
+ cache->name, netdev->name );
+ }
+
+ /* Select appropriate parent settings block */
+ settings = ( netdev ? netdev_settings ( netdev ) : NULL );
+
+ /* Register settings */
+ if ( ( rc = register_settings ( &cache->dhcppkt->settings, settings,
+ cache->name ) ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP %s could not register settings: %s\n",
+ cache->name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Free cached DHCP packet */
+ cachedhcp_free ( cache );
+
+ return 0;
+}
+
+/**
+ * Record cached DHCP packet
+ *
+ * @v cache Cached DHCP packet
* @v data DHCPACK packet buffer
* @v max_len Maximum possible length
* @ret rc Return status code
*/
-int cachedhcp_record ( userptr_t data, size_t max_len ) {
+int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
+ size_t max_len ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
+ unsigned int i;
size_t len;
+ /* Free any existing cached packet */
+ cachedhcp_free ( cache );
+
/* Allocate and populate DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
if ( ! dhcppkt ) {
- DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
+ DBGC ( colour, "CACHEDHCP %s could not allocate copy\n",
+ cache->name );
return -ENOMEM;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
@@ -80,10 +172,26 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
dhcppkt_init ( dhcppkt, dhcphdr, len );
- /* Store as cached DHCPACK, and mark original copy as consumed */
- DBGC ( colour, "CACHEDHCP found cached DHCPACK at %#08lx+%#zx/%#zx\n",
+ /* Discard duplicate packets, since some PXE stacks (including
+ * iPXE itself) will report the DHCPACK packet as the PXEBSACK
+ * if no separate PXEBSACK exists.
+ */
+ for ( i = 0 ; i < ( sizeof ( cached_packets ) /
+ sizeof ( cached_packets[0] ) ) ; i++ ) {
+ tmp = cached_packets[i]->dhcppkt;
+ if ( tmp && ( dhcppkt_len ( tmp ) == len ) &&
+ ( memcmp ( tmp->dhcphdr, dhcppkt->dhcphdr, len ) == 0 ) ) {
+ DBGC ( colour, "CACHEDHCP %s duplicates %s\n",
+ cache->name, cached_packets[i]->name );
+ dhcppkt_put ( dhcppkt );
+ return -EEXIST;
+ }
+ }
+
+ /* Store as cached packet */
+ DBGC ( colour, "CACHEDHCP %s at %#08lx+%#zx/%#zx\n", cache->name,
user_to_phys ( data, 0 ), len, max_len );
- cached_dhcpack = dhcppkt;
+ cache->dhcppkt = dhcppkt;
return 0;
}
@@ -94,14 +202,20 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
*/
static void cachedhcp_startup ( void ) {
- /* If cached DHCP packet was not claimed by any network device
- * during startup, then free it.
- */
- if ( cached_dhcpack ) {
- DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
- dhcppkt_put ( cached_dhcpack );
- cached_dhcpack = NULL;
+ /* Apply cached ProxyDHCPOFFER, if any */
+ cachedhcp_apply ( &cached_proxydhcp, NULL );
+
+ /* Apply cached PXEBSACK, if any */
+ cachedhcp_apply ( &cached_pxebs, NULL );
+
+ /* Free any remaining cached packets */
+ if ( cached_dhcpack.dhcppkt ) {
+ DBGC ( colour, "CACHEDHCP %s unclaimed\n",
+ cached_dhcpack.name );
}
+ cachedhcp_free ( &cached_dhcpack );
+ cachedhcp_free ( &cached_proxydhcp );
+ cachedhcp_free ( &cached_pxebs );
}
/** Cached DHCPACK startup function */
@@ -117,38 +231,9 @@ struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
* @ret rc Return status code
*/
static int cachedhcp_probe ( struct net_device *netdev ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
- int rc;
- /* Do nothing unless we have a cached DHCPACK */
- if ( ! cached_dhcpack )
- return 0;
-
- /* Do nothing unless cached DHCPACK's MAC address matches this
- * network device.
- */
- if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr,
- ll_protocol->ll_addr_len ) != 0 ) {
- DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n",
- netdev->name );
- return 0;
- }
- DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name );
-
- /* Register as DHCP settings for this network device */
- if ( ( rc = register_settings ( &cached_dhcpack->settings,
- netdev_settings ( netdev ),
- DHCP_SETTINGS_NAME ) ) != 0 ) {
- DBGC ( colour, "CACHEDHCP could not register settings: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- /* Claim cached DHCPACK */
- dhcppkt_put ( cached_dhcpack );
- cached_dhcpack = NULL;
-
- return 0;
+ /* Apply cached DHCPACK to network device, if applicable */
+ return cachedhcp_apply ( &cached_dhcpack, netdev );
}
/** Cached DHCP packet network device driver */
diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c
index 847a45b85..826b3b16d 100644
--- a/src/drivers/net/ecm.c
+++ b/src/drivers/net/ecm.c
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/if_ether.h>
#include <ipxe/base16.h>
#include <ipxe/profile.h>
+#include <ipxe/acpimac.h>
#include <ipxe/usb.h>
#include "ecm.h"
@@ -81,17 +82,26 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
/**
* Get hardware MAC address
*
- * @v usb USB device
+ * @v func USB function
* @v desc Ethernet functional descriptor
* @v hw_addr Hardware address to fill in
* @ret rc Return status code
*/
-int ecm_fetch_mac ( struct usb_device *usb,
+int ecm_fetch_mac ( struct usb_function *func,
struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) {
+ struct usb_device *usb = func->usb;
char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ];
int len;
int rc;
+ /* Use system-specific MAC address, if present and not already used */
+ if ( ( ( rc = acpi_mac ( hw_addr ) ) == 0 ) &&
+ ! find_netdev_by_ll_addr ( &ethernet_protocol, hw_addr ) ) {
+ DBGC ( usb, "USB %s using system-specific MAC %s\n",
+ func->name, eth_ntoa ( hw_addr ) );
+ return 0;
+ }
+
/* Fetch MAC address string */
len = usb_get_string_descriptor ( usb, desc->mac, 0, buf,
sizeof ( buf ) );
@@ -103,7 +113,7 @@ int ecm_fetch_mac ( struct usb_device *usb,
/* Sanity check */
if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) {
DBGC ( usb, "USB %s has invalid ECM MAC \"%s\"\n",
- usb->name, buf );
+ func->name, buf );
return -EINVAL;
}
@@ -112,7 +122,7 @@ int ecm_fetch_mac ( struct usb_device *usb,
if ( len < 0 ) {
rc = len;
DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n",
- usb->name, buf, strerror ( rc ) );
+ func->name, buf, strerror ( rc ) );
return rc;
}
@@ -464,7 +474,7 @@ static int ecm_probe ( struct usb_function *func,
}
/* Fetch MAC address */
- if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
+ if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) {
DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n",
ecm, strerror ( rc ) );
goto err_fetch_mac;
diff --git a/src/drivers/net/ecm.h b/src/drivers/net/ecm.h
index 83d324bdc..0ad3ddb95 100644
--- a/src/drivers/net/ecm.h
+++ b/src/drivers/net/ecm.h
@@ -86,7 +86,7 @@ struct ecm_device {
extern struct ecm_ethernet_descriptor *
ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
struct usb_interface_descriptor *interface );
-extern int ecm_fetch_mac ( struct usb_device *usb,
+extern int ecm_fetch_mac ( struct usb_function *func,
struct ecm_ethernet_descriptor *desc,
uint8_t *hw_addr );
diff --git a/src/drivers/net/ncm.c b/src/drivers/net/ncm.c
index cc07a4388..1e8088d76 100644
--- a/src/drivers/net/ncm.c
+++ b/src/drivers/net/ncm.c
@@ -598,7 +598,7 @@ static int ncm_probe ( struct usb_function *func,
}
/* Fetch MAC address */
- if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
+ if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) {
DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n",
ncm, strerror ( rc ) );
goto err_fetch_mac;
diff --git a/src/hci/readline.c b/src/hci/readline.c
index 852c4503a..ecc72d43f 100644
--- a/src/hci/readline.c
+++ b/src/hci/readline.c
@@ -38,7 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
-#define READLINE_MAX 256
+#define READLINE_MAX 1024
/**
* Synchronise console with edited string
@@ -258,8 +258,8 @@ void history_free ( struct readline_history *history ) {
int readline_history ( const char *prompt, const char *prefill,
struct readline_history *history, unsigned long timeout,
char **line ) {
- char buf[READLINE_MAX];
struct edit_string string;
+ char *buf;
int key;
int move_by;
const char *new_string;
@@ -275,10 +275,14 @@ int readline_history ( const char *prompt, const char *prefill,
/* Ensure cursor is visible */
printf ( "\033[?25h" );
- /* Initialise editable string */
+ /* Allocate buffer and initialise editable string */
+ buf = zalloc ( READLINE_MAX );
+ if ( ! buf ) {
+ rc = -ENOMEM;
+ goto done;
+ }
memset ( &string, 0, sizeof ( string ) );
- init_editstring ( &string, buf, sizeof ( buf ) );
- buf[0] = '\0';
+ init_editstring ( &string, buf, READLINE_MAX );
/* Prefill string, if applicable */
if ( prefill ) {
@@ -303,8 +307,13 @@ int readline_history ( const char *prompt, const char *prefill,
switch ( key ) {
case CR:
case LF:
- *line = strdup ( buf );
- rc = ( ( *line ) ? 0 : -ENOMEM );
+ /* Shrink string (ignoring failures) */
+ *line = realloc ( buf,
+ ( strlen ( buf ) + 1 /* NUL */ ) );
+ if ( ! *line )
+ *line = buf;
+ buf = NULL;
+ rc = 0;
goto done;
case CTRL_C:
rc = -ECANCELED;
@@ -332,6 +341,7 @@ int readline_history ( const char *prompt, const char *prefill,
done:
putchar ( '\n' );
+ free ( buf );
if ( history ) {
if ( *line && (*line)[0] )
history_append ( history, *line );
diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h
index 81ef7ff76..7df3ec21c 100644
--- a/src/include/ipxe/acpi.h
+++ b/src/include/ipxe/acpi.h
@@ -387,7 +387,9 @@ acpi_describe ( struct interface *interface );
typeof ( struct acpi_descriptor * ( object_type ) )
extern void acpi_fix_checksum ( struct acpi_header *acpi );
-extern int acpi_sx ( uint32_t signature );
+extern int acpi_extract ( uint32_t signature, void *data,
+ int ( * extract ) ( userptr_t zsdt, size_t len,
+ size_t offset, void *data ) );
extern void acpi_add ( struct acpi_descriptor *desc );
extern void acpi_del ( struct acpi_descriptor *desc );
extern int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) );
diff --git a/src/include/ipxe/acpimac.h b/src/include/ipxe/acpimac.h
new file mode 100644
index 000000000..de673eb28
--- /dev/null
+++ b/src/include/ipxe/acpimac.h
@@ -0,0 +1,14 @@
+#ifndef _IPXE_ACPIMAC_H
+#define _IPXE_ACPIMAC_H
+
+/** @file
+ *
+ * ACPI MAC address
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern int acpi_mac ( uint8_t *hw_addr );
+
+#endif /* _IPXE_ACPIMAC_H */
diff --git a/src/include/ipxe/cachedhcp.h b/src/include/ipxe/cachedhcp.h
index 7765c6455..39ce74543 100644
--- a/src/include/ipxe/cachedhcp.h
+++ b/src/include/ipxe/cachedhcp.h
@@ -12,6 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <ipxe/uaccess.h>
-extern int cachedhcp_record ( userptr_t data, size_t max_len );
+struct cached_dhcp_packet;
+
+extern struct cached_dhcp_packet cached_dhcpack;
+extern struct cached_dhcp_packet cached_proxydhcp;
+extern struct cached_dhcp_packet cached_pxebs;
+
+extern int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
+ size_t max_len );
#endif /* _IPXE_CACHEDHCP_H */
diff --git a/src/include/ipxe/dhcppkt.h b/src/include/ipxe/dhcppkt.h
index f13dfc93d..86075960a 100644
--- a/src/include/ipxe/dhcppkt.h
+++ b/src/include/ipxe/dhcppkt.h
@@ -56,7 +56,7 @@ dhcppkt_put ( struct dhcp_packet *dhcppkt ) {
* @v dhcppkt DHCP packet
* @ret len Used length
*/
-static inline int dhcppkt_len ( struct dhcp_packet *dhcppkt ) {
+static inline size_t dhcppkt_len ( struct dhcp_packet *dhcppkt ) {
return ( offsetof ( struct dhcphdr, options ) +
dhcppkt->options.used_len );
}
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index cf5757874..23e406b62 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -77,6 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_fdt ( ERRFILE_CORE | 0x00250000 )
#define ERRFILE_dma ( ERRFILE_CORE | 0x00260000 )
#define ERRFILE_cachedhcp ( ERRFILE_CORE | 0x00270000 )
+#define ERRFILE_acpimac ( ERRFILE_CORE | 0x00280000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
diff --git a/src/interface/efi/efi_cachedhcp.c b/src/interface/efi/efi_cachedhcp.c
index 14b531d09..1d4b98fd6 100644
--- a/src/interface/efi/efi_cachedhcp.c
+++ b/src/interface/efi/efi_cachedhcp.c
@@ -75,17 +75,40 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
/* Record DHCPACK, if present */
if ( mode->DhcpAckReceived &&
- ( ( rc = cachedhcp_record ( virt_to_user ( &mode->DhcpAck ),
+ ( ( rc = cachedhcp_record ( &cached_dhcpack,
+ virt_to_user ( &mode->DhcpAck ),
sizeof ( mode->DhcpAck ) ) ) != 0 ) ) {
DBGC ( device, "EFI %s could not record DHCPACK: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
- goto err_record;
+ goto err_dhcpack;
+ }
+
+ /* Record ProxyDHCPOFFER, if present */
+ if ( mode->ProxyOfferReceived &&
+ ( ( rc = cachedhcp_record ( &cached_proxydhcp,
+ virt_to_user ( &mode->ProxyOffer ),
+ sizeof ( mode->ProxyOffer ) ) ) != 0)){
+ DBGC ( device, "EFI %s could not record ProxyDHCPOFFER: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_proxydhcp;
+ }
+
+ /* Record PxeBSACK, if present */
+ if ( mode->PxeReplyReceived &&
+ ( ( rc = cachedhcp_record ( &cached_pxebs,
+ virt_to_user ( &mode->PxeReply ),
+ sizeof ( mode->PxeReply ) ) ) != 0)){
+ DBGC ( device, "EFI %s could not record PXEBSACK: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_pxebs;
}
/* Success */
rc = 0;
- err_record:
+ err_pxebs:
+ err_proxydhcp:
+ err_dhcpack:
err_ipv6:
bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
efi_image_handle, NULL );
diff --git a/src/util/genfsimg b/src/util/genfsimg
index 1635a11ac..c13158203 100755
--- a/src/util/genfsimg
+++ b/src/util/genfsimg
@@ -255,7 +255,7 @@ fi
# Create FAT filesystem image, if applicable
#
if [ -n "${FATIMG}" ] ; then
- FATSIZE=$(du -s -k ${FATDIR} | cut -f1)
+ FATSIZE=$(du -s -k "${FATDIR}" | cut -f1)
FATSIZE=$(( FATSIZE + PAD + 256 ))
touch "${FATIMG}"
if [ "${FATSIZE}" -le "1440" ] ; then