mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-11-03 07:59:06 +08:00
Compare commits
26 Commits
bigint_out
...
uri
| Author | SHA1 | Date | |
|---|---|---|---|
| 1844aacc83 | |||
| 85eb961bf9 | |||
| f24a2794e1 | |||
| 2265a65191 | |||
| 05a76acc6d | |||
| 91e147213c | |||
| 0cc4c42f0a | |||
| 02ec659b73 | |||
| e09e1142a3 | |||
| db6310c3e5 | |||
| b33cc1efe3 | |||
| 4d180be517 | |||
| c64dfff0a9 | |||
| 8d08300ad9 | |||
| 2690f73096 | |||
| 4aa0375821 | |||
| 5622575c5e | |||
| 0688114ea6 | |||
| 9b6ad2d888 | |||
| 51c88a4a62 | |||
| bf4ccd4265 | |||
| 3c040ad387 | |||
| 3dd1989ac0 | |||
| 52300ccf98 | |||
| 92807f5759 | |||
| 065dce8d59 |
@ -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] $@"
|
||||
|
||||
@ -1167,7 +1167,8 @@ $(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS)
|
||||
$(Q)$(RM) $(BLIB)
|
||||
$(QM)$(ECHO) " [AR] $@"
|
||||
$(Q)$(AR) rD $@ $(sort $(BLIB_OBJS))
|
||||
$(Q)$(OBJCOPY) --prefix-symbols=$(SYMBOL_PREFIX) $@
|
||||
$(Q)$(OBJCOPY) --enable-deterministic-archives \
|
||||
--prefix-symbols=$(SYMBOL_PREFIX) $@
|
||||
$(Q)$(RANLIB) -D $@
|
||||
blib : $(BLIB)
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -36,7 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
|
||||
static const char base64[64] =
|
||||
static const char base64[ 64 + 1 /* NUL */ ] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -20,11 +20,12 @@ unsigned int console_height = CONSOLE_DEFAULT_HEIGHT;
|
||||
* Write a single character to each console device
|
||||
*
|
||||
* @v character Character to be written
|
||||
* @ret character Character written
|
||||
*
|
||||
* The character is written out to all enabled console devices, using
|
||||
* each device's console_driver::putchar() method.
|
||||
*/
|
||||
void putchar ( int character ) {
|
||||
int putchar ( int character ) {
|
||||
struct console_driver *console;
|
||||
|
||||
/* Automatic LF -> CR,LF translation */
|
||||
@ -37,6 +38,8 @@ void putchar ( int character ) {
|
||||
console->putchar )
|
||||
console->putchar ( character );
|
||||
}
|
||||
|
||||
return character;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
|
||||
131
src/core/uri.c
131
src/core/uri.c
@ -79,12 +79,10 @@ size_t uri_decode ( const char *encoded, void *buf, size_t len ) {
|
||||
/**
|
||||
* Decode URI field in-place
|
||||
*
|
||||
* @v uri URI
|
||||
* @v field URI field index
|
||||
* @v encoded Encoded field, or NULL
|
||||
*/
|
||||
static void uri_decode_inplace ( struct uri *uri, unsigned int field ) {
|
||||
const char *encoded = uri_field ( uri, field );
|
||||
char *decoded = ( ( char * ) encoded );
|
||||
static void uri_decode_inplace ( char *encoded ) {
|
||||
char *decoded = encoded;
|
||||
size_t len;
|
||||
|
||||
/* Do nothing if field is not present */
|
||||
@ -150,7 +148,7 @@ static int uri_character_escaped ( char c, unsigned int field ) {
|
||||
* parser but for any other URI parsers (e.g. HTTP query
|
||||
* string parsers, which care about '=' and '&').
|
||||
*/
|
||||
static const char *escaped[URI_FIELDS] = {
|
||||
static const char *escaped[URI_EPATH] = {
|
||||
/* Scheme or default: escape everything */
|
||||
[URI_SCHEME] = "/#:@?=&",
|
||||
/* Opaque part: escape characters which would affect
|
||||
@ -172,20 +170,21 @@ static int uri_character_escaped ( char c, unsigned int field ) {
|
||||
* appears within paths.
|
||||
*/
|
||||
[URI_PATH] = "#:@?",
|
||||
/* Query: escape everything except '/', which
|
||||
* sometimes appears within queries.
|
||||
*/
|
||||
[URI_QUERY] = "#:@?",
|
||||
/* Fragment: escape everything */
|
||||
[URI_FRAGMENT] = "/#:@?",
|
||||
};
|
||||
|
||||
return ( /* Always escape non-printing characters and whitespace */
|
||||
( ! isprint ( c ) ) || ( c == ' ' ) ||
|
||||
/* Always escape '%' */
|
||||
( c == '%' ) ||
|
||||
/* Escape field-specific characters */
|
||||
strchr ( escaped[field], c ) );
|
||||
/* Always escape non-printing characters and whitespace */
|
||||
if ( ( ! isprint ( c ) ) || ( c == ' ' ) )
|
||||
return 1;
|
||||
|
||||
/* Escape nothing else in already-escaped fields */
|
||||
if ( field >= URI_EPATH )
|
||||
return 0;
|
||||
|
||||
/* Escape '%' and any field-specific characters */
|
||||
if ( ( c == '%' ) || strchr ( escaped[field], c ) )
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,10 +261,12 @@ static void uri_dump ( const struct uri *uri ) {
|
||||
DBGC ( uri, " port \"%s\"", uri->port );
|
||||
if ( uri->path )
|
||||
DBGC ( uri, " path \"%s\"", uri->path );
|
||||
if ( uri->query )
|
||||
DBGC ( uri, " query \"%s\"", uri->query );
|
||||
if ( uri->fragment )
|
||||
DBGC ( uri, " fragment \"%s\"", uri->fragment );
|
||||
if ( uri->epath )
|
||||
DBGC ( uri, " epath \"%s\"", uri->epath );
|
||||
if ( uri->equery )
|
||||
DBGC ( uri, " equery \"%s\"", uri->equery );
|
||||
if ( uri->efragment )
|
||||
DBGC ( uri, " efragment \"%s\"", uri->efragment );
|
||||
if ( uri->params )
|
||||
DBGC ( uri, " params \"%s\"", uri->params->name );
|
||||
}
|
||||
@ -298,17 +299,19 @@ struct uri * parse_uri ( const char *uri_string ) {
|
||||
char *raw;
|
||||
char *tmp;
|
||||
char *path;
|
||||
char *epath;
|
||||
char *authority;
|
||||
size_t raw_len;
|
||||
unsigned int field;
|
||||
|
||||
/* Allocate space for URI struct and a copy of the string */
|
||||
/* Allocate space for URI struct and two copies of the string */
|
||||
raw_len = ( strlen ( uri_string ) + 1 /* NUL */ );
|
||||
uri = zalloc ( sizeof ( *uri ) + raw_len );
|
||||
uri = zalloc ( sizeof ( *uri ) + ( 2 * raw_len ) );
|
||||
if ( ! uri )
|
||||
return NULL;
|
||||
ref_init ( &uri->refcnt, uri_free );
|
||||
raw = ( ( ( void * ) uri ) + sizeof ( *uri ) );
|
||||
path = ( raw + raw_len );
|
||||
|
||||
/* Copy in the raw string */
|
||||
memcpy ( raw, uri_string, raw_len );
|
||||
@ -328,7 +331,7 @@ struct uri * parse_uri ( const char *uri_string ) {
|
||||
/* Chop off the fragment, if it exists */
|
||||
if ( ( tmp = strchr ( raw, '#' ) ) ) {
|
||||
*(tmp++) = '\0';
|
||||
uri->fragment = tmp;
|
||||
uri->efragment = tmp;
|
||||
}
|
||||
|
||||
/* Identify absolute/relative URI */
|
||||
@ -338,47 +341,47 @@ struct uri * parse_uri ( const char *uri_string ) {
|
||||
*(tmp++) = '\0';
|
||||
if ( *tmp == '/' ) {
|
||||
/* Absolute URI with hierarchical part */
|
||||
path = tmp;
|
||||
epath = tmp;
|
||||
} else {
|
||||
/* Absolute URI with opaque part */
|
||||
uri->opaque = tmp;
|
||||
path = NULL;
|
||||
epath = NULL;
|
||||
}
|
||||
} else {
|
||||
/* Relative URI */
|
||||
path = raw;
|
||||
epath = raw;
|
||||
}
|
||||
|
||||
/* If we don't have a path (i.e. we have an absolute URI with
|
||||
* an opaque portion, we're already finished processing
|
||||
*/
|
||||
if ( ! path )
|
||||
if ( ! epath )
|
||||
goto done;
|
||||
|
||||
/* Chop off the query, if it exists */
|
||||
if ( ( tmp = strchr ( path, '?' ) ) ) {
|
||||
if ( ( tmp = strchr ( epath, '?' ) ) ) {
|
||||
*(tmp++) = '\0';
|
||||
uri->query = tmp;
|
||||
uri->equery = tmp;
|
||||
}
|
||||
|
||||
/* If we have no path remaining, then we're already finished
|
||||
* processing.
|
||||
*/
|
||||
if ( ! path[0] )
|
||||
if ( ! epath[0] )
|
||||
goto done;
|
||||
|
||||
/* Identify net/absolute/relative path */
|
||||
if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) {
|
||||
if ( uri->scheme && ( strncmp ( epath, "//", 2 ) == 0 ) ) {
|
||||
/* Net path. If this is terminated by the first '/'
|
||||
* of an absolute path, then we have no space for a
|
||||
* terminator after the authority field, so shuffle
|
||||
* the authority down by one byte, overwriting one of
|
||||
* the two slashes.
|
||||
*/
|
||||
authority = ( path + 2 );
|
||||
authority = ( epath + 2 );
|
||||
if ( ( tmp = strchr ( authority, '/' ) ) ) {
|
||||
/* Shuffle down */
|
||||
uri->path = tmp;
|
||||
uri->epath = tmp;
|
||||
memmove ( ( authority - 1 ), authority,
|
||||
( tmp - authority ) );
|
||||
authority--;
|
||||
@ -386,10 +389,16 @@ struct uri * parse_uri ( const char *uri_string ) {
|
||||
}
|
||||
} else {
|
||||
/* Absolute/relative path */
|
||||
uri->path = path;
|
||||
uri->epath = epath;
|
||||
authority = NULL;
|
||||
}
|
||||
|
||||
/* Create copy of path for decoding */
|
||||
if ( uri->epath ) {
|
||||
strcpy ( path, uri->epath );
|
||||
uri->path = path;
|
||||
}
|
||||
|
||||
/* If we don't have an authority (i.e. we have a non-net
|
||||
* path), we're already finished processing
|
||||
*/
|
||||
@ -421,8 +430,8 @@ struct uri * parse_uri ( const char *uri_string ) {
|
||||
|
||||
done:
|
||||
/* Decode fields in-place */
|
||||
for ( field = 0 ; field < URI_FIELDS ; field++ )
|
||||
uri_decode_inplace ( uri, field );
|
||||
for ( field = 0 ; field < URI_EPATH ; field++ )
|
||||
uri_decode_inplace ( ( char * ) uri_field ( uri, field ) );
|
||||
|
||||
DBGC ( uri, "URI parsed \"%s\" to", uri_string );
|
||||
uri_dump ( uri );
|
||||
@ -458,8 +467,8 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) {
|
||||
static const char prefixes[URI_FIELDS] = {
|
||||
[URI_PASSWORD] = ':',
|
||||
[URI_PORT] = ':',
|
||||
[URI_QUERY] = '?',
|
||||
[URI_FRAGMENT] = '#',
|
||||
[URI_EQUERY] = '?',
|
||||
[URI_EFRAGMENT] = '#',
|
||||
};
|
||||
char prefix;
|
||||
size_t used = 0;
|
||||
@ -480,6 +489,10 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) {
|
||||
if ( ! uri_field ( uri, field ) )
|
||||
continue;
|
||||
|
||||
/* Skip path field if encoded path is present */
|
||||
if ( ( field == URI_PATH ) && uri->epath )
|
||||
continue;
|
||||
|
||||
/* Prefix this field, if applicable */
|
||||
prefix = prefixes[field];
|
||||
if ( ( field == URI_HOST ) && ( uri->user != NULL ) )
|
||||
@ -676,6 +689,7 @@ char * resolve_path ( const char *base_path,
|
||||
struct uri * resolve_uri ( const struct uri *base_uri,
|
||||
struct uri *relative_uri ) {
|
||||
struct uri tmp_uri;
|
||||
char *tmp_epath = NULL;
|
||||
char *tmp_path = NULL;
|
||||
struct uri *new_uri;
|
||||
|
||||
@ -685,20 +699,27 @@ struct uri * resolve_uri ( const struct uri *base_uri,
|
||||
|
||||
/* Mangle URI */
|
||||
memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) );
|
||||
if ( relative_uri->path ) {
|
||||
tmp_path = resolve_path ( ( base_uri->path ?
|
||||
base_uri->path : "/" ),
|
||||
relative_uri->path );
|
||||
if ( relative_uri->epath ) {
|
||||
tmp_epath = resolve_path ( ( base_uri->epath ?
|
||||
base_uri->epath : "/" ),
|
||||
relative_uri->epath );
|
||||
if ( ! tmp_epath )
|
||||
goto err_epath;
|
||||
tmp_path = strdup ( tmp_epath );
|
||||
if ( ! tmp_path )
|
||||
goto err_path;
|
||||
uri_decode_inplace ( tmp_path );
|
||||
tmp_uri.epath = tmp_epath;
|
||||
tmp_uri.path = tmp_path;
|
||||
tmp_uri.query = relative_uri->query;
|
||||
tmp_uri.fragment = relative_uri->fragment;
|
||||
tmp_uri.equery = relative_uri->equery;
|
||||
tmp_uri.efragment = relative_uri->efragment;
|
||||
tmp_uri.params = relative_uri->params;
|
||||
} else if ( relative_uri->query ) {
|
||||
tmp_uri.query = relative_uri->query;
|
||||
tmp_uri.fragment = relative_uri->fragment;
|
||||
} else if ( relative_uri->equery ) {
|
||||
tmp_uri.equery = relative_uri->equery;
|
||||
tmp_uri.efragment = relative_uri->efragment;
|
||||
tmp_uri.params = relative_uri->params;
|
||||
} else if ( relative_uri->fragment ) {
|
||||
tmp_uri.fragment = relative_uri->fragment;
|
||||
} else if ( relative_uri->efragment ) {
|
||||
tmp_uri.efragment = relative_uri->efragment;
|
||||
tmp_uri.params = relative_uri->params;
|
||||
} else if ( relative_uri->params ) {
|
||||
tmp_uri.params = relative_uri->params;
|
||||
@ -707,7 +728,14 @@ struct uri * resolve_uri ( const struct uri *base_uri,
|
||||
/* Create demangled URI */
|
||||
new_uri = uri_dup ( &tmp_uri );
|
||||
free ( tmp_path );
|
||||
free ( tmp_epath );
|
||||
return new_uri;
|
||||
|
||||
free ( tmp_path );
|
||||
err_path:
|
||||
free ( tmp_epath );
|
||||
err_epath:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -746,6 +774,7 @@ static struct uri * tftp_uri ( struct sockaddr *sa_server,
|
||||
if ( asprintf ( &path, "/%s", filename ) < 0 )
|
||||
goto err_path;
|
||||
tmp.path = path;
|
||||
tmp.epath = path;
|
||||
|
||||
/* Demangle URI */
|
||||
uri = uri_dup ( &tmp );
|
||||
|
||||
@ -17,37 +17,47 @@
|
||||
#include "ipxe/io.h"
|
||||
#include "ipxe/iomap.h"
|
||||
#include "ipxe/pci.h"
|
||||
#include "ipxe/dma.h"
|
||||
#include "ipxe/reboot.h"
|
||||
#include "ipxe/virtio-pci.h"
|
||||
#include "ipxe/virtio-ring.h"
|
||||
|
||||
static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num)
|
||||
static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num, size_t header_size)
|
||||
{
|
||||
size_t queue_size = PAGE_MASK + vring_size(num);
|
||||
size_t ring_size = PAGE_MASK + vring_size(num);
|
||||
size_t vdata_size = num * sizeof(void *);
|
||||
size_t queue_size = ring_size + vdata_size + header_size;
|
||||
|
||||
vq->queue = zalloc(queue_size + vdata_size);
|
||||
vq->queue = dma_alloc(vq->dma, &vq->map, queue_size, queue_size);
|
||||
if (!vq->queue) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset ( vq->queue, 0, queue_size );
|
||||
vq->queue_size = queue_size;
|
||||
|
||||
/* vdata immediately follows the ring */
|
||||
vq->vdata = (void **)(vq->queue + queue_size);
|
||||
vq->vdata = (void **)(vq->queue + ring_size);
|
||||
|
||||
/* empty header immediately follows vdata */
|
||||
vq->empty_header = (struct virtio_net_hdr_modern *)(vq->queue + ring_size + vdata_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vp_free_vq(struct vring_virtqueue *vq)
|
||||
{
|
||||
if (vq->queue) {
|
||||
free(vq->queue);
|
||||
if (vq->queue && vq->queue_size) {
|
||||
dma_free(&vq->map, vq->queue, vq->queue_size);
|
||||
vq->queue = NULL;
|
||||
vq->vdata = NULL;
|
||||
vq->queue_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int vp_find_vq(unsigned int ioaddr, int queue_index,
|
||||
struct vring_virtqueue *vq)
|
||||
struct vring_virtqueue *vq, struct dma_device *dma_dev,
|
||||
size_t header_size)
|
||||
{
|
||||
struct vring * vr = &vq->vring;
|
||||
u16 num;
|
||||
@ -73,9 +83,10 @@ int vp_find_vq(unsigned int ioaddr, int queue_index,
|
||||
}
|
||||
|
||||
vq->queue_index = queue_index;
|
||||
vq->dma = dma_dev;
|
||||
|
||||
/* initialize the queue */
|
||||
rc = vp_alloc_vq(vq, num);
|
||||
rc = vp_alloc_vq(vq, num, header_size);
|
||||
if (rc) {
|
||||
DBG("VIRTIO-PCI ERROR: failed to allocate queue memory\n");
|
||||
return rc;
|
||||
@ -87,8 +98,7 @@ int vp_find_vq(unsigned int ioaddr, int queue_index,
|
||||
* NOTE: vr->desc is initialized by vring_init()
|
||||
*/
|
||||
|
||||
outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT,
|
||||
ioaddr + VIRTIO_PCI_QUEUE_PFN);
|
||||
outl(dma(&vq->map, vr->desc) >> PAGE_SHIFT, ioaddr + VIRTIO_PCI_QUEUE_PFN);
|
||||
|
||||
return num;
|
||||
}
|
||||
@ -348,7 +358,8 @@ void vpm_notify(struct virtio_pci_modern_device *vdev,
|
||||
}
|
||||
|
||||
int vpm_find_vqs(struct virtio_pci_modern_device *vdev,
|
||||
unsigned nvqs, struct vring_virtqueue *vqs)
|
||||
unsigned nvqs, struct vring_virtqueue *vqs,
|
||||
struct dma_device *dma_dev, size_t header_size)
|
||||
{
|
||||
unsigned i;
|
||||
struct vring_virtqueue *vq;
|
||||
@ -392,11 +403,12 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev,
|
||||
|
||||
vq = &vqs[i];
|
||||
vq->queue_index = i;
|
||||
vq->dma = dma_dev;
|
||||
|
||||
/* get offset of notification word for this vq */
|
||||
off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off));
|
||||
|
||||
err = vp_alloc_vq(vq, size);
|
||||
err = vp_alloc_vq(vq, size, header_size);
|
||||
if (err) {
|
||||
DBG("VIRTIO-PCI %p: failed to allocate queue memory\n", vdev);
|
||||
return err;
|
||||
@ -406,13 +418,16 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev,
|
||||
/* activate the queue */
|
||||
vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size));
|
||||
|
||||
vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.desc),
|
||||
vpm_iowrite64(vdev, &vdev->common,
|
||||
dma(&vq->map, vq->vring.desc),
|
||||
COMMON_OFFSET(queue_desc_lo),
|
||||
COMMON_OFFSET(queue_desc_hi));
|
||||
vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.avail),
|
||||
vpm_iowrite64(vdev, &vdev->common,
|
||||
dma(&vq->map, vq->vring.avail),
|
||||
COMMON_OFFSET(queue_avail_lo),
|
||||
COMMON_OFFSET(queue_avail_hi));
|
||||
vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.used),
|
||||
vpm_iowrite64(vdev, &vdev->common,
|
||||
dma(&vq->map, vq->vring.used),
|
||||
COMMON_OFFSET(queue_used_lo),
|
||||
COMMON_OFFSET(queue_used_hi));
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ void vring_add_buf(struct vring_virtqueue *vq,
|
||||
for (i = head; out; i = vr->desc[i].next, out--) {
|
||||
|
||||
vr->desc[i].flags = VRING_DESC_F_NEXT;
|
||||
vr->desc[i].addr = (u64)virt_to_phys(list->addr);
|
||||
vr->desc[i].addr = list->addr;
|
||||
vr->desc[i].len = list->length;
|
||||
prev = i;
|
||||
list++;
|
||||
@ -106,7 +106,7 @@ void vring_add_buf(struct vring_virtqueue *vq,
|
||||
for ( ; in; i = vr->desc[i].next, in--) {
|
||||
|
||||
vr->desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
|
||||
vr->desc[i].addr = (u64)virt_to_phys(list->addr);
|
||||
vr->desc[i].addr = list->addr;
|
||||
vr->desc[i].len = list->length;
|
||||
prev = i;
|
||||
list++;
|
||||
|
||||
@ -39,6 +39,9 @@ static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data)
|
||||
{
|
||||
u32 status, timeout;
|
||||
|
||||
/* Avoid returning uninitialised data on error */
|
||||
*data = 0xffff;
|
||||
|
||||
/*
|
||||
* Initialize EEPROM access
|
||||
*/
|
||||
|
||||
@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/if_ether.h>
|
||||
#include <ipxe/base16.h>
|
||||
#include <ipxe/profile.h>
|
||||
#include <ipxe/acpimac.h>
|
||||
#include <ipxe/usb.h>
|
||||
#include "ecm.h"
|
||||
|
||||
@ -81,17 +82,26 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
|
||||
/**
|
||||
* Get hardware MAC address
|
||||
*
|
||||
* @v usb USB device
|
||||
* @v func USB function
|
||||
* @v desc Ethernet functional descriptor
|
||||
* @v hw_addr Hardware address to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ecm_fetch_mac ( struct usb_device *usb,
|
||||
int ecm_fetch_mac ( struct usb_function *func,
|
||||
struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) {
|
||||
struct usb_device *usb = func->usb;
|
||||
char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ];
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
/* Use system-specific MAC address, if present and not already used */
|
||||
if ( ( ( rc = acpi_mac ( hw_addr ) ) == 0 ) &&
|
||||
! find_netdev_by_ll_addr ( ðernet_protocol, hw_addr ) ) {
|
||||
DBGC ( usb, "USB %s using system-specific MAC %s\n",
|
||||
func->name, eth_ntoa ( hw_addr ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fetch MAC address string */
|
||||
len = usb_get_string_descriptor ( usb, desc->mac, 0, buf,
|
||||
sizeof ( buf ) );
|
||||
@ -103,7 +113,7 @@ int ecm_fetch_mac ( struct usb_device *usb,
|
||||
/* Sanity check */
|
||||
if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) {
|
||||
DBGC ( usb, "USB %s has invalid ECM MAC \"%s\"\n",
|
||||
usb->name, buf );
|
||||
func->name, buf );
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -112,7 +122,7 @@ int ecm_fetch_mac ( struct usb_device *usb,
|
||||
if ( len < 0 ) {
|
||||
rc = len;
|
||||
DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n",
|
||||
usb->name, buf, strerror ( rc ) );
|
||||
func->name, buf, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -464,7 +474,7 @@ static int ecm_probe ( struct usb_function *func,
|
||||
}
|
||||
|
||||
/* Fetch MAC address */
|
||||
if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
|
||||
if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) {
|
||||
DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n",
|
||||
ecm, strerror ( rc ) );
|
||||
goto err_fetch_mac;
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/iobuf.h>
|
||||
#include <ipxe/netdevice.h>
|
||||
#include <ipxe/pci.h>
|
||||
#include <ipxe/dma.h>
|
||||
#include <ipxe/if_ether.h>
|
||||
#include <ipxe/ethernet.h>
|
||||
#include <ipxe/virtio-pci.h>
|
||||
@ -99,8 +100,9 @@ struct virtnet_nic {
|
||||
/** Pending rx packet count */
|
||||
unsigned int rx_num_iobufs;
|
||||
|
||||
/** Virtio net dummy packet headers */
|
||||
struct virtio_net_hdr_modern empty_header[QUEUE_NB];
|
||||
/** DMA device */
|
||||
struct dma_device *dma;
|
||||
|
||||
};
|
||||
|
||||
/** Add an iobuf to a virtqueue
|
||||
@ -115,7 +117,7 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
|
||||
int vq_idx, struct io_buffer *iobuf ) {
|
||||
struct virtnet_nic *virtnet = netdev->priv;
|
||||
struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
|
||||
struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx];
|
||||
struct virtio_net_hdr_modern *header = vq->empty_header;
|
||||
unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
|
||||
unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
|
||||
size_t header_len = ( virtnet->virtio_version ?
|
||||
@ -132,11 +134,11 @@ static void virtnet_enqueue_iob ( struct net_device *netdev,
|
||||
* to header->flags for received packets. Work around
|
||||
* this by using separate RX and TX headers.
|
||||
*/
|
||||
.addr = ( char* ) header,
|
||||
.addr = dma ( &vq->map, header ),
|
||||
.length = header_len,
|
||||
},
|
||||
{
|
||||
.addr = ( char* ) iobuf->data,
|
||||
.addr = iob_dma ( iobuf ),
|
||||
.length = iob_len ( iobuf ),
|
||||
},
|
||||
};
|
||||
@ -161,7 +163,7 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) {
|
||||
struct io_buffer *iobuf;
|
||||
|
||||
/* Try to allocate a buffer, stop for now if out of memory */
|
||||
iobuf = alloc_iob ( len );
|
||||
iobuf = alloc_rx_iob ( len, virtnet->dma );
|
||||
if ( ! iobuf )
|
||||
break;
|
||||
|
||||
@ -215,7 +217,8 @@ static int virtnet_open_legacy ( struct net_device *netdev ) {
|
||||
|
||||
/* Initialize rx/tx virtqueues */
|
||||
for ( i = 0; i < QUEUE_NB; i++ ) {
|
||||
if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) {
|
||||
if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i], virtnet->dma,
|
||||
sizeof ( struct virtio_net_hdr_modern ) ) == -1 ) {
|
||||
DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n",
|
||||
virtnet, i );
|
||||
virtnet_free_virtqueues ( netdev );
|
||||
@ -280,7 +283,8 @@ static int virtnet_open_modern ( struct net_device *netdev ) {
|
||||
}
|
||||
|
||||
/* Initialize rx/tx virtqueues */
|
||||
if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) {
|
||||
if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue,
|
||||
virtnet->dma, sizeof ( struct virtio_net_hdr_modern ) ) ) {
|
||||
DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n",
|
||||
virtnet );
|
||||
virtnet_free_virtqueues ( netdev );
|
||||
@ -335,7 +339,7 @@ static void virtnet_close ( struct net_device *netdev ) {
|
||||
|
||||
/* Free rx iobufs */
|
||||
list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) {
|
||||
free_iob ( iobuf );
|
||||
free_rx_iob ( iobuf );
|
||||
}
|
||||
INIT_LIST_HEAD ( &virtnet->rx_iobufs );
|
||||
virtnet->rx_num_iobufs = 0;
|
||||
@ -478,6 +482,12 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) {
|
||||
|
||||
/* Enable PCI bus master and reset NIC */
|
||||
adjust_pci_device ( pci );
|
||||
|
||||
/* Configure DMA */
|
||||
virtnet->dma = &pci->dma;
|
||||
dma_set_mask_64bit ( virtnet->dma );
|
||||
netdev->dma = virtnet->dma;
|
||||
|
||||
vp_reset ( ioaddr );
|
||||
|
||||
/* Load MAC address and MTU */
|
||||
@ -506,7 +516,7 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) {
|
||||
return 0;
|
||||
|
||||
unregister_netdev ( netdev );
|
||||
err_register_netdev:
|
||||
err_register_netdev:
|
||||
vp_reset ( ioaddr );
|
||||
netdev_nullify ( netdev );
|
||||
netdev_put ( netdev );
|
||||
@ -586,6 +596,11 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) {
|
||||
/* Enable the PCI device */
|
||||
adjust_pci_device ( pci );
|
||||
|
||||
/* Configure DMA */
|
||||
virtnet->dma = &pci->dma;
|
||||
dma_set_mask_64bit ( virtnet->dma );
|
||||
netdev->dma = virtnet->dma;
|
||||
|
||||
/* Reset the device and set initial status bits */
|
||||
vpm_reset ( &virtnet->vdev );
|
||||
vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE );
|
||||
@ -633,7 +648,6 @@ err_mac_address:
|
||||
vpm_reset ( &virtnet->vdev );
|
||||
netdev_nullify ( netdev );
|
||||
netdev_put ( netdev );
|
||||
|
||||
virtio_pci_unmap_capability ( &virtnet->vdev.device );
|
||||
err_map_device:
|
||||
virtio_pci_unmap_capability ( &virtnet->vdev.isr );
|
||||
|
||||
@ -1165,6 +1165,31 @@ static int xhci_reset ( struct xhci_device *xhci ) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark xHCI device as permanently failed
|
||||
*
|
||||
* @v xhci xHCI device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int xhci_fail ( struct xhci_device *xhci ) {
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Mark command mechanism as permanently failed */
|
||||
xhci->failed = 1;
|
||||
|
||||
/* Reset device */
|
||||
if ( ( rc = xhci_reset ( xhci ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Discard DCBAA entries since DCBAAP has been cleared */
|
||||
assert ( xhci->dcbaa.context != NULL );
|
||||
len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa.context[0] ) );
|
||||
memset ( xhci->dcbaa.context, 0, len );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Transfer request blocks
|
||||
@ -1720,6 +1745,10 @@ static void xhci_event_poll ( struct xhci_device *xhci ) {
|
||||
unsigned int consumed;
|
||||
unsigned int type;
|
||||
|
||||
/* Do nothing if device has permanently failed */
|
||||
if ( xhci->failed )
|
||||
return;
|
||||
|
||||
/* Poll for events */
|
||||
profile_start ( &xhci_event_profiler );
|
||||
for ( consumed = 0 ; ; consumed++ ) {
|
||||
@ -1778,6 +1807,7 @@ static void xhci_event_poll ( struct xhci_device *xhci ) {
|
||||
*/
|
||||
static void xhci_abort ( struct xhci_device *xhci ) {
|
||||
physaddr_t crp;
|
||||
uint32_t crcr;
|
||||
|
||||
/* Abort the command */
|
||||
DBGC2 ( xhci, "XHCI %s aborting command\n", xhci->name );
|
||||
@ -1786,8 +1816,18 @@ static void xhci_abort ( struct xhci_device *xhci ) {
|
||||
/* Allow time for command to abort */
|
||||
mdelay ( XHCI_COMMAND_ABORT_DELAY_MS );
|
||||
|
||||
/* Sanity check */
|
||||
assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 );
|
||||
/* Check for failure to abort */
|
||||
crcr = readl ( xhci->op + XHCI_OP_CRCR );
|
||||
if ( crcr & XHCI_CRCR_CRR ) {
|
||||
|
||||
/* Device has failed to abort a command and is almost
|
||||
* certainly beyond repair. Reset device, abandoning
|
||||
* all state, and mark device as failed to avoid
|
||||
* delays on any future command attempts.
|
||||
*/
|
||||
DBGC ( xhci, "XHCI %s failed to abort command\n", xhci->name );
|
||||
xhci_fail ( xhci );
|
||||
}
|
||||
|
||||
/* Consume (and ignore) any final command status */
|
||||
xhci_event_poll ( xhci );
|
||||
@ -1813,6 +1853,12 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) {
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
/* Immediately fail all commands if command mechanism has failed */
|
||||
if ( xhci->failed ) {
|
||||
rc = -EPIPE;
|
||||
goto err_failed;
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
if ( xhci->pending ) {
|
||||
DBGC ( xhci, "XHCI %s command ring busy\n", xhci->name );
|
||||
@ -1863,6 +1909,7 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) {
|
||||
err_enqueue:
|
||||
xhci->pending = NULL;
|
||||
err_pending:
|
||||
err_failed:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -1115,6 +1115,8 @@ struct xhci_device {
|
||||
struct xhci_event_ring event;
|
||||
/** Current command (if any) */
|
||||
union xhci_trb *pending;
|
||||
/** Command mechanism has permanently failed */
|
||||
int failed;
|
||||
|
||||
/** Device slots, indexed by slot ID */
|
||||
struct xhci_slot **slot;
|
||||
|
||||
@ -38,7 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
|
||||
#define READLINE_MAX 256
|
||||
#define READLINE_MAX 1024
|
||||
|
||||
/**
|
||||
* Synchronise console with edited string
|
||||
@ -258,8 +258,8 @@ void history_free ( struct readline_history *history ) {
|
||||
int readline_history ( const char *prompt, const char *prefill,
|
||||
struct readline_history *history, unsigned long timeout,
|
||||
char **line ) {
|
||||
char buf[READLINE_MAX];
|
||||
struct edit_string string;
|
||||
char *buf;
|
||||
int key;
|
||||
int move_by;
|
||||
const char *new_string;
|
||||
@ -275,10 +275,14 @@ int readline_history ( const char *prompt, const char *prefill,
|
||||
/* Ensure cursor is visible */
|
||||
printf ( "\033[?25h" );
|
||||
|
||||
/* Initialise editable string */
|
||||
/* Allocate buffer and initialise editable string */
|
||||
buf = zalloc ( READLINE_MAX );
|
||||
if ( ! buf ) {
|
||||
rc = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
memset ( &string, 0, sizeof ( string ) );
|
||||
init_editstring ( &string, buf, sizeof ( buf ) );
|
||||
buf[0] = '\0';
|
||||
init_editstring ( &string, buf, READLINE_MAX );
|
||||
|
||||
/* Prefill string, if applicable */
|
||||
if ( prefill ) {
|
||||
@ -303,8 +307,13 @@ int readline_history ( const char *prompt, const char *prefill,
|
||||
switch ( key ) {
|
||||
case CR:
|
||||
case LF:
|
||||
*line = strdup ( buf );
|
||||
rc = ( ( *line ) ? 0 : -ENOMEM );
|
||||
/* Shrink string (ignoring failures) */
|
||||
*line = realloc ( buf,
|
||||
( strlen ( buf ) + 1 /* NUL */ ) );
|
||||
if ( ! *line )
|
||||
*line = buf;
|
||||
buf = NULL;
|
||||
rc = 0;
|
||||
goto done;
|
||||
case CTRL_C:
|
||||
rc = -ECANCELED;
|
||||
@ -332,6 +341,7 @@ int readline_history ( const char *prompt, const char *prefill,
|
||||
|
||||
done:
|
||||
putchar ( '\n' );
|
||||
free ( buf );
|
||||
if ( history ) {
|
||||
if ( *line && (*line)[0] )
|
||||
history_append ( history, *line );
|
||||
|
||||
@ -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 )
|
||||
|
||||
@ -84,7 +84,7 @@ struct rndis_initialise_completion {
|
||||
/** Packet alignment factor */
|
||||
uint32_t align;
|
||||
/** Reserved */
|
||||
uint32_t reserved;
|
||||
uint32_t reserved[2];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** RNDIS halt message */
|
||||
@ -237,7 +237,7 @@ struct rndis_packet_message {
|
||||
/** Per-packet information record */
|
||||
struct rndis_packet_field ppi;
|
||||
/** Reserved */
|
||||
uint32_t reserved;
|
||||
uint32_t reserved[2];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** RNDIS packet record */
|
||||
|
||||
@ -46,6 +46,20 @@ struct parameters;
|
||||
* scheme = "ftp", user = "joe", password = "secret",
|
||||
* host = "insecure.org", port = "8081", path = "/hidden/path/to",
|
||||
* query = "what=is", fragment = "this"
|
||||
*
|
||||
* The URI syntax includes a percent-encoding mechanism that can be
|
||||
* used to represent characters that would otherwise not be possible,
|
||||
* such as a '/' character within the password field. These encodings
|
||||
* are decoded during the URI parsing stage, thereby allowing protocol
|
||||
* implementations to consume the raw field values directly without
|
||||
* further decoding.
|
||||
*
|
||||
* Some protocols (such as HTTP) communicate using URI-encoded values.
|
||||
* For these protocols, the original encoded substring must be
|
||||
* retained verbatim since the choice of whether or not to encode a
|
||||
* particular character may have significance to the receiving
|
||||
* application. We therefore retain the originally-encoded substrings
|
||||
* for the path, query, and fragment fields.
|
||||
*/
|
||||
struct uri {
|
||||
/** Reference count */
|
||||
@ -62,12 +76,14 @@ struct uri {
|
||||
const char *host;
|
||||
/** Port number */
|
||||
const char *port;
|
||||
/** Path */
|
||||
/** Path (after URI decoding) */
|
||||
const char *path;
|
||||
/** Query */
|
||||
const char *query;
|
||||
/** Fragment */
|
||||
const char *fragment;
|
||||
/** Path (with original URI encoding) */
|
||||
const char *epath;
|
||||
/** Query (with original URI encoding) */
|
||||
const char *equery;
|
||||
/** Fragment (with original URI encoding) */
|
||||
const char *efragment;
|
||||
/** Form parameters */
|
||||
struct parameters *params;
|
||||
} __attribute__ (( packed ));
|
||||
@ -100,8 +116,9 @@ enum uri_fields {
|
||||
URI_HOST = URI_FIELD ( host ),
|
||||
URI_PORT = URI_FIELD ( port ),
|
||||
URI_PATH = URI_FIELD ( path ),
|
||||
URI_QUERY = URI_FIELD ( query ),
|
||||
URI_FRAGMENT = URI_FIELD ( fragment ),
|
||||
URI_EPATH = URI_FIELD ( epath ),
|
||||
URI_EQUERY = URI_FIELD ( equery ),
|
||||
URI_EFRAGMENT = URI_FIELD ( efragment ),
|
||||
URI_FIELDS
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#ifndef _VIRTIO_PCI_H_
|
||||
# define _VIRTIO_PCI_H_
|
||||
|
||||
#include <ipxe/dma.h>
|
||||
|
||||
/* A 32-bit r/o bitmask of the features supported by the host */
|
||||
#define VIRTIO_PCI_HOST_FEATURES 0
|
||||
|
||||
@ -198,7 +200,8 @@ struct vring_virtqueue;
|
||||
|
||||
void vp_free_vq(struct vring_virtqueue *vq);
|
||||
int vp_find_vq(unsigned int ioaddr, int queue_index,
|
||||
struct vring_virtqueue *vq);
|
||||
struct vring_virtqueue *vq, struct dma_device *dma_dev,
|
||||
size_t header_size);
|
||||
|
||||
|
||||
/* Virtio 1.0 I/O routines abstract away the three possible HW access
|
||||
@ -298,7 +301,8 @@ void vpm_notify(struct virtio_pci_modern_device *vdev,
|
||||
struct vring_virtqueue *vq);
|
||||
|
||||
int vpm_find_vqs(struct virtio_pci_modern_device *vdev,
|
||||
unsigned nvqs, struct vring_virtqueue *vqs);
|
||||
unsigned nvqs, struct vring_virtqueue *vqs,
|
||||
struct dma_device *dma_dev, size_t header_size);
|
||||
|
||||
int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type);
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# define _VIRTIO_RING_H_
|
||||
|
||||
#include <ipxe/virtio-pci.h>
|
||||
#include <ipxe/dma.h>
|
||||
|
||||
/* Status byte for guest to report progress, and synchronize features. */
|
||||
/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
|
||||
@ -74,17 +75,21 @@ struct vring {
|
||||
|
||||
struct vring_virtqueue {
|
||||
unsigned char *queue;
|
||||
size_t queue_size;
|
||||
struct dma_mapping map;
|
||||
struct dma_device *dma;
|
||||
struct vring vring;
|
||||
u16 free_head;
|
||||
u16 last_used_idx;
|
||||
void **vdata;
|
||||
struct virtio_net_hdr_modern *empty_header;
|
||||
/* PCI */
|
||||
int queue_index;
|
||||
struct virtio_pci_region notification;
|
||||
};
|
||||
|
||||
struct vring_list {
|
||||
char *addr;
|
||||
physaddr_t addr;
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -6,7 +6,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
extern void putchar ( int character );
|
||||
extern int putchar ( int character );
|
||||
|
||||
extern int getchar ( void );
|
||||
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -362,7 +362,7 @@ static int efi_veto_driver ( EFI_HANDLE driver ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Veto Dell Ip4ConfigDxe driver
|
||||
* Veto Ip4ConfigDxe driver on some platforms
|
||||
*
|
||||
* @v binding Driver binding protocol
|
||||
* @v loaded Loaded image protocol
|
||||
@ -372,19 +372,21 @@ static int efi_veto_driver ( EFI_HANDLE driver ) {
|
||||
* @ret vetoed Driver is to be vetoed
|
||||
*/
|
||||
static int
|
||||
efi_veto_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused,
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded __unused,
|
||||
EFI_COMPONENT_NAME_PROTOCOL *wtf __unused,
|
||||
const char *manufacturer, const CHAR16 *name ) {
|
||||
efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused,
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded __unused,
|
||||
EFI_COMPONENT_NAME_PROTOCOL *wtf __unused,
|
||||
const char *manufacturer, const CHAR16 *name ) {
|
||||
static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver";
|
||||
static const char *dell = "Dell Inc.";
|
||||
static const char *itautec = "Itautec S.A.";
|
||||
|
||||
/* Check manufacturer and driver name */
|
||||
if ( ! manufacturer )
|
||||
return 0;
|
||||
if ( ! name )
|
||||
return 0;
|
||||
if ( strcmp ( manufacturer, dell ) != 0 )
|
||||
if ( ( strcmp ( manufacturer, dell ) != 0 ) &&
|
||||
( strcmp ( manufacturer, itautec ) != 0 ) )
|
||||
return 0;
|
||||
if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 )
|
||||
return 0;
|
||||
@ -436,8 +438,8 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused,
|
||||
/** Driver vetoes */
|
||||
static struct efi_veto efi_vetoes[] = {
|
||||
{
|
||||
.name = "Dell Ip4Config",
|
||||
.veto = efi_veto_dell_ip4config,
|
||||
.name = "Ip4Config",
|
||||
.veto = efi_veto_ip4config,
|
||||
},
|
||||
{
|
||||
.name = "HP Xhci",
|
||||
|
||||
@ -73,6 +73,9 @@ static LIST_HEAD ( peerdisc_segments );
|
||||
*/
|
||||
unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS;
|
||||
|
||||
/** Most recently discovered peer (for any block) */
|
||||
static char *peerdisc_recent;
|
||||
|
||||
/** Hosted cache server */
|
||||
static char *peerhost;
|
||||
|
||||
@ -383,6 +386,7 @@ static int peerdisc_discovered ( struct peerdisc_segment *segment,
|
||||
struct peerdisc_peer *peer;
|
||||
struct peerdisc_client *peerdisc;
|
||||
struct peerdisc_client *tmp;
|
||||
char *recent;
|
||||
|
||||
/* Ignore duplicate peers */
|
||||
list_for_each_entry ( peer, &segment->peers, list ) {
|
||||
@ -403,6 +407,15 @@ static int peerdisc_discovered ( struct peerdisc_segment *segment,
|
||||
/* Add to end of list of peers */
|
||||
list_add_tail ( &peer->list, &segment->peers );
|
||||
|
||||
/* Record as most recently discovered peer */
|
||||
if ( location != peerdisc_recent ) {
|
||||
recent = strdup ( location );
|
||||
if ( recent ) {
|
||||
free ( peerdisc_recent );
|
||||
peerdisc_recent = recent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Notify all clients */
|
||||
list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list )
|
||||
peerdisc->op->discovered ( peerdisc );
|
||||
@ -484,6 +497,16 @@ static struct peerdisc_segment * peerdisc_create ( const char *id ) {
|
||||
|
||||
} else {
|
||||
|
||||
/* Add most recently discovered peer to list of peers
|
||||
*
|
||||
* This is a performance optimisation: we assume that
|
||||
* the most recently discovered peer for any block has
|
||||
* a high probability of also having a copy of the
|
||||
* next block that we attempt to discover.
|
||||
*/
|
||||
if ( peerdisc_recent )
|
||||
peerdisc_discovered ( segment, peerdisc_recent );
|
||||
|
||||
/* Start discovery timer */
|
||||
start_timer_nodelay ( &segment->timer );
|
||||
DBGC2 ( segment, "PEERDISC %p discovering %s\n",
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -614,8 +614,8 @@ int http_open ( struct interface *xfer, struct http_method *method,
|
||||
|
||||
/* Calculate request URI length */
|
||||
memset ( &request_uri, 0, sizeof ( request_uri ) );
|
||||
request_uri.path = ( uri->path ? uri->path : "/" );
|
||||
request_uri.query = uri->query;
|
||||
request_uri.epath = ( uri->epath ? uri->epath : "/" );
|
||||
request_uri.equery = uri->equery;
|
||||
request_uri_len =
|
||||
( format_uri ( &request_uri, NULL, 0 ) + 1 /* NUL */);
|
||||
|
||||
|
||||
@ -149,8 +149,10 @@ static void uri_okx ( struct uri *uri, struct uri *expected, const char *file,
|
||||
okx ( uristrcmp ( uri->host, expected->host ) == 0, file, line );
|
||||
okx ( uristrcmp ( uri->port, expected->port ) == 0, file, line );
|
||||
okx ( uristrcmp ( uri->path, expected->path ) == 0, file, line );
|
||||
okx ( uristrcmp ( uri->query, expected->query ) == 0, file, line );
|
||||
okx ( uristrcmp ( uri->fragment, expected->fragment ) == 0, file, line);
|
||||
okx ( uristrcmp ( uri->epath, expected->epath ) == 0, file, line );
|
||||
okx ( uristrcmp ( uri->equery, expected->equery ) == 0, file, line );
|
||||
okx ( uristrcmp ( uri->efragment, expected->efragment ) == 0,
|
||||
file, line);
|
||||
okx ( uri->params == expected->params, file, line );
|
||||
}
|
||||
#define uri_ok( uri, expected ) uri_okx ( uri, expected, __FILE__, __LINE__ )
|
||||
@ -490,25 +492,33 @@ static struct uri_test uri_empty = {
|
||||
/** Basic HTTP URI */
|
||||
static struct uri_test uri_boot_ipxe_org = {
|
||||
"http://boot.ipxe.org/demo/boot.php",
|
||||
{ .scheme = "http", .host = "boot.ipxe.org", .path = "/demo/boot.php" }
|
||||
{ .scheme = "http", .host = "boot.ipxe.org",
|
||||
.path = "/demo/boot.php", .epath = "/demo/boot.php" },
|
||||
};
|
||||
|
||||
/** Basic opaque URI */
|
||||
static struct uri_test uri_mailto = {
|
||||
"mailto:ipxe-devel@lists.ipxe.org",
|
||||
{ .scheme = "mailto", .opaque = "ipxe-devel@lists.ipxe.org" }
|
||||
{ .scheme = "mailto", .opaque = "ipxe-devel@lists.ipxe.org" },
|
||||
};
|
||||
|
||||
/** Basic host-only URI */
|
||||
static struct uri_test uri_host = {
|
||||
"http://boot.ipxe.org",
|
||||
{ .scheme = "http", .host = "boot.ipxe.org" },
|
||||
};
|
||||
|
||||
/** Basic path-only URI */
|
||||
static struct uri_test uri_path = {
|
||||
"/var/lib/tftpboot/pxelinux.0",
|
||||
{ .path = "/var/lib/tftpboot/pxelinux.0" },
|
||||
{ .path = "/var/lib/tftpboot/pxelinux.0",
|
||||
.epath ="/var/lib/tftpboot/pxelinux.0" },
|
||||
};
|
||||
|
||||
/** Path-only URI with escaped characters */
|
||||
static struct uri_test uri_path_escaped = {
|
||||
"/hello%20world%3F",
|
||||
{ .path = "/hello world?" },
|
||||
{ .path = "/hello world?", .epath = "/hello%20world%3F" },
|
||||
};
|
||||
|
||||
/** HTTP URI with all the trimmings */
|
||||
@ -521,8 +531,9 @@ static struct uri_test uri_http_all = {
|
||||
.host = "example.com",
|
||||
.port = "3001",
|
||||
.path = "/~foo/cgi-bin/foo.pl",
|
||||
.query = "a=b&c=d",
|
||||
.fragment = "bit",
|
||||
.epath = "/~foo/cgi-bin/foo.pl",
|
||||
.equery = "a=b&c=d",
|
||||
.efragment = "bit",
|
||||
},
|
||||
};
|
||||
|
||||
@ -533,8 +544,9 @@ static struct uri_test uri_http_escaped = {
|
||||
.scheme = "https",
|
||||
.host = "test.ipxe.org",
|
||||
.path = "/wtf?\n",
|
||||
.query = "kind#of/uri is",
|
||||
.fragment = "this?",
|
||||
.epath = "/wtf%3F%0A",
|
||||
.equery = "kind%23of/uri%20is",
|
||||
.efragment = "this%3F",
|
||||
},
|
||||
};
|
||||
|
||||
@ -550,8 +562,9 @@ static struct uri_test uri_http_escaped_improper = {
|
||||
.scheme = "https",
|
||||
.host = "test.ipxe.org",
|
||||
.path = "/wtf?\n",
|
||||
.query = "kind#of/uri is",
|
||||
.fragment = "this?",
|
||||
.epath = "/wt%66%3f\n",
|
||||
.equery = "kind%23of/uri is",
|
||||
.efragment = "this?",
|
||||
},
|
||||
};
|
||||
|
||||
@ -562,6 +575,7 @@ static struct uri_test uri_ipv6 = {
|
||||
.scheme = "http",
|
||||
.host = "[2001:ba8:0:1d4::6950:5845]",
|
||||
.path = "/",
|
||||
.epath = "/",
|
||||
},
|
||||
};
|
||||
|
||||
@ -573,6 +587,7 @@ static struct uri_test uri_ipv6_port = {
|
||||
.host = "[2001:ba8:0:1d4::6950:5845]",
|
||||
.port = "8001",
|
||||
.path = "/boot",
|
||||
.epath = "/boot",
|
||||
},
|
||||
};
|
||||
|
||||
@ -583,6 +598,7 @@ static struct uri_test uri_ipv6_local = {
|
||||
.scheme = "http",
|
||||
.host = "[fe80::69ff:fe50:5845%net0]",
|
||||
.path = "/ipxe",
|
||||
.epath = "/ipxe",
|
||||
},
|
||||
};
|
||||
|
||||
@ -598,6 +614,7 @@ static struct uri_test uri_ipv6_local_non_conforming = {
|
||||
.scheme = "http",
|
||||
.host = "[fe80::69ff:fe50:5845%net0]",
|
||||
.path = "/ipxe",
|
||||
.epath = "/ipxe",
|
||||
},
|
||||
};
|
||||
|
||||
@ -625,6 +642,7 @@ static struct uri_test uri_file_absolute = {
|
||||
{
|
||||
.scheme = "file",
|
||||
.path = "/boot/script.ipxe",
|
||||
.epath = "/boot/script.ipxe",
|
||||
},
|
||||
};
|
||||
|
||||
@ -635,6 +653,7 @@ static struct uri_test uri_file_volume = {
|
||||
.scheme = "file",
|
||||
.host = "hpilo",
|
||||
.path = "/boot/script.ipxe",
|
||||
.epath = "/boot/script.ipxe",
|
||||
},
|
||||
};
|
||||
|
||||
@ -736,6 +755,7 @@ static struct uri_pxe_test uri_pxe_absolute = {
|
||||
.scheme = "http",
|
||||
.host = "not.a.tftp",
|
||||
.path = "/uri",
|
||||
.epath = "/uri",
|
||||
},
|
||||
"http://not.a.tftp/uri",
|
||||
};
|
||||
@ -754,6 +774,7 @@ static struct uri_pxe_test uri_pxe_absolute_path = {
|
||||
.scheme = "tftp",
|
||||
.host = "192.168.0.2",
|
||||
.path = "//absolute/path",
|
||||
.epath = "//absolute/path",
|
||||
},
|
||||
"tftp://192.168.0.2//absolute/path",
|
||||
};
|
||||
@ -772,6 +793,7 @@ static struct uri_pxe_test uri_pxe_relative_path = {
|
||||
.scheme = "tftp",
|
||||
.host = "192.168.0.3",
|
||||
.path = "/relative/path",
|
||||
.epath = "/relative/path",
|
||||
},
|
||||
"tftp://192.168.0.3/relative/path",
|
||||
};
|
||||
@ -790,8 +812,9 @@ static struct uri_pxe_test uri_pxe_icky = {
|
||||
.scheme = "tftp",
|
||||
.host = "10.0.0.6",
|
||||
.path = "/C:\\tftpboot\\icky#path",
|
||||
.epath = "/C:\\tftpboot\\icky#path",
|
||||
},
|
||||
"tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path",
|
||||
"tftp://10.0.0.6/C:\\tftpboot\\icky#path",
|
||||
};
|
||||
|
||||
/** PXE URI with custom port */
|
||||
@ -810,6 +833,7 @@ static struct uri_pxe_test uri_pxe_port = {
|
||||
.host = "192.168.0.1",
|
||||
.port = "4069",
|
||||
.path = "//another/path",
|
||||
.epath = "//another/path",
|
||||
},
|
||||
"tftp://192.168.0.1:4069//another/path",
|
||||
};
|
||||
@ -873,6 +897,7 @@ static struct uri_params_test uri_params = {
|
||||
.scheme = "http",
|
||||
.host = "boot.ipxe.org",
|
||||
.path = "/demo/boot.php",
|
||||
.epath = "/demo/boot.php",
|
||||
},
|
||||
NULL,
|
||||
uri_params_list,
|
||||
@ -902,6 +927,7 @@ static struct uri_params_test uri_named_params = {
|
||||
.host = "192.168.100.4",
|
||||
.port = "3001",
|
||||
.path = "/register",
|
||||
.epath = "/register",
|
||||
},
|
||||
"foo",
|
||||
uri_named_params_list,
|
||||
@ -917,6 +943,7 @@ static void uri_test_exec ( void ) {
|
||||
uri_parse_format_dup_ok ( &uri_empty );
|
||||
uri_parse_format_dup_ok ( &uri_boot_ipxe_org );
|
||||
uri_parse_format_dup_ok ( &uri_mailto );
|
||||
uri_parse_format_dup_ok ( &uri_host );
|
||||
uri_parse_format_dup_ok ( &uri_path );
|
||||
uri_parse_format_dup_ok ( &uri_path_escaped );
|
||||
uri_parse_format_dup_ok ( &uri_http_all );
|
||||
|
||||
@ -58,8 +58,8 @@ int imgdownload ( struct uri *uri, unsigned long timeout,
|
||||
memcpy ( &uri_redacted, uri, sizeof ( uri_redacted ) );
|
||||
uri_redacted.user = NULL;
|
||||
uri_redacted.password = NULL;
|
||||
uri_redacted.query = NULL;
|
||||
uri_redacted.fragment = NULL;
|
||||
uri_redacted.equery = NULL;
|
||||
uri_redacted.efragment = NULL;
|
||||
uri_string_redacted = format_uri_alloc ( &uri_redacted );
|
||||
if ( ! uri_string_redacted ) {
|
||||
rc = -ENOMEM;
|
||||
|
||||
@ -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