Compare commits

..

15 Commits

Author SHA1 Message Date
11932efcf3 [ecm] Use ACPI-provided system-specific MAC address if present
Use the "system MAC address" provided within the DSDT/SSDT if such an
address is available and has not already been assigned to a network
device.

Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-09-09 12:18:20 +01:00
861c3f82e2 [ecm] Expose USB vendor/device information to ecm_fetch_mac()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-09-09 12:18:20 +01:00
0cc4c42f0a [acpi] Allow for extraction of a MAC address from the DSDT/SSDT
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.

Debugged-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-09-09 12:18:00 +01:00
02ec659b73 [acpi] Generalise DSDT/SSDT data extraction logic
Allow for the DSDT/SSDT signature-scanning and value extraction code
to be reused for extracting a pass-through MAC address.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-09-08 14:46:30 +01:00
e09e1142a3 [efi] Record cached ProxyDHCPOFFER and PXEBSACK, if present
Commit cd3de55 ("[efi] Record cached DHCPACK from loaded image's
device handle, if present") added the ability for a chainloaded UEFI
iPXE to reuse an IPv4 address and DHCP options previously obtained by
a built-in PXE stack, without needing to perform a second DHCP
request.

Extend this to also record the cached ProxyDHCPOFFER and PXEBSACK
obtained from the EFI_PXE_BASE_CODE_PROTOCOL instance installed on the
loaded image's device handle, if present.

This allows a chainloaded UEFI iPXE to reuse a boot filename or other
options that were provided via a ProxyDHCP or PXE boot server
mechanism, rather than by standard DHCP.

Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-07-27 13:50:36 +01:00
db6310c3e5 [efi] Use zero for PCI vendor/device IDs when no applicable ID exists
When building an EFI ROM image for which no PCI vendor/device ID is
applicable (e.g. bin-x86_64-efi/ipxe.efirom), the build process will
currently construct a command such as

  ./util/efirom -v -d -c bin-x86_64-efi/ipxe.efidrv \
                         bin-x86_64-efi/ipxe.efirom

which gets interpreted as a vendor ID of "-0xd" (i.e. 0xfff3, after
truncation to 16 bits).

Fix by using an explicit zero ID when no applicable ID exists, as is
already done when constructing BIOS ROM images.

Reported-by: Konstantin Aladyshev <aladyshev22@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-07-26 15:47:47 +01:00
b33cc1efe3 [build] Fix genfsimg to work with FATDIR with space
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-07-26 15:34:33 +01:00
4d180be517 [cloud] Retry DHCP aggressively in AWS EC2
The DHCP service in EC2 has been observed to occasionally stop
responding for bursts of several seconds.  This can easily result in a
failed boot, since the current cloud boot script will attempt DHCP
only once.

Work around this problem by retrying DHCP in a fairly tight cycle
within the cloud boot script, and falling back to a reboot after
several failed DHCP attempts.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-07-20 13:19:15 +01:00
c64dfff0a9 [efi] Match signtool expectations for file alignment
As of commit f1e9e2b ("[efi] Align EFI image sections by page size"),
our SectionAlignment has been increased to 4kB in order to allow for
page-level memory protection to be applied by the UEFI firmware, with
FileAlignment left at 32 bytes.

The PE specification states that the value for FileAlignment "should
be a power of 2 between 512 and 64k, inclusive", and that "if the
SectionAlignment is less than the architecture's page size, then
FileAlignment must match SectionAlignment".

Testing shows that signtool.exe will reject binaries where
FileAlignment is less than 512, unless FileAlignment is equal to
SectionAlignment.  This indicates a somewhat zealous interpretation of
the word "should" in the PE specification.

Work around this interpretation by increasing FileAlignment from 32
bytes to 512 bytes, and add explanatory comments for both
FileAlignment and SectionAlignment.

Debugged-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-07-15 15:45:24 +01:00
8d08300ad9 [libc] Allow for externally-defined LITTLE_ENDIAN and BIG_ENDIAN constants
When building the Linux userspace binaries, the external system
headers may have already defined values for the __LITTLE_ENDIAN and
__BIG_ENDIAN constants.

Fix by retaining the existing values if already defined, since the
actual values of these constants do not matter.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-07-15 14:16:17 +01:00
2690f73096 [uri] Make URI schemes case-insensitive
RFC 3986 section 3.1 defines URI schemes as case-insensitive (though
the canonical form is always lowercase).

Use strcasecmp() rather than strcmp() to allow for case insensitivity
in URI schemes.

Requested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-07-01 16:32:46 +01:00
4aa0375821 [rdc] Add driver for RDC R6040 embedded NIC
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-28 12:32:19 +01:00
5622575c5e [realtek] Work around hardware bug on RTL8211B
The RTL8211B seems to have a bug that prevents the link from coming up
unless the MII_MMD_DATA register is cleared.

The Linux kernel driver applies this workaround (in rtl8211b_resume())
only to the specific RTL8211B PHY model, along with a matching
workaround to set bit 9 of MII_MMD_DATA when suspending the PHY.
Since we have no need to ever suspend the PHY, and since writing a
zero ought to be harmless, we just clear the register unconditionally.

Debugged-by: Nikolay Pertsev <nikolay.p@cos.flag.org>
Tested-by: Nikolay Pertsev <nikolay.p@cos.flag.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-24 12:36:46 +01:00
0688114ea6 [cloud] Show ifstat output after a failed boot attempt
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-23 10:22:38 +01:00
9b6ad2d888 [peerdist] Assume that most recently discovered peer can be reused
The peer discovery time has a significant impact on the overall
PeerDist download speed, since each block requires an individual
discovery attempt.  In most cases, a peer that responds for block N
will turn out to also respond for block N+1.

Assume that the most recently discovered peer (for any block) probably
has a copy of the next block to be discovered, thereby allowing the
peer download attempt to begin immediately.

In the case that this assumption is incorrect, the existing error
recovery path will allow for fallback to newly discovered peers (or to
the origin server).

Suggested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-22 09:45:21 +01:00
26 changed files with 1398 additions and 134 deletions

View File

@ -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] $@"

View File

@ -42,6 +42,69 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** _S5_ signature */
#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
*
@ -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;

View File

@ -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 ) );

View File

@ -3,6 +3,22 @@
echo Amazon EC2 - iPXE boot via user-data
echo CPU: ${cpuvendor} ${cpumodel}
ifstat ||
dhcp ||
set attempt:int8 1
:dhcp_retry
echo DHCP attempt ${attempt}
dhcp --timeout 5000 && goto dhcp_ok ||
ifstat ||
inc attempt
iseq ${attempt} 10 || goto dhcp_retry
:dhcp_fail
echo DHCP failed - rebooting
reboot ||
exit
:dhcp_ok
route ||
chain -ar http://169.254.169.254/latest/user-data
chain -ar http://169.254.169.254/latest/user-data ||
ifstat ||
exit

View File

@ -5,4 +5,5 @@ echo CPU: ${cpuvendor} ${cpumodel}
ifstat ||
dhcp ||
route ||
chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot
chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot ||
ifstat ||

View File

@ -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;
}

154
src/core/acpimac.c Normal file
View File

@ -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;
}

View File

@ -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 */

View File

@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <ipxe/xfer.h>
#include <ipxe/uri.h>
@ -47,7 +48,7 @@ struct uri_opener * xfer_uri_opener ( const char *scheme ) {
struct uri_opener *opener;
for_each_table_entry ( opener, URI_OPENERS ) {
if ( strcmp ( scheme, opener->scheme ) == 0 )
if ( strcasecmp ( scheme, opener->scheme ) == 0 )
return opener;
}
return NULL;

View File

@ -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 usb 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;

View File

@ -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 );

View File

@ -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;

694
src/drivers/net/rdc.c Normal file
View File

@ -0,0 +1,694 @@
/*
* 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 (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
#include <ipxe/iobuf.h>
#include <ipxe/malloc.h>
#include <ipxe/pci.h>
#include "rdc.h"
/** @file
*
* RDC R6040 network driver
*
*/
/******************************************************************************
*
* Device reset
*
******************************************************************************
*/
/**
* Reset hardware
*
* @v rdc RDC device
* @ret rc Return status code
*/
static int rdc_reset ( struct rdc_nic *rdc ) {
unsigned int i;
/* Reset NIC */
writew ( RDC_MCR1_RST, rdc->regs + RDC_MCR1 );
/* Wait for reset to complete */
for ( i = 0 ; i < RDC_RESET_MAX_WAIT_MS ; i++ ) {
/* Check for reset completion */
if ( readw ( rdc->regs + RDC_MCR1 ) & RDC_MCR1_RST ) {
mdelay ( 1 );
continue;
}
/* Reset internal state machine */
writew ( RDC_MACSM_RST, rdc->regs + RDC_MACSM );
writew ( 0, rdc->regs + RDC_MACSM );
mdelay ( RDC_MACSM_RESET_DELAY_MS );
return 0;
}
DBGC ( rdc, "RDC %p timed out waiting for reset\n", rdc );
return -ETIMEDOUT;
}
/******************************************************************************
*
* MII interface
*
******************************************************************************
*/
/**
* Read from MII register
*
* @v mdio MII interface
* @v phy PHY address
* @v reg Register address
* @ret value Data read, or negative error
*/
static int rdc_mii_read ( struct mii_interface *mdio, unsigned int phy,
unsigned int reg ) {
struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio );
uint16_t mmdio;
unsigned int i;
/* Initiate read */
mmdio = ( RDC_MMDIO_MIIRD | RDC_MMDIO_PHYAD ( phy ) |
RDC_MMDIO_REGAD ( reg ) );
writew ( mmdio, rdc->regs + RDC_MMDIO );
/* Wait for read to complete */
for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) {
/* Check for read completion */
if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIRD ) {
udelay ( 1 );
continue;
}
/* Return register value */
return ( readw ( rdc->regs + RDC_MMRD ) );
}
DBGC ( rdc, "RDC %p timed out waiting for MII read\n", rdc );
return -ETIMEDOUT;
}
/**
* Write to MII register
*
* @v mdio MII interface
* @v phy PHY address
* @v reg Register address
* @v data Data to write
* @ret rc Return status code
*/
static int rdc_mii_write ( struct mii_interface *mdio, unsigned int phy,
unsigned int reg, unsigned int data ) {
struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio );
uint16_t mmdio;
unsigned int i;
/* Initiate write */
mmdio = ( RDC_MMDIO_MIIWR | RDC_MMDIO_PHYAD ( phy ) |
RDC_MMDIO_REGAD ( reg ) );
writew ( data, rdc->regs + RDC_MMWD );
writew ( mmdio, rdc->regs + RDC_MMDIO );
/* Wait for write to complete */
for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) {
/* Check for write completion */
if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIWR ) {
udelay ( 1 );
continue;
}
return 0;
}
DBGC ( rdc, "RDC %p timed out waiting for MII write\n", rdc );
return -ETIMEDOUT;
}
/** RDC MII operations */
static struct mii_operations rdc_mii_operations = {
.read = rdc_mii_read,
.write = rdc_mii_write,
};
/******************************************************************************
*
* Link state
*
******************************************************************************
*/
/**
* Initialise PHY
*
* @v rdc RDC device
* @ret rc Return status code
*/
static int rdc_init_phy ( struct rdc_nic *rdc ) {
int rc;
/* Find PHY address */
if ( ( rc = mii_find ( &rdc->mii ) ) != 0 ) {
DBGC ( rdc, "RDC %p could not find PHY address: %s\n",
rdc, strerror ( rc ) );
return rc;
}
/* Reset PHY */
if ( ( rc = mii_reset ( &rdc->mii ) ) != 0 ) {
DBGC ( rdc, "RDC %p could not reset PHY: %s\n",
rdc, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Check link state
*
* @v netdev Network device
* @ret rc Return status code
*/
static int rdc_check_link ( struct net_device *netdev ) {
struct rdc_nic *rdc = netdev->priv;
int rc;
/* Check link state */
if ( ( rc = mii_check_link ( &rdc->mii, netdev ) ) != 0 ) {
DBGC ( rdc, "RDC %p could not check link: %s\n",
rdc, strerror ( rc ) );
return rc;
}
return 0;
}
/******************************************************************************
*
* Network device interface
*
******************************************************************************
*/
/**
* Create descriptor ring
*
* @v rdc RDC device
* @v ring Descriptor ring
* @ret rc Return status code
*/
static int rdc_create_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) {
size_t len = ( ring->count * sizeof ( ring->desc[0] ) );
struct rdc_descriptor *desc;
struct rdc_descriptor *next;
physaddr_t start;
unsigned int i;
/* Allocate descriptor ring */
ring->desc = dma_alloc ( rdc->dma, &ring->map, len, len );
if ( ! ring->desc )
return -ENOMEM;
/* Initialise descriptor ring */
memset ( ring->desc, 0, len );
for ( i = 0 ; i < ring->count ; i++ ) {
desc = &ring->desc[i];
next = &ring->desc[ ( i + 1 ) & ( ring->count - 1 ) ];
desc->next = cpu_to_le32 ( dma ( &ring->map, next ) );
}
/* Program ring address */
start = dma ( &ring->map, ring->desc );
writew ( ( start >> 0 ), ( rdc->regs + ring->reg + RDC_MxDSA_LO ) );
writew ( ( start >> 16 ), ( rdc->regs + ring->reg + RDC_MxDSA_HI ) );
DBGC ( rdc, "RDC %p ring %#02x is at [%08lx,%08lx)\n",
rdc, ring->reg, virt_to_phys ( ring->desc ),
( virt_to_phys ( ring->desc ) + len ) );
return 0;
}
/**
* Destroy descriptor ring
*
* @v rdc RDC device
* @v ring Descriptor ring
*/
static void rdc_destroy_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) {
size_t len = ( ring->count * sizeof ( ring->desc[0] ) );
/* Clear ring address */
writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_LO ) );
writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_HI ) );
/* Free descriptors */
dma_free ( &ring->map, ring->desc, len );
ring->desc = NULL;
/* Reset ring */
ring->prod = 0;
ring->cons = 0;
}
/**
* Refill receive descriptor ring
*
* @v rdc RDC device
*/
static void rdc_refill_rx ( struct rdc_nic *rdc ) {
struct rdc_descriptor *rx;
struct io_buffer *iobuf;
unsigned int rx_idx;
/* Refill ring */
while ( ( rdc->rx.prod - rdc->rx.cons ) < RDC_NUM_RX_DESC ) {
/* Allocate I/O buffer */
iobuf = alloc_rx_iob ( RDC_RX_MAX_LEN, rdc->dma );
if ( ! iobuf ) {
/* Wait for next refill */
break;
}
/* Get next receive descriptor */
rx_idx = ( rdc->rx.prod++ % RDC_NUM_RX_DESC );
rx = &rdc->rx.desc[rx_idx];
/* Populate receive descriptor */
rx->len = cpu_to_le16 ( RDC_RX_MAX_LEN );
rx->addr = cpu_to_le32 ( iob_dma ( iobuf ) );
wmb();
rx->flags = cpu_to_le16 ( RDC_FL_OWNED );
/* Record I/O buffer */
assert ( rdc->rx_iobuf[rx_idx] == NULL );
rdc->rx_iobuf[rx_idx] = iobuf;
DBGC2 ( rdc, "RDC %p RX %d is [%lx,%lx)\n",
rdc, rx_idx, virt_to_phys ( iobuf->data ),
( virt_to_phys ( iobuf->data ) + RDC_RX_MAX_LEN ) );
}
}
/**
* Open network device
*
* @v netdev Network device
* @ret rc Return status code
*/
static int rdc_open ( struct net_device *netdev ) {
struct rdc_nic *rdc = netdev->priv;
int rc;
/* Create transmit descriptor ring */
if ( ( rc = rdc_create_ring ( rdc, &rdc->tx ) ) != 0 )
goto err_create_tx;
/* Create receive descriptor ring */
if ( ( rc = rdc_create_ring ( rdc, &rdc->rx ) ) != 0 )
goto err_create_rx;
/* Program receive buffer length */
writew ( RDC_RX_MAX_LEN, rdc->regs + RDC_MRBSR );
/* Enable transmit and receive */
writew ( ( RDC_MCR0_FD | RDC_MCR0_TXEN | RDC_MCR0_PROMISC |
RDC_MCR0_RXEN ),
rdc->regs + RDC_MCR0 );
/* Enable PHY status polling */
writew ( ( RDC_MPSCCR_EN | RDC_MPSCCR_PHYAD ( rdc->mii.address ) |
RDC_MPSCCR_SLOW ),
rdc->regs + RDC_MPSCCR );
/* Fill receive ring */
rdc_refill_rx ( rdc );
/* Update link state */
rdc_check_link ( netdev );
return 0;
rdc_destroy_ring ( rdc, &rdc->rx );
err_create_rx:
rdc_destroy_ring ( rdc, &rdc->tx );
err_create_tx:
return rc;
}
/**
* Close network device
*
* @v netdev Network device
*/
static void rdc_close ( struct net_device *netdev ) {
struct rdc_nic *rdc = netdev->priv;
unsigned int i;
/* Disable NIC */
writew ( 0, rdc->regs + RDC_MCR0 );
/* Destroy receive descriptor ring */
rdc_destroy_ring ( rdc, &rdc->rx );
/* Discard any unused receive buffers */
for ( i = 0 ; i < RDC_NUM_RX_DESC ; i++ ) {
if ( rdc->rx_iobuf[i] )
free_rx_iob ( rdc->rx_iobuf[i] );
rdc->rx_iobuf[i] = NULL;
}
/* Destroy transmit descriptor ring */
rdc_destroy_ring ( rdc, &rdc->tx );
}
/**
* Transmit packet
*
* @v netdev Network device
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int rdc_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
struct rdc_nic *rdc = netdev->priv;
struct rdc_descriptor *tx;
unsigned int tx_idx;
int rc;
/* Get next transmit descriptor */
if ( ( rdc->tx.prod - rdc->tx.cons ) >= RDC_NUM_TX_DESC ) {
DBGC ( rdc, "RDC %p out of transmit descriptors\n", rdc );
return -ENOBUFS;
}
tx_idx = ( rdc->tx.prod % RDC_NUM_TX_DESC );
tx = &rdc->tx.desc[tx_idx];
/* Pad to minimum length */
iob_pad ( iobuf, ETH_ZLEN );
/* Map I/O buffer */
if ( ( rc = iob_map_tx ( iobuf, rdc->dma ) ) != 0 )
return rc;
/* Update producer index */
rdc->tx.prod++;
/* Populate transmit descriptor */
tx->len = cpu_to_le16 ( iob_len ( iobuf ) );
tx->addr = cpu_to_le32 ( iob_dma ( iobuf ) );
wmb();
tx->flags = cpu_to_le16 ( RDC_FL_OWNED );
wmb();
/* Notify card that there are packets ready to transmit */
writew ( RDC_MTPR_TM2TX, rdc->regs + RDC_MTPR );
return 0;
}
/**
* Poll for completed packets
*
* @v netdev Network device
*/
static void rdc_poll_tx ( struct net_device *netdev ) {
struct rdc_nic *rdc = netdev->priv;
struct rdc_descriptor *tx;
unsigned int tx_idx;
/* Check for completed packets */
while ( rdc->tx.cons != rdc->tx.prod ) {
/* Get next transmit descriptor */
tx_idx = ( rdc->tx.cons % RDC_NUM_TX_DESC );
tx = &rdc->tx.desc[tx_idx];
/* Stop if descriptor is still in use */
if ( tx->flags & cpu_to_le16 ( RDC_FL_OWNED ) )
return;
DBGC2 ( rdc, "RDC %p TX %d complete\n", rdc, tx_idx );
/* Complete transmit descriptor */
rdc->tx.cons++;
netdev_tx_complete_next ( netdev );
}
}
/**
* Poll for received packets
*
* @v netdev Network device
*/
static void rdc_poll_rx ( struct net_device *netdev ) {
struct rdc_nic *rdc = netdev->priv;
struct rdc_descriptor *rx;
struct io_buffer *iobuf;
unsigned int rx_idx;
size_t len;
/* Check for received packets */
while ( rdc->rx.cons != rdc->rx.prod ) {
/* Get next receive descriptor */
rx_idx = ( rdc->rx.cons % RDC_NUM_RX_DESC );
rx = &rdc->rx.desc[rx_idx];
/* Stop if descriptor is still in use */
if ( rx->flags & cpu_to_le16 ( RDC_FL_OWNED ) )
return;
/* Populate I/O buffer */
iobuf = rdc->rx_iobuf[rx_idx];
rdc->rx_iobuf[rx_idx] = NULL;
len = le16_to_cpu ( rx->len );
iob_put ( iobuf, len );
iob_unput ( iobuf, 4 /* strip CRC */ );
/* Hand off to network stack */
if ( rx->flags & cpu_to_le16 ( RDC_FL_OK ) ) {
DBGC2 ( rdc, "RDC %p RX %d complete (length %zd)\n",
rdc, rx_idx, len );
netdev_rx ( netdev, iobuf );
} else {
DBGC2 ( rdc, "RDC %p RX %d error (length %zd, "
"flags %#04x)\n", rdc, rx_idx, len,
le16_to_cpu ( rx->flags ) );
netdev_rx_err ( netdev, iobuf, -EIO );
}
rdc->rx.cons++;
}
}
/**
* Poll for completed and received packets
*
* @v netdev Network device
*/
static void rdc_poll ( struct net_device *netdev ) {
struct rdc_nic *rdc = netdev->priv;
uint16_t misr;
/* Check for (and acknowledge) interrupts */
misr = readw ( rdc->regs + RDC_MISR );
/* Poll for TX completions, if applicable */
if ( misr & RDC_MIRQ_TX )
rdc_poll_tx ( netdev );
/* Poll for RX completions, if applicable */
if ( misr & RDC_MIRQ_RX )
rdc_poll_rx ( netdev );
/* Check link state, if applicable */
if ( misr & RDC_MIRQ_LINK )
rdc_check_link ( netdev );
/* Check for unexpected interrupts */
if ( misr & ~( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX_EARLY |
RDC_MIRQ_RX_EMPTY | RDC_MIRQ_RX ) ) {
DBGC ( rdc, "RDC %p unexpected MISR %#04x\n", rdc, misr );
/* Report as a TX error */
netdev_tx_err ( netdev, NULL, -ENOTSUP );
}
/* Refill receive ring */
rdc_refill_rx ( rdc );
}
/**
* Enable or disable interrupts
*
* @v netdev Network device
* @v enable Interrupts should be enabled
*/
static void rdc_irq ( struct net_device *netdev, int enable ) {
struct rdc_nic *rdc = netdev->priv;
uint16_t mier;
/* Enable/disable interrupts */
mier = ( enable ? ( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX ) : 0 );
writew ( mier, rdc->regs + RDC_MIER );
}
/** RDC network device operations */
static struct net_device_operations rdc_operations = {
.open = rdc_open,
.close = rdc_close,
.transmit = rdc_transmit,
.poll = rdc_poll,
.irq = rdc_irq,
};
/******************************************************************************
*
* PCI interface
*
******************************************************************************
*/
/**
* Probe PCI device
*
* @v pci PCI device
* @ret rc Return status code
*/
static int rdc_probe ( struct pci_device *pci ) {
struct net_device *netdev;
struct rdc_nic *rdc;
union rdc_mac mac;
int rc;
/* Allocate and initialise net device */
netdev = alloc_etherdev ( sizeof ( *rdc ) );
if ( ! netdev ) {
rc = -ENOMEM;
goto err_alloc;
}
netdev_init ( netdev, &rdc_operations );
rdc = netdev->priv;
pci_set_drvdata ( pci, netdev );
netdev->dev = &pci->dev;
memset ( rdc, 0, sizeof ( *rdc ) );
rdc->dma = &pci->dma;
mdio_init ( &rdc->mdio, &rdc_mii_operations );
mii_init ( &rdc->mii, &rdc->mdio, 0 );
rdc_init_ring ( &rdc->tx, RDC_NUM_TX_DESC, RDC_MTDSA );
rdc_init_ring ( &rdc->rx, RDC_NUM_RX_DESC, RDC_MRDSA );
/* Fix up PCI device */
adjust_pci_device ( pci );
/* Map registers */
rdc->regs = pci_ioremap ( pci, pci->membase, RDC_BAR_SIZE );
if ( ! rdc->regs ) {
rc = -ENODEV;
goto err_ioremap;
}
/* Fetch MAC address */
mac.mid[0] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID0 ) );
mac.mid[1] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID1 ) );
mac.mid[2] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID2 ) );
memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN );
/* Reset the NIC */
if ( ( rc = rdc_reset ( rdc ) ) != 0 )
goto err_reset;
/* Initialise PHY */
if ( ( rc = rdc_init_phy ( rdc ) ) != 0 )
goto err_init_phy;
/* Register network device */
if ( ( rc = register_netdev ( netdev ) ) != 0 )
goto err_register_netdev;
/* Set initial link state */
rdc_check_link ( netdev );
return 0;
unregister_netdev ( netdev );
err_register_netdev:
err_init_phy:
rdc_reset ( rdc );
err_reset:
iounmap ( rdc->regs );
err_ioremap:
netdev_nullify ( netdev );
netdev_put ( netdev );
err_alloc:
return rc;
}
/**
* Remove PCI device
*
* @v pci PCI device
*/
static void rdc_remove ( struct pci_device *pci ) {
struct net_device *netdev = pci_get_drvdata ( pci );
struct rdc_nic *rdc = netdev->priv;
/* Unregister network device */
unregister_netdev ( netdev );
/* Reset card */
rdc_reset ( rdc );
/* Free network device */
iounmap ( rdc->regs );
netdev_nullify ( netdev );
netdev_put ( netdev );
}
/** RDC PCI device IDs */
static struct pci_device_id rdc_nics[] = {
PCI_ROM ( 0x17f3, 0x6040, "r6040", "RDC R6040", 0 ),
};
/** RDC PCI driver */
struct pci_driver rdc_driver __pci_driver = {
.ids = rdc_nics,
.id_count = ( sizeof ( rdc_nics ) / sizeof ( rdc_nics[0] ) ),
.probe = rdc_probe,
.remove = rdc_remove,
};

194
src/drivers/net/rdc.h Normal file
View File

@ -0,0 +1,194 @@
#ifndef _RDC_H
#define _RDC_H
/** @file
*
* RDC R6040 network driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/if_ether.h>
#include <ipxe/mii.h>
/** RDC BAR size */
#define RDC_BAR_SIZE 256
/** An RDC descriptor */
struct rdc_descriptor {
/** Flags */
uint16_t flags;
/** Length */
uint16_t len;
/** Address */
uint32_t addr;
/** Next descriptor */
uint32_t next;
/** Reserved */
uint32_t reserved;
} __attribute__ (( packed ));
/** Descriptor is owned by NIC */
#define RDC_FL_OWNED 0x8000
/** Packet OK */
#define RDC_FL_OK 0x4000
/** MAC control register 0 */
#define RDC_MCR0 0x00
#define RDC_MCR0_FD 0x8000 /**< Full duplex */
#define RDC_MCR0_TXEN 0x1000 /**< Transmit enable */
#define RDC_MCR0_PROMISC 0x0020 /**< Promiscuous mode */
#define RDC_MCR0_RXEN 0x0002 /**< Receive enable */
/** MAC control register 1 */
#define RDC_MCR1 0x04
#define RDC_MCR1_RST 0x0001 /**< MAC reset */
/** Maximum time to wait for reset */
#define RDC_RESET_MAX_WAIT_MS 10
/** MAC transmit poll command register */
#define RDC_MTPR 0x14
#define RDC_MTPR_TM2TX 0x0001 /**< Trigger MAC to transmit */
/** MAC receive buffer size register */
#define RDC_MRBSR 0x18
/** MAC MDIO control register */
#define RDC_MMDIO 0x20
#define RDC_MMDIO_MIIWR 0x4000 /**< MDIO write */
#define RDC_MMDIO_MIIRD 0x2000 /**< MDIO read */
#define RDC_MMDIO_PHYAD(x) ( (x) << 8 ) /**< PHY address */
#define RDC_MMDIO_REGAD(x) ( (x) << 0 ) /**< Register address */
/** Maximum time to wait for an MII read or write */
#define RDC_MII_MAX_WAIT_US 2048
/** MAC MDIO read data register */
#define RDC_MMRD 0x24
/** MAC MDIO write data register */
#define RDC_MMWD 0x28
/** MAC transmit descriptor start address */
#define RDC_MTDSA 0x2c
/** MAC receive descriptor start address */
#define RDC_MRDSA 0x34
/** MAC descriptor start address low half */
#define RDC_MxDSA_LO 0x0
/** MAC descriptor start address low half */
#define RDC_MxDSA_HI 0x4
/** MAC interrupt status register */
#define RDC_MISR 0x3c
#define RDC_MIRQ_LINK 0x0200 /**< Link status changed */
#define RDC_MIRQ_TX 0x0010 /**< Transmit complete */
#define RDC_MIRQ_RX_EARLY 0x0008 /**< Receive early interrupt */
#define RDC_MIRQ_RX_EMPTY 0x0002 /**< Receive descriptor unavailable */
#define RDC_MIRQ_RX 0x0001 /**< Receive complete */
/** MAC interrupt enable register */
#define RDC_MIER 0x40
/** MAC address word 0 */
#define RDC_MID0 0x68
/** MAC address word 1 */
#define RDC_MID1 0x6a
/** MAC address word 2 */
#define RDC_MID2 0x6c
/** MAC PHY status change configuration register */
#define RDC_MPSCCR 0x88
#define RDC_MPSCCR_EN 0x8000 /**< PHY status change enable */
#define RDC_MPSCCR_PHYAD(x) ( (x) << 8 ) /**< PHY address */
#define RDC_MPSCCR_SLOW 0x0007 /**< Poll slowly */
/** MAC state machine register */
#define RDC_MACSM 0xac
#define RDC_MACSM_RST 0x0002 /**< Reset state machine */
/** Time to wait after resetting MAC state machine */
#define RDC_MACSM_RESET_DELAY_MS 10
/** A MAC address */
union rdc_mac {
/** Raw bytes */
uint8_t raw[ETH_ALEN];
/** MIDx registers */
uint16_t mid[ ETH_ALEN / 2 ];
};
/** A descriptor ring */
struct rdc_ring {
/** Descriptors */
struct rdc_descriptor *desc;
/** Descriptor ring DMA mapping */
struct dma_mapping map;
/** Producer index */
unsigned int prod;
/** Consumer index */
unsigned int cons;
/** Number of descriptors */
unsigned int count;
/** Start address register 0 */
unsigned int reg;
};
/**
* Initialise descriptor ring
*
* @v ring Descriptor ring
* @v count Number of descriptors
* @v reg Start address register 0
*/
static inline __attribute__ (( always_inline )) void
rdc_init_ring ( struct rdc_ring *ring, unsigned int count, unsigned int reg ) {
ring->count = count;
ring->reg = reg;
}
/** Number of transmit descriptors
*
* This is a policy decision.
*/
#define RDC_NUM_TX_DESC 16
/** Number of receive descriptors
*
* This is a policy decision.
*/
#define RDC_NUM_RX_DESC 8
/** Receive buffer length */
#define RDC_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ )
/** An RDC network card */
struct rdc_nic {
/** Registers */
void *regs;
/** DMA device */
struct dma_device *dma;
/** MII interface */
struct mii_interface mdio;
/** MII device */
struct mii_device mii;
/** Transmit descriptor ring */
struct rdc_ring tx;
/** Receive descriptor ring */
struct rdc_ring rx;
/** Receive I/O buffers */
struct io_buffer *rx_iobuf[RDC_NUM_RX_DESC];
};
#endif /* _RDC_H */

View File

@ -420,6 +420,16 @@ static int realtek_phy_reset ( struct realtek_nic *rtl ) {
*/
}
/* Some cards (e.g. RTL8211B) have a hardware errata that
* requires the MII_MMD_DATA register to be cleared before the
* link will come up.
*/
if ( ( rc = mii_write ( &rtl->mii, MII_MMD_DATA, 0 ) ) != 0 ) {
/* Ignore failures, since the register may not be
* present on all PHYs.
*/
}
/* Restart autonegotiation */
if ( ( rc = mii_restart ( &rtl->mii ) ) != 0 ) {
DBGC ( rtl, "REALTEK %p could not restart MII: %s\n",

View File

@ -8,14 +8,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Little-endian systems should define BYTE_ORDER as LITTLE_ENDIAN.
* This constant is intended to be used only at compile time.
*/
#ifndef __LITTLE_ENDIAN
#define __LITTLE_ENDIAN 0x44332211UL
#endif
/** Constant representing big-endian byte order
*
* Big-endian systems should define BYTE_ORDER as BIG_ENDIAN.
* This constant is intended to be used only at compile time.
*/
#ifndef __BIG_ENDIAN
#define __BIG_ENDIAN 0x11223344UL
#endif
#include "bits/endian.h"

View File

@ -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 ) );

View File

@ -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 */

View File

@ -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 */

View File

@ -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 );
}

View File

@ -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 )
@ -213,6 +214,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_usbblk ( ERRFILE_DRIVER | 0x00ce0000 )
#define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 )
#define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 )
#define ERRFILE_rdc ( ERRFILE_DRIVER | 0x00d10000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )

View File

@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_ONLY );
#define MII_EXPANSION 0x06 /* Expansion register */
#define MII_CTRL1000 0x09 /* 1000BASE-T control */
#define MII_STAT1000 0x0a /* 1000BASE-T status */
#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */
#define MII_MMD_DATA 0x0e /* MMD Access Data Register */
#define MII_ESTATUS 0x0f /* Extended Status */
#define MII_DCOUNTER 0x12 /* Disconnect counter */
#define MII_FCSCOUNTER 0x13 /* False carrier counter */

View File

@ -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 );

View File

@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/tcpip.h>
@ -63,7 +64,7 @@ static struct http_scheme * http_scheme ( struct uri *uri ) {
/* Identify scheme */
for_each_table_entry ( scheme, HTTP_SCHEMES ) {
if ( strcmp ( uri->scheme, scheme->name ) == 0 )
if ( strcasecmp ( uri->scheme, scheme->name ) == 0 )
return scheme;
}

View File

@ -125,7 +125,21 @@
#define R_ARM_V4BX 40
#endif
#define EFI_FILE_ALIGN 0x20
/**
* Alignment of raw data of sections in the image file
*
* Some versions of signtool.exe will spuriously complain if this
* value is less than 512.
*/
#define EFI_FILE_ALIGN 0x200
/**
* Alignment of sections when loaded into memory
*
* This must equal the architecture page size, in order to allow for
* the possibility of the firmware using page-level protection to
* enforce section attributes at runtime.
*/
#define EFI_IMAGE_ALIGN 0x1000
struct elf_file {

View File

@ -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