Compare commits

..

26 Commits

Author SHA1 Message Date
1844aacc83 [uri] Retain original encodings for path, query, and fragment fields
iPXE decodes any percent-encoded characters during the URI parsing
stage, thereby allowing protocol implementations to consume the raw
field values directly without further decoding.

When reconstructing a URI string for use in an HTTP request line, the
percent-encoding is currently reapplied in a reversible way: we
guarantee that our reconstructed URI string could be decoded to give
the same raw field values.

This technically violates RFC3986, which states that "URIs that differ
in the replacement of a reserved character with its corresponding
percent-encoded octet are not equivalent".  Experiments show that
several HTTP server applications will attach meaning to the choice of
whether or not a particular character was percent-encoded, even when
the percent-encoding is unnecessary from the perspective of parsing
the URI into its component fields.

Fix by storing the originally encoded substrings for the path, query,
and fragment fields and using these original encoded versions when
reconstructing a URI string.  The path field is also stored as a
decoded string, for use by protocols such as TFTP that communicate
using raw strings rather than URI-encoded strings.  All other fields
(such as the username and password) continue to be stored only in
their decoded versions since nothing ever needs to know the originally
encoded versions of these fields.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-12 09:58:29 +00:00
85eb961bf9 [xhci] Allow for permanent failure of the command mechanism
Some xHCI controllers (observed with the Thunderbolt ports on a
ThinkPad X1 Extreme Gen3 and a ThinkPad P53) seem to suffer a
catastrophic failure at the point that ExitBootServices() is called if
the IOMMU is enabled.  The symptoms appear to be consistent with
another UEFI driver (e.g. the IOMMU driver, or the Thunderbolt driver)
having torn down the DMA mappings, leaving the xHCI controller unable
to write to host memory.  The observable effect is that all commands
fail with a timeout, and attempts to abort command execution similarly
fail since the xHCI controller is unable to report the abort
completion.

Check for failure to abort a command, and respond by performing a full
device reset (as recommended by the xHCI specification) and by marking
the device as permanently failed.

Reported-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-10-28 23:18:07 +01:00
f24a2794e1 [virtio] Update driver to use DMA API
Signed-off-by: Aaron Young <aaron.young@oracle.com>
2021-10-28 13:19:30 +01:00
2265a65191 [readline] Extend maximum read line length to 1024 characters
Realistic Linux kernel command lines may exceed our current 256
character limit for interactively edited commands or settings.

Switch from stack allocation to heap allocation, and increase the
limit to 1024 characters.

Requested-by: Matteo Guglielmi <Matteo.Guglielmi@dalco.ch>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-09-10 15:51:14 +01:00
05a76acc6d [ecm] Use ACPI-provided system-specific MAC address if present
Use the "system MAC address" provided within the DSDT/SSDT if such an
address is available and has not already been assigned to a network
device.

Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-09-09 12:56:02 +01:00
91e147213c [ecm] Expose USB vendor/device information to ecm_fetch_mac()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-09-09 12:52:12 +01:00
0cc4c42f0a [acpi] Allow for extraction of a MAC address from the DSDT/SSDT
Some vendors provide a "system MAC address" within the DSDT/SSDT, to
be used to override the MAC address for a USB docking station.

A full implementation would require an ACPI bytecode interpreter,
since at least one OEM allows the MAC address to be constructed by
executable ACPI bytecode (rather than a fixed data structure).

We instead attempt to extract a plausible-looking "_AUXMAC_#.....#"
string that appears shortly after an "AMAC" or "MACA" signature.  This
should work for most implementations encountered in practice.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Suggested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-22 09:45:21 +01:00
51c88a4a62 [build] Fix building on broken versions of GNU binutils
Some versions of GNU objcopy (observed with binutils 2.23.52.0.1 on
CentOS 7.0.1406) document the -D/--enable-deterministic-archives
option but fail to recognise the short form of the option.

Work around this problem by using the long form of the option.

Reported-by: Olaf Hering <olaf@aepfle.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-17 14:37:39 +01:00
bf4ccd4265 [build] Ensure build ID is deterministic
Commit 040cdd0 ("[linux] Add a prefix to all symbols to avoid future
name collisions") unintentionally reintroduced an element of
non-determinism into the build ID, by omitting the -D option when
manipulating the blib.a archive.

Fix by adding the -D option to restore determinism.

Reworded-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-14 14:36:51 +01:00
3c040ad387 [efi] Veto the Itautec Ip4ConfigDxe driver
The Ip4ConfigDxe driver bug that was observed on Dell systems in
commit 64b4452 ("[efi] Blacklist the Dell Ip4ConfigDxe driver") has
also been observed on systems with a manufacturer name of "Itautec
S.A.".  The symptoms of the bug are identical: an attempt to call
DisconnectController() on the LOM device handle will lock up the
system.

Fix by extending the veto to cover the Ip4ConfigDxe driver for this
manufacturer.

Debugged-by: Celso Viana <celso.vianna@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-11 15:14:21 +01:00
3dd1989ac0 [libc] Match standard prototype for putchar()
Reported-by: Bernhard M. Wiedemann <bwiedemann@suse.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-07 13:26:01 +01:00
52300ccf98 [base64] Include terminating NUL within base64 character array
Reported-by: Bernhard M. Wiedemann <bwiedemann@suse.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-07 13:20:02 +01:00
92807f5759 [rndis] Fix size of reserved fields
Most RNDIS data structures include a trailing 4-byte reserved field.
For the REMOTE_NDIS_PACKET_MSG and REMOTE_NDIS_INITIALIZE_CMPLT
structures, this is an 8-byte field instead.

iPXE currently uses incorrect structure definitions with a 4-byte
reserved field in all data structures, resulting in data payloads that
overlap the last 4 bytes of the 8-byte reserved field.

RNDIS uses explicit offsets to locate any data payloads beyond the
message header, and so liberal RNDIS parsers (such as those used in
Hyper-V and in the Linux USB Ethernet gadget driver) are still able to
parse the malformed structures.

A stricter RNDIS parser (such as that found in some older Android
builds that seem to use an out-of-tree USB Ethernet gadget driver) may
reject the malformed structures since the data payload offset is less
than the header length, causing iPXE to be unable to transmit packets.

Fix by correcting the length of the reserved fields.

Debugged-by: Martin Nield <pmn1492@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-07 12:01:10 +01:00
065dce8d59 [ath5k] Avoid returning uninitialised data on EEPROM read errors
Originally-implemented-by: Bernhard M. Wiedemann <bwiedemann@suse.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-06-04 14:16:44 +01:00
47 changed files with 1729 additions and 263 deletions

View File

@ -43,7 +43,8 @@ $(BIN)/%.drv.efi : $(BIN)/%.efidrv
$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM)
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) -c $< $@
$(Q)$(EFIROM) -v $(firstword $(TGT_PCI_VENDOR) 0) \
-d $(firstword $(TGT_PCI_DEVICE) 0) -c $< $@
$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined
$(QM)$(ECHO) " [CAB] $@"

View File

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

View File

@ -42,6 +42,69 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** _S5_ signature */
#define S5_SIGNATURE ACPI_SIGNATURE ( '_', 'S', '5', '_' )
/**
* Extract \_Sx value from DSDT/SSDT
*
* @v zsdt DSDT or SSDT
* @v len Length of DSDT/SSDT
* @v offset Offset of signature within DSDT/SSDT
* @v data Data buffer
* @ret rc Return status code
*
* In theory, extracting the \_Sx value from the DSDT/SSDT requires a
* full ACPI parser plus some heuristics to work around the various
* broken encodings encountered in real ACPI implementations.
*
* In practice, we can get the same result by scanning through the
* DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
* four bytes, removing any bytes with bit 3 set, and treating
* whatever is left as a little-endian value. This is one of the
* uglier hacks I have ever implemented, but it's still prettier than
* the ACPI specification itself.
*/
static int acpi_extract_sx ( userptr_t zsdt, size_t len, size_t offset,
void *data ) {
unsigned int *sx = data;
uint8_t bytes[4];
uint8_t *byte;
/* Skip signature and package header */
offset += ( 4 /* signature */ + 3 /* package header */ );
/* Sanity check */
if ( ( offset + sizeof ( bytes ) /* value */ ) > len ) {
return -EINVAL;
}
/* Read first four bytes of value */
copy_from_user ( bytes, zsdt, offset, sizeof ( bytes ) );
DBGC ( colour, "ACPI found \\_Sx containing %02x:%02x:%02x:%02x\n",
bytes[0], bytes[1], bytes[2], bytes[3] );
/* Extract \Sx value. There are three potential encodings
* that we might encounter:
*
* - SLP_TYPa, SLP_TYPb, rsvd, rsvd
*
* - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
*
* - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
*
* Since <byteprefix> and <dwordprefix> both have bit 3 set,
* and valid SLP_TYPx must have bit 3 clear (since SLP_TYPx is
* a 3-bit field), we can just skip any bytes with bit 3 set.
*/
byte = bytes;
if ( *byte & 0x08 )
byte++;
*sx = *(byte++);
if ( *byte & 0x08 )
byte++;
*sx |= ( *byte << 8 );
return 0;
}
/**
* Power off the computer using ACPI
*
@ -56,7 +119,7 @@ int acpi_poweroff ( void ) {
unsigned int pm1b_cnt;
unsigned int slp_typa;
unsigned int slp_typb;
int s5;
unsigned int s5;
int rc;
/* Locate FADT */
@ -74,9 +137,8 @@ int acpi_poweroff ( void ) {
pm1b_cnt = ( pm1b_cnt_blk + ACPI_PM1_CNT );
/* Extract \_S5 from DSDT or any SSDT */
s5 = acpi_sx ( S5_SIGNATURE );
if ( s5 < 0 ) {
rc = s5;
if ( ( rc = acpi_extract ( S5_SIGNATURE, &s5,
acpi_extract_sx ) ) != 0 ) {
DBGC ( colour, "ACPI could not extract \\_S5: %s\n",
strerror ( rc ) );
return rc;

View File

@ -59,7 +59,8 @@ static void cachedhcp_init ( void ) {
}
/* Record cached DHCPACK */
if ( ( rc = cachedhcp_record ( phys_to_user ( cached_dhcpack_phys ),
if ( ( rc = cachedhcp_record ( &cached_dhcpack,
phys_to_user ( cached_dhcpack_phys ),
sizeof ( BOOTPLAYER_t ) ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",
strerror ( rc ) );

View File

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

View File

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

View File

@ -169,33 +169,22 @@ userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index ) {
}
/**
* Extract \_Sx value from DSDT/SSDT
* Extract value from DSDT/SSDT
*
* @v zsdt DSDT or SSDT
* @v signature Signature (e.g. "_S5_")
* @ret sx \_Sx value, or negative error
*
* In theory, extracting the \_Sx value from the DSDT/SSDT requires a
* full ACPI parser plus some heuristics to work around the various
* broken encodings encountered in real ACPI implementations.
*
* In practice, we can get the same result by scanning through the
* DSDT/SSDT for the signature (e.g. "_S5_"), extracting the first
* four bytes, removing any bytes with bit 3 set, and treating
* whatever is left as a little-endian value. This is one of the
* uglier hacks I have ever implemented, but it's still prettier than
* the ACPI specification itself.
* @v data Data buffer
* @v extract Extraction method
* @ret rc Return status code
*/
static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
static int acpi_zsdt ( userptr_t zsdt, uint32_t signature, void *data,
int ( * extract ) ( userptr_t zsdt, size_t len,
size_t offset, void *data ) ) {
struct acpi_header acpi;
union {
uint32_t dword;
uint8_t byte[4];
} buf;
uint32_t buf;
size_t offset;
size_t len;
unsigned int sx;
uint8_t *byte;
int rc;
/* Read table header */
copy_from_user ( &acpi, zsdt, 0, sizeof ( acpi ) );
@ -203,75 +192,51 @@ static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
/* Locate signature */
for ( offset = sizeof ( acpi ) ;
( ( offset + sizeof ( buf ) /* signature */ + 3 /* pkg header */
+ sizeof ( buf ) /* value */ ) < len ) ;
( ( offset + sizeof ( buf ) /* signature */ ) < len ) ;
offset++ ) {
/* Check signature */
copy_from_user ( &buf, zsdt, offset, sizeof ( buf ) );
if ( buf.dword != cpu_to_le32 ( signature ) )
if ( buf != cpu_to_le32 ( signature ) )
continue;
DBGC ( zsdt, "DSDT/SSDT %#08lx found %s at offset %#zx\n",
user_to_phys ( zsdt, 0 ), acpi_name ( signature ),
offset );
offset += sizeof ( buf );
/* Read first four bytes of value */
copy_from_user ( &buf, zsdt, ( offset + 3 /* pkg header */ ),
sizeof ( buf ) );
DBGC ( zsdt, "DSDT/SSDT %#08lx found %s containing "
"%02x:%02x:%02x:%02x\n", user_to_phys ( zsdt, 0 ),
acpi_name ( signature ), buf.byte[0], buf.byte[1],
buf.byte[2], buf.byte[3] );
/* Extract \Sx value. There are three potential
* encodings that we might encounter:
*
* - SLP_TYPa, SLP_TYPb, rsvd, rsvd
*
* - <byteprefix>, SLP_TYPa, <byteprefix>, SLP_TYPb, ...
*
* - <dwordprefix>, SLP_TYPa, SLP_TYPb, 0, 0
*
* Since <byteprefix> and <dwordprefix> both have bit
* 3 set, and valid SLP_TYPx must have bit 3 clear
* (since SLP_TYPx is a 3-bit field), we can just skip
* any bytes with bit 3 set.
*/
byte = &buf.byte[0];
if ( *byte & 0x08 )
byte++;
sx = *(byte++);
if ( *byte & 0x08 )
byte++;
sx |= ( *byte << 8 );
return sx;
/* Attempt to extract data */
if ( ( rc = extract ( zsdt, len, offset, data ) ) == 0 )
return 0;
}
return -ENOENT;
}
/**
* Extract \_Sx value from DSDT/SSDT
* Extract value from DSDT/SSDT
*
* @v signature Signature (e.g. "_S5_")
* @ret sx \_Sx value, or negative error
* @v data Data buffer
* @v extract Extraction method
* @ret rc Return status code
*/
int acpi_sx ( uint32_t signature ) {
int acpi_extract ( uint32_t signature, void *data,
int ( * extract ) ( userptr_t zsdt, size_t len,
size_t offset, void *data ) ) {
struct acpi_fadt fadtab;
userptr_t fadt;
userptr_t dsdt;
userptr_t ssdt;
unsigned int i;
int sx;
int rc;
/* Try DSDT first */
fadt = acpi_find ( FADT_SIGNATURE, 0 );
if ( fadt ) {
copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) );
dsdt = phys_to_user ( fadtab.dsdt );
if ( ( sx = acpi_sx_zsdt ( dsdt, signature ) ) >= 0 )
return sx;
if ( ( rc = acpi_zsdt ( dsdt, signature, data,
extract ) ) == 0 )
return 0;
}
/* Try all SSDTs */
@ -279,11 +244,12 @@ int acpi_sx ( uint32_t signature ) {
ssdt = acpi_find ( SSDT_SIGNATURE, i );
if ( ! ssdt )
break;
if ( ( sx = acpi_sx_zsdt ( ssdt, signature ) ) >= 0 )
return sx;
if ( ( rc = acpi_zsdt ( ssdt, signature, data,
extract ) ) == 0 )
return 0;
}
DBGC ( colour, "ACPI could not find \\_Sx \"%s\"\n",
DBGC ( colour, "ACPI could not find \"%s\"\n",
acpi_name ( signature ) );
return -ENOENT;
}

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

@ -0,0 +1,154 @@
/*
* Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <errno.h>
#include <ipxe/acpi.h>
#include <ipxe/base16.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
#include <ipxe/acpimac.h>
/** @file
*
* ACPI MAC address
*
*/
/** Colour for debug messages */
#define colour FADT_SIGNATURE
/** AMAC signature */
#define AMAC_SIGNATURE ACPI_SIGNATURE ( 'A', 'M', 'A', 'C' )
/** MACA signature */
#define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' )
/** Maximum number of bytes to skip after AMAC/MACA signature
*
* This is entirely empirical.
*/
#define AUXMAC_MAX_SKIP 8
/**
* Extract MAC address from DSDT/SSDT
*
* @v zsdt DSDT or SSDT
* @v len Length of DSDT/SSDT
* @v offset Offset of signature within DSDT/SSDT
* @v data Data buffer
* @ret rc Return status code
*
* Some vendors provide a "system MAC address" within the DSDT/SSDT,
* to be used to override the MAC address for a USB docking station.
*
* A full implementation would require an ACPI bytecode interpreter,
* since at least one OEM allows the MAC address to be constructed by
* executable ACPI bytecode (rather than a fixed data structure).
*
* We instead attempt to extract a plausible-looking "_AUXMAC_#.....#"
* string that appears shortly after an "AMAC" or "MACA" signature.
* This should work for most implementations encountered in practice.
*/
static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset,
void *data ) {
static const char prefix[9] = "_AUXMAC_#";
uint8_t *hw_addr = data;
size_t skip = 0;
char auxmac[ sizeof ( prefix ) /* "_AUXMAC_#" */ +
( ETH_ALEN * 2 ) /* MAC */ + 1 /* "#" */ + 1 /* NUL */ ];
char *mac = &auxmac[ sizeof ( prefix ) ];
int decoded_len;
int rc;
/* Skip signature and at least one tag byte */
offset += ( 4 /* signature */ + 1 /* tag byte */ );
/* Scan for "_AUXMAC_#.....#" close to signature */
for ( skip = 0 ;
( ( skip < AUXMAC_MAX_SKIP ) &&
( offset + skip + sizeof ( auxmac ) ) < len ) ;
skip++ ) {
/* Read value */
copy_from_user ( auxmac, zsdt, ( offset + skip ),
sizeof ( auxmac ) );
/* Check for expected format */
if ( memcmp ( auxmac, prefix, sizeof ( prefix ) ) != 0 )
continue;
if ( auxmac[ sizeof ( auxmac ) - 2 ] != '#' )
continue;
if ( auxmac[ sizeof ( auxmac ) - 1 ] != '\0' )
continue;
DBGC ( colour, "ACPI found MAC string \"%s\"\n", auxmac );
/* Terminate MAC address string */
mac = &auxmac[ sizeof ( prefix ) ];
mac[ ETH_ALEN * 2 ] = '\0';
/* Decode MAC address */
decoded_len = base16_decode ( mac, hw_addr, ETH_ALEN );
if ( decoded_len < 0 ) {
rc = decoded_len;
DBGC ( colour, "ACPI could not decode MAC \"%s\": %s\n",
mac, strerror ( rc ) );
return rc;
}
/* Check MAC address validity */
if ( ! is_valid_ether_addr ( hw_addr ) ) {
DBGC ( colour, "ACPI has invalid MAC %s\n",
eth_ntoa ( hw_addr ) );
return -EINVAL;
}
return 0;
}
return -ENOENT;
}
/**
* Extract MAC address from DSDT/SSDT
*
* @v hw_addr MAC address to fill in
* @ret rc Return status code
*/
int acpi_mac ( uint8_t *hw_addr ) {
int rc;
/* Look for an "AMAC" address */
if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr,
acpi_extract_mac ) ) == 0 )
return 0;
/* Look for a "MACA" address */
if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr,
acpi_extract_mac ) ) == 0 )
return 0;
return -ENOENT;
}

View File

@ -36,7 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
static const char base64[64] =
static const char base64[ 64 + 1 /* NUL */ ] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**

View File

@ -37,29 +37,121 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
/** A cached DHCP packet */
struct cached_dhcp_packet {
/** Settings block name */
const char *name;
/** DHCP packet (if any) */
struct dhcp_packet *dhcppkt;
};
/** Cached DHCPACK */
static struct dhcp_packet *cached_dhcpack;
struct cached_dhcp_packet cached_dhcpack = {
.name = DHCP_SETTINGS_NAME,
};
/** Cached ProxyDHCPOFFER */
struct cached_dhcp_packet cached_proxydhcp = {
.name = PROXYDHCP_SETTINGS_NAME,
};
/** Cached PXEBSACK */
struct cached_dhcp_packet cached_pxebs = {
.name = PXEBS_SETTINGS_NAME,
};
/** List of cached DHCP packets */
static struct cached_dhcp_packet *cached_packets[] = {
&cached_dhcpack,
&cached_proxydhcp,
&cached_pxebs,
};
/** Colour for debug messages */
#define colour &cached_dhcpack
/**
* Record cached DHCPACK
* Free cached DHCP packet
*
* @v cache Cached DHCP packet
*/
static void cachedhcp_free ( struct cached_dhcp_packet *cache ) {
dhcppkt_put ( cache->dhcppkt );
cache->dhcppkt = NULL;
}
/**
* Apply cached DHCP packet settings
*
* @v cache Cached DHCP packet
* @v netdev Network device, or NULL
* @ret rc Return status code
*/
static int cachedhcp_apply ( struct cached_dhcp_packet *cache,
struct net_device *netdev ) {
struct settings *settings;
int rc;
/* Do nothing if cache is empty */
if ( ! cache->dhcppkt )
return 0;
/* Do nothing unless cached packet's MAC address matches this
* network device, if specified.
*/
if ( netdev ) {
if ( memcmp ( netdev->ll_addr, cache->dhcppkt->dhcphdr->chaddr,
netdev->ll_protocol->ll_addr_len ) != 0 ) {
DBGC ( colour, "CACHEDHCP %s does not match %s\n",
cache->name, netdev->name );
return 0;
}
DBGC ( colour, "CACHEDHCP %s is for %s\n",
cache->name, netdev->name );
}
/* Select appropriate parent settings block */
settings = ( netdev ? netdev_settings ( netdev ) : NULL );
/* Register settings */
if ( ( rc = register_settings ( &cache->dhcppkt->settings, settings,
cache->name ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP %s could not register settings: %s\n",
cache->name, strerror ( rc ) );
return rc;
}
/* Free cached DHCP packet */
cachedhcp_free ( cache );
return 0;
}
/**
* Record cached DHCP packet
*
* @v cache Cached DHCP packet
* @v data DHCPACK packet buffer
* @v max_len Maximum possible length
* @ret rc Return status code
*/
int cachedhcp_record ( userptr_t data, size_t max_len ) {
int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
size_t max_len ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
unsigned int i;
size_t len;
/* Free any existing cached packet */
cachedhcp_free ( cache );
/* Allocate and populate DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
if ( ! dhcppkt ) {
DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
DBGC ( colour, "CACHEDHCP %s could not allocate copy\n",
cache->name );
return -ENOMEM;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
@ -80,10 +172,26 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
dhcppkt_init ( dhcppkt, dhcphdr, len );
/* Store as cached DHCPACK, and mark original copy as consumed */
DBGC ( colour, "CACHEDHCP found cached DHCPACK at %#08lx+%#zx/%#zx\n",
/* Discard duplicate packets, since some PXE stacks (including
* iPXE itself) will report the DHCPACK packet as the PXEBSACK
* if no separate PXEBSACK exists.
*/
for ( i = 0 ; i < ( sizeof ( cached_packets ) /
sizeof ( cached_packets[0] ) ) ; i++ ) {
tmp = cached_packets[i]->dhcppkt;
if ( tmp && ( dhcppkt_len ( tmp ) == len ) &&
( memcmp ( tmp->dhcphdr, dhcppkt->dhcphdr, len ) == 0 ) ) {
DBGC ( colour, "CACHEDHCP %s duplicates %s\n",
cache->name, cached_packets[i]->name );
dhcppkt_put ( dhcppkt );
return -EEXIST;
}
}
/* Store as cached packet */
DBGC ( colour, "CACHEDHCP %s at %#08lx+%#zx/%#zx\n", cache->name,
user_to_phys ( data, 0 ), len, max_len );
cached_dhcpack = dhcppkt;
cache->dhcppkt = dhcppkt;
return 0;
}
@ -94,14 +202,20 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
*/
static void cachedhcp_startup ( void ) {
/* If cached DHCP packet was not claimed by any network device
* during startup, then free it.
*/
if ( cached_dhcpack ) {
DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
dhcppkt_put ( cached_dhcpack );
cached_dhcpack = NULL;
/* Apply cached ProxyDHCPOFFER, if any */
cachedhcp_apply ( &cached_proxydhcp, NULL );
/* Apply cached PXEBSACK, if any */
cachedhcp_apply ( &cached_pxebs, NULL );
/* Free any remaining cached packets */
if ( cached_dhcpack.dhcppkt ) {
DBGC ( colour, "CACHEDHCP %s unclaimed\n",
cached_dhcpack.name );
}
cachedhcp_free ( &cached_dhcpack );
cachedhcp_free ( &cached_proxydhcp );
cachedhcp_free ( &cached_pxebs );
}
/** Cached DHCPACK startup function */
@ -117,38 +231,9 @@ struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
* @ret rc Return status code
*/
static int cachedhcp_probe ( struct net_device *netdev ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc;
/* Do nothing unless we have a cached DHCPACK */
if ( ! cached_dhcpack )
return 0;
/* Do nothing unless cached DHCPACK's MAC address matches this
* network device.
*/
if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr,
ll_protocol->ll_addr_len ) != 0 ) {
DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n",
netdev->name );
return 0;
}
DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name );
/* Register as DHCP settings for this network device */
if ( ( rc = register_settings ( &cached_dhcpack->settings,
netdev_settings ( netdev ),
DHCP_SETTINGS_NAME ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not register settings: %s\n",
strerror ( rc ) );
return rc;
}
/* Claim cached DHCPACK */
dhcppkt_put ( cached_dhcpack );
cached_dhcpack = NULL;
return 0;
/* Apply cached DHCPACK to network device, if applicable */
return cachedhcp_apply ( &cached_dhcpack, netdev );
}
/** Cached DHCP packet network device driver */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/if_ether.h>
#include <ipxe/base16.h>
#include <ipxe/profile.h>
#include <ipxe/acpimac.h>
#include <ipxe/usb.h>
#include "ecm.h"
@ -81,17 +82,26 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
/**
* Get hardware MAC address
*
* @v usb USB device
* @v func USB function
* @v desc Ethernet functional descriptor
* @v hw_addr Hardware address to fill in
* @ret rc Return status code
*/
int ecm_fetch_mac ( struct usb_device *usb,
int ecm_fetch_mac ( struct usb_function *func,
struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) {
struct usb_device *usb = func->usb;
char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ];
int len;
int rc;
/* Use system-specific MAC address, if present and not already used */
if ( ( ( rc = acpi_mac ( hw_addr ) ) == 0 ) &&
! find_netdev_by_ll_addr ( &ethernet_protocol, hw_addr ) ) {
DBGC ( usb, "USB %s using system-specific MAC %s\n",
func->name, eth_ntoa ( hw_addr ) );
return 0;
}
/* Fetch MAC address string */
len = usb_get_string_descriptor ( usb, desc->mac, 0, buf,
sizeof ( buf ) );
@ -103,7 +113,7 @@ int ecm_fetch_mac ( struct usb_device *usb,
/* Sanity check */
if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) {
DBGC ( usb, "USB %s has invalid ECM MAC \"%s\"\n",
usb->name, buf );
func->name, buf );
return -EINVAL;
}
@ -112,7 +122,7 @@ int ecm_fetch_mac ( struct usb_device *usb,
if ( len < 0 ) {
rc = len;
DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n",
usb->name, buf, strerror ( rc ) );
func->name, buf, strerror ( rc ) );
return rc;
}
@ -464,7 +474,7 @@ static int ecm_probe ( struct usb_function *func,
}
/* Fetch MAC address */
if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) {
DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n",
ecm, strerror ( rc ) );
goto err_fetch_mac;

View File

@ -86,7 +86,7 @@ struct ecm_device {
extern struct ecm_ethernet_descriptor *
ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
struct usb_interface_descriptor *interface );
extern int ecm_fetch_mac ( struct usb_device *usb,
extern int ecm_fetch_mac ( struct usb_function *func,
struct ecm_ethernet_descriptor *desc,
uint8_t *hw_addr );

View File

@ -598,7 +598,7 @@ static int ncm_probe ( struct usb_function *func,
}
/* Fetch MAC address */
if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) {
DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n",
ncm, strerror ( rc ) );
goto err_fetch_mac;

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -387,7 +387,9 @@ acpi_describe ( struct interface *interface );
typeof ( struct acpi_descriptor * ( object_type ) )
extern void acpi_fix_checksum ( struct acpi_header *acpi );
extern int acpi_sx ( uint32_t signature );
extern int acpi_extract ( uint32_t signature, void *data,
int ( * extract ) ( userptr_t zsdt, size_t len,
size_t offset, void *data ) );
extern void acpi_add ( struct acpi_descriptor *desc );
extern void acpi_del ( struct acpi_descriptor *desc );
extern int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) );

View File

@ -0,0 +1,14 @@
#ifndef _IPXE_ACPIMAC_H
#define _IPXE_ACPIMAC_H
/** @file
*
* ACPI MAC address
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
extern int acpi_mac ( uint8_t *hw_addr );
#endif /* _IPXE_ACPIMAC_H */

View File

@ -12,6 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <ipxe/uaccess.h>
extern int cachedhcp_record ( userptr_t data, size_t max_len );
struct cached_dhcp_packet;
extern struct cached_dhcp_packet cached_dhcpack;
extern struct cached_dhcp_packet cached_proxydhcp;
extern struct cached_dhcp_packet cached_pxebs;
extern int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
size_t max_len );
#endif /* _IPXE_CACHEDHCP_H */

View File

@ -56,7 +56,7 @@ dhcppkt_put ( struct dhcp_packet *dhcppkt ) {
* @v dhcppkt DHCP packet
* @ret len Used length
*/
static inline int dhcppkt_len ( struct dhcp_packet *dhcppkt ) {
static inline size_t dhcppkt_len ( struct dhcp_packet *dhcppkt ) {
return ( offsetof ( struct dhcphdr, options ) +
dhcppkt->options.used_len );
}

View File

@ -77,6 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_fdt ( ERRFILE_CORE | 0x00250000 )
#define ERRFILE_dma ( ERRFILE_CORE | 0x00260000 )
#define ERRFILE_cachedhcp ( ERRFILE_CORE | 0x00270000 )
#define ERRFILE_acpimac ( ERRFILE_CORE | 0x00280000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
@ -213,6 +214,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_usbblk ( ERRFILE_DRIVER | 0x00ce0000 )
#define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 )
#define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 )
#define ERRFILE_rdc ( ERRFILE_DRIVER | 0x00d10000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -75,17 +75,40 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
/* Record DHCPACK, if present */
if ( mode->DhcpAckReceived &&
( ( rc = cachedhcp_record ( virt_to_user ( &mode->DhcpAck ),
( ( rc = cachedhcp_record ( &cached_dhcpack,
virt_to_user ( &mode->DhcpAck ),
sizeof ( mode->DhcpAck ) ) ) != 0 ) ) {
DBGC ( device, "EFI %s could not record DHCPACK: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_record;
goto err_dhcpack;
}
/* Record ProxyDHCPOFFER, if present */
if ( mode->ProxyOfferReceived &&
( ( rc = cachedhcp_record ( &cached_proxydhcp,
virt_to_user ( &mode->ProxyOffer ),
sizeof ( mode->ProxyOffer ) ) ) != 0)){
DBGC ( device, "EFI %s could not record ProxyDHCPOFFER: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_proxydhcp;
}
/* Record PxeBSACK, if present */
if ( mode->PxeReplyReceived &&
( ( rc = cachedhcp_record ( &cached_pxebs,
virt_to_user ( &mode->PxeReply ),
sizeof ( mode->PxeReply ) ) ) != 0)){
DBGC ( device, "EFI %s could not record PXEBSACK: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_pxebs;
}
/* Success */
rc = 0;
err_record:
err_pxebs:
err_proxydhcp:
err_dhcpack:
err_ipv6:
bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
efi_image_handle, NULL );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -255,7 +255,7 @@ fi
# Create FAT filesystem image, if applicable
#
if [ -n "${FATIMG}" ] ; then
FATSIZE=$(du -s -k ${FATDIR} | cut -f1)
FATSIZE=$(du -s -k "${FATDIR}" | cut -f1)
FATSIZE=$(( FATSIZE + PAD + 256 ))
touch "${FATIMG}"
if [ "${FATSIZE}" -le "1440" ] ; then