mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-11-03 07:59:06 +08:00
Compare commits
15 Commits
peerdisc_r
...
mac_passth
| Author | SHA1 | Date | |
|---|---|---|---|
| 11932efcf3 | |||
| 861c3f82e2 | |||
| 0cc4c42f0a | |||
| 02ec659b73 | |||
| e09e1142a3 | |||
| db6310c3e5 | |||
| b33cc1efe3 | |||
| 4d180be517 | |||
| c64dfff0a9 | |||
| 8d08300ad9 | |||
| 2690f73096 | |||
| 4aa0375821 | |||
| 5622575c5e | |||
| 0688114ea6 | |||
| 9b6ad2d888 |
@ -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] $@"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 ) );
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 ||
|
||||
|
||||
@ -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
154
src/core/acpimac.c
Normal 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;
|
||||
}
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 ( ð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;
|
||||
|
||||
@ -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 );
|
||||
|
||||
|
||||
@ -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
694
src/drivers/net/rdc.c
Normal 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
194
src/drivers/net/rdc.h
Normal 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 */
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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 ) );
|
||||
|
||||
14
src/include/ipxe/acpimac.h
Normal file
14
src/include/ipxe/acpimac.h
Normal 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 */
|
||||
@ -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 */
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
|
||||
@ -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 )
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user