diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.efi | 3 | ||||
| -rw-r--r-- | src/arch/x86/interface/pcbios/acpipwr.c | 70 | ||||
| -rw-r--r-- | src/arch/x86/interface/pcbios/bios_cachedhcp.c | 3 | ||||
| -rw-r--r-- | src/core/acpi.c | 94 | ||||
| -rw-r--r-- | src/core/acpimac.c | 154 | ||||
| -rw-r--r-- | src/core/cachedhcp.c | 175 | ||||
| -rw-r--r-- | src/drivers/net/ecm.c | 20 | ||||
| -rw-r--r-- | src/drivers/net/ecm.h | 2 | ||||
| -rw-r--r-- | src/drivers/net/ncm.c | 2 | ||||
| -rw-r--r-- | src/hci/readline.c | 24 | ||||
| -rw-r--r-- | src/include/ipxe/acpi.h | 4 | ||||
| -rw-r--r-- | src/include/ipxe/acpimac.h | 14 | ||||
| -rw-r--r-- | src/include/ipxe/cachedhcp.h | 9 | ||||
| -rw-r--r-- | src/include/ipxe/dhcppkt.h | 2 | ||||
| -rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
| -rw-r--r-- | src/interface/efi/efi_cachedhcp.c | 29 | ||||
| -rwxr-xr-x | src/util/genfsimg | 2 |
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 ( ðernet_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 |
