From 7c8fc2cae8769fff7d9fe8af3cef046995a3fd3e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 11 Mar 2021 15:08:57 +0000 Subject: [linux] Fail at link time if building slirp.linux without libslirp The iPXE build system is constructed for a standalone codebase with no external dependencies, and does not have any equivalent of the standard userspace ./configure script. We currently check for the ability to include slirp/libslirp.h and conditionalise portions of linux_api.c on its presence. The actual slirp driver code is built unconditionally, as with all iPXE drivers. This currently leads to a silent runtime failure if attempting to use slirp.linux built on a system that was missing slirp/libslirp.h. Convert this to a link-time failure by deliberately omitting the relevant symbols from linux_api.c when slirp/libslirp.h is not present. This allows other builds (e.g. tap.linux or tests.linux) to succeed: the link-time failure will occur only if the slirp driver is included within the build target. Signed-off-by: Michael Brown --- src/interface/linux/linux_api.c | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/interface/linux/linux_api.c b/src/interface/linux/linux_api.c index d1f969aa7..21024ede1 100644 --- a/src/interface/linux/linux_api.c +++ b/src/interface/linux/linux_api.c @@ -486,35 +486,6 @@ linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error, slirp_pollfds_poll ( slirp, select_error, get_revents, opaque ); } -#else /* HAVE_LIBSLIRP */ - -struct Slirp * __asmcall -linux_slirp_new ( const struct slirp_config *config, - const struct slirp_callbacks *callbacks, void *opaque ) { - return NULL; -} - -void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ) { -} - -void __asmcall linux_slirp_input ( struct Slirp *slirp, const uint8_t *pkt, - int pkt_len ) { -} - -void __asmcall -linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout, - int ( __asmcall * add_poll ) ( int fd, int events, - void *opaque ), - void *opaque ) { -} - -void __asmcall -linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error, - int ( __asmcall * get_revents ) ( int idx, - void *opaque ), - void *opaque ) { -} - #endif /* HAVE_LIBSLIRP */ /****************************************************************************** @@ -544,8 +515,11 @@ PROVIDE_IPXE_SYM ( linux_socket ); PROVIDE_IPXE_SYM ( linux_bind ); PROVIDE_IPXE_SYM ( linux_sendto ); PROVIDE_IPXE_SYM ( linux_strerror ); + +#ifdef HAVE_LIBSLIRP PROVIDE_IPXE_SYM ( linux_slirp_new ); PROVIDE_IPXE_SYM ( linux_slirp_cleanup ); PROVIDE_IPXE_SYM ( linux_slirp_input ); PROVIDE_IPXE_SYM ( linux_slirp_pollfds_fill ); PROVIDE_IPXE_SYM ( linux_slirp_pollfds_poll ); +#endif /* HAVE_LIBSLIRP */ -- cgit v1.2.3-55-g7522 From 1192edf394aaebde666c6aafda1c8b782b08a845 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 11 Mar 2021 15:54:26 +0000 Subject: [dhcp] Handle DHCPNAK by returning to discovery state Handle a DHCPNAK by returning to the discovery state to allow iPXE to attempt to obtain a replacement IPv4 address. Reuse the existing logic for deferring discovery when the link is blocked: this avoids hammering a misconfigured DHCP server with a non-stop stream of requests and allows the DHCP process to eventually time out and fail. Originally-implemented-by: Blake Rouse Signed-off-by: Michael Brown --- src/net/udp/dhcp.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 4ebd38bbb..a335a778a 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -443,6 +443,26 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, dhcp_set_state ( dhcp, &dhcp_state_request ); } +/** + * Defer DHCP discovery + * + * @v dhcp DHCP session + */ +static void dhcp_defer ( struct dhcp_session *dhcp ) { + + /* Do nothing if we have reached the deferral limit */ + if ( dhcp->count > DHCP_DISC_MAX_DEFERRALS ) + return; + + /* Return to discovery state */ + DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp ); + dhcp_set_state ( dhcp, &dhcp_state_discover ); + + /* Delay first DHCPDISCOVER */ + start_timer_fixed ( &dhcp->timer, + ( DHCP_DISC_START_TIMEOUT_SEC * TICKS_PER_SEC ) ); +} + /** * Handle timer expiry during DHCP discovery * @@ -462,14 +482,8 @@ static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) { dhcp_tx ( dhcp ); /* If link is blocked, defer DHCP discovery timeout */ - if ( netdev_link_blocked ( dhcp->netdev ) && - ( dhcp->count <= DHCP_DISC_MAX_DEFERRALS ) ) { - DBGC ( dhcp, "DHCP %p deferring discovery timeout\n", dhcp ); - dhcp->start = currticks(); - start_timer_fixed ( &dhcp->timer, - ( DHCP_DISC_START_TIMEOUT_SEC * - TICKS_PER_SEC ) ); - } + if ( netdev_link_blocked ( dhcp->netdev ) ) + dhcp_defer ( dhcp ); } /** DHCP discovery state operations */ @@ -553,9 +567,17 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp, DBGC ( dhcp, " for %s", inet_ntoa ( ip ) ); DBGC ( dhcp, "\n" ); - /* Filter out unacceptable responses */ + /* Filter out invalid port */ if ( peer->sin_port != htons ( BOOTPS_PORT ) ) return; + + /* Handle DHCPNAK */ + if ( msgtype == DHCPNAK ) { + dhcp_defer ( dhcp ); + return; + } + + /* Filter out unacceptable responses */ if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) ) return; if ( server_id.s_addr != dhcp->server.s_addr ) -- cgit v1.2.3-55-g7522 From 1cc87565110014205f647fd413f897a1a5a04b22 Mon Sep 17 00:00:00 2001 From: Marvin Häuser Date: Thu, 8 Apr 2021 19:58:35 +0200 Subject: [efi] Discard .pci_devlist.* sections for EFI images As per https://github.com/ipxe/ipxe/pull/313#issuecomment-816018398, these sections are not required for EFI execution. Discard them to avoid implementation-defined alignment malforming binaries. Signed-off-by: Marvin Häuser --- src/scripts/efi.lds | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripts/efi.lds b/src/scripts/efi.lds index f1049f24b..36022545d 100644 --- a/src/scripts/efi.lds +++ b/src/scripts/efi.lds @@ -106,5 +106,6 @@ SECTIONS { *(.einfo.*) *(.discard) *(.discard.*) + *(.pci_devlist.*) } } -- cgit v1.2.3-55-g7522 From f1e9e2b062fab46a6e3aec1f08d4554dd5dd2b98 Mon Sep 17 00:00:00 2001 From: Marvin Häuser Date: Thu, 8 Apr 2021 20:04:16 +0200 Subject: [efi] Align EFI image sections by page size For optimal memory permission management, PE sections need to be aligned by the platform's minimum page size. Currently, the PE section alignment is fixed to 32 bytes, which is below the typical 4kB page size. Align all sections to 4kB and adjust ELF to PE image conversion accordingly. Signed-off-by: Marvin Häuser --- src/scripts/efi.lds | 18 +++++++++--------- src/util/elf2efi.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/scripts/efi.lds b/src/scripts/efi.lds index 36022545d..dd7b3f019 100644 --- a/src/scripts/efi.lds +++ b/src/scripts/efi.lds @@ -8,22 +8,22 @@ SECTIONS { /* The file starts at a virtual address of zero, and sections are - * contiguous. Each section is aligned to at least _max_align, - * which defaults to 32. Load addresses are equal to virtual + * contiguous. Each section is aligned to at least _page_align, + * which defaults to 4096. Load addresses are equal to virtual * addresses. */ - _max_align = 32; + _page_align = 4096; - /* Allow plenty of space for file headers */ - . = 0x1000; + /* Allow one page of space for file headers, common PE/COFF layout */ + . = _page_align; /* * The text section * */ - . = ALIGN ( _max_align ); + . = ALIGN ( _page_align ); .text : { _text = .; *(.text) @@ -36,7 +36,7 @@ SECTIONS { * */ - . = ALIGN ( _max_align ); + . = ALIGN ( _page_align ); .rodata : { _rodata = .; *(.rodata) @@ -49,7 +49,7 @@ SECTIONS { * */ - . = ALIGN ( _max_align ); + . = ALIGN ( _page_align ); .data : { _data = .; *(.data) @@ -65,7 +65,7 @@ SECTIONS { * */ - . = ALIGN ( _max_align ); + . = ALIGN ( _page_align ); .bss : { _bss = .; *(.bss) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 38eb29964..b0d546645 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -125,7 +125,8 @@ #define R_ARM_V4BX 40 #endif -#define EFI_FILE_ALIGN 0x20 +#define EFI_FILE_ALIGN 0x20 +#define EFI_IMAGE_ALIGN 0x1000 struct elf_file { void *data; @@ -173,9 +174,9 @@ static struct pe_header efi_pe_header = { .Magic = EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC, .MajorLinkerVersion = 42, .MinorLinkerVersion = 42, - .SectionAlignment = EFI_FILE_ALIGN, + .SectionAlignment = EFI_IMAGE_ALIGN, .FileAlignment = EFI_FILE_ALIGN, - .SizeOfImage = sizeof ( efi_pe_header ), + .SizeOfImage = EFI_IMAGE_ALIGN, .SizeOfHeaders = sizeof ( efi_pe_header ), .NumberOfRvaAndSizes = EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, @@ -216,6 +217,16 @@ static unsigned long efi_file_align ( unsigned long offset ) { return ( ( offset + EFI_FILE_ALIGN - 1 ) & ~( EFI_FILE_ALIGN - 1 ) ); } +/** + * Align section within PE image + * + * @v offset Unaligned offset + * @ret aligned_offset Aligned offset + */ +static unsigned long efi_image_align ( unsigned long offset ) { + return ( ( offset + EFI_IMAGE_ALIGN - 1 ) & ~( EFI_IMAGE_ALIGN - 1 ) ); +} + /** * Generate entry in PE relocation table * @@ -605,7 +616,7 @@ static struct pe_section * process_section ( struct elf_file *elf, pe_header->nt.FileHeader.NumberOfSections++; pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr ); pe_header->nt.OptionalHeader.SizeOfImage = - efi_file_align ( data_end ); + efi_image_align ( data_end ); return new; } @@ -728,13 +739,15 @@ static struct pe_section * create_reloc_section ( struct pe_header *pe_header, struct pe_relocs *pe_reltab ) { struct pe_section *reloc; + size_t section_rawsz; size_t section_memsz; size_t section_filesz; EFI_IMAGE_DATA_DIRECTORY *relocdir; /* Allocate PE section */ - section_memsz = output_pe_reltab ( pe_reltab, NULL ); - section_filesz = efi_file_align ( section_memsz ); + section_rawsz = output_pe_reltab ( pe_reltab, NULL ); + section_filesz = efi_file_align ( section_rawsz ); + section_memsz = efi_image_align ( section_rawsz ); reloc = xmalloc ( sizeof ( *reloc ) + section_filesz ); memset ( reloc, 0, sizeof ( *reloc ) + section_filesz ); @@ -754,11 +767,11 @@ create_reloc_section ( struct pe_header *pe_header, /* Update file header details */ pe_header->nt.FileHeader.NumberOfSections++; pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr ); - pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; + pe_header->nt.OptionalHeader.SizeOfImage += section_memsz; relocdir = &(pe_header->nt.OptionalHeader.DataDirectory [EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]); relocdir->VirtualAddress = reloc->hdr.VirtualAddress; - relocdir->Size = reloc->hdr.Misc.VirtualSize; + relocdir->Size = section_rawsz; return reloc; } @@ -796,8 +809,8 @@ create_debug_section ( struct pe_header *pe_header, const char *filename ) { } *contents; /* Allocate PE section */ - section_memsz = sizeof ( *contents ); - section_filesz = efi_file_align ( section_memsz ); + section_memsz = efi_image_align ( sizeof ( *contents ) ); + section_filesz = efi_file_align ( sizeof ( *contents ) ); debug = xmalloc ( sizeof ( *debug ) + section_filesz ); memset ( debug, 0, sizeof ( *debug ) + section_filesz ); contents = ( void * ) debug->contents; @@ -828,7 +841,7 @@ create_debug_section ( struct pe_header *pe_header, const char *filename ) { /* Update file header details */ pe_header->nt.FileHeader.NumberOfSections++; pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( debug->hdr ); - pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; + pe_header->nt.OptionalHeader.SizeOfImage += section_memsz; debugdir = &(pe_header->nt.OptionalHeader.DataDirectory [EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); debugdir->VirtualAddress = debug->hdr.VirtualAddress; -- cgit v1.2.3-55-g7522 From 94245624e449d4d65223c00055be3d7ea04983f9 Mon Sep 17 00:00:00 2001 From: Marvin Häuser Date: Mon, 5 Apr 2021 16:45:07 +0200 Subject: [efi] Mark PE .reloc and .debug sections as discardable After a PE image is fully loaded and relocated, the loader code may opt to zero discardable sections for security reasons. This includes relocation and debug information, as both contain hints about specific locations within the binary. Mark both generated sections as discardable, which follows the PE specification. Signed-off-by: Marvin Häuser --- src/util/elf2efi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index b0d546645..5542b99f8 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -758,6 +758,7 @@ create_reloc_section ( struct pe_header *pe_header, reloc->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; reloc->hdr.SizeOfRawData = section_filesz; reloc->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_DISCARDABLE | EFI_IMAGE_SCN_MEM_NOT_PAGED | EFI_IMAGE_SCN_MEM_READ ); @@ -822,6 +823,7 @@ create_debug_section ( struct pe_header *pe_header, const char *filename ) { debug->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; debug->hdr.SizeOfRawData = section_filesz; debug->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_DISCARDABLE | EFI_IMAGE_SCN_MEM_NOT_PAGED | EFI_IMAGE_SCN_MEM_READ ); debug->fixup = fixup_debug_section; -- cgit v1.2.3-55-g7522 From c0346dbb49de0bb6c7637c511a175ce478aca9b9 Mon Sep 17 00:00:00 2001 From: Tyler J. Stachecki Date: Sun, 4 Apr 2021 10:13:59 -0400 Subject: [intel] Add additional PCI device ID to table Adding this missing identifier allows the X557-AT2 chipset seen on (at least) Super Micro A2SDI-H-TF motherboards to function with iPXE. Signed-off-by: Tyler J. Stachecki --- src/drivers/net/intelxvf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intelxvf.c b/src/drivers/net/intelxvf.c index f0ba091d5..d50bac698 100644 --- a/src/drivers/net/intelxvf.c +++ b/src/drivers/net/intelxvf.c @@ -530,6 +530,7 @@ static struct pci_device_id intelxvf_nics[] = { PCI_ROM ( 0x8086, 0x1515, "x540-vf", "X540 VF", 0 ), PCI_ROM ( 0x8086, 0x1565, "x550-vf", "X550 VF", 0 ), PCI_ROM ( 0x8086, 0x15a8, "x552-vf", "X552 VF", 0 ), + PCI_ROM ( 0x8086, 0x15c5, "x557-vf", "X557-AT2 VF", 0 ), }; /** PCI driver */ -- cgit v1.2.3-55-g7522 From 0be8491b717fec6697dbf6ef8ac07604e062ecb1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 10 Apr 2021 13:14:30 +0100 Subject: [pci] Avoid scanning nonexistent buses when using PCIAPI_DIRECT There is no method for obtaining the number of PCI buses when using PCIAPI_DIRECT, and we therefore currently scan all possible bus numbers. This can cause a several-second startup delay in some virtualised environments, since PCI configuration space access will necessarily require the involvement of the hypervisor. Ameliorate this situation by defaulting to scanning only a single bus, and expanding the number of PCI buses to accommodate any subordinate buses that are detected during enumeration. Signed-off-by: Michael Brown --- src/arch/x86/include/ipxe/pcidirect.h | 4 ++-- src/drivers/bus/pci.c | 27 +++++++++++++++++++++++++-- src/include/ipxe/pci.h | 3 +++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/arch/x86/include/ipxe/pcidirect.h b/src/arch/x86/include/ipxe/pcidirect.h index 9570fd7d6..decdc8100 100644 --- a/src/arch/x86/include/ipxe/pcidirect.h +++ b/src/arch/x86/include/ipxe/pcidirect.h @@ -32,8 +32,8 @@ extern void pcidirect_prepare ( struct pci_device *pci, int where ); */ static inline __always_inline int PCIAPI_INLINE ( direct, pci_num_bus ) ( void ) { - /* No way to work this out via Type 1 accesses */ - return 0x100; + /* Scan first bus and rely on bridge detection to find higher buses */ + return 1; } /** diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 06b36a770..1b7350c8b 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -228,6 +228,9 @@ int pci_read_config ( struct pci_device *pci ) { */ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { static unsigned int end; + unsigned int sub_end; + uint8_t hdrtype; + uint8_t sub; int rc; /* Determine number of PCI buses */ @@ -236,10 +239,30 @@ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { /* Find next PCI device, if any */ for ( ; busdevfn < end ; busdevfn++ ) { + + /* Check for PCI device existence */ memset ( pci, 0, sizeof ( *pci ) ); pci_init ( pci, busdevfn ); - if ( ( rc = pci_read_config ( pci ) ) == 0 ) - return busdevfn; + if ( ( rc = pci_read_config ( pci ) ) != 0 ) + continue; + + /* If device is a bridge, expand the number of PCI + * buses as needed. + */ + pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdrtype ); + hdrtype &= PCI_HEADER_TYPE_MASK; + if ( hdrtype == PCI_HEADER_TYPE_BRIDGE ) { + pci_read_config_byte ( pci, PCI_SUBORDINATE, &sub ); + sub_end = PCI_BUSDEVFN ( 0, ( sub + 1 ), 0, 0 ); + if ( end < sub_end ) { + DBGC ( pci, PCI_FMT " found subordinate bus " + "%#02x\n", PCI_ARGS ( pci ), sub ); + end = sub_end; + } + } + + /* Return this device */ + return busdevfn; } return -ENODEV; diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 6632c574d..933f48530 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -135,6 +135,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_CLASS_SERIAL_USB_EHCI 0x20 /**< ECHI USB controller */ #define PCI_CLASS_SERIAL_USB_XHCI 0x30 /**< xHCI USB controller */ +/** Subordinate bus number */ +#define PCI_SUBORDINATE 0x1a + /** Construct PCI class * * @v base Base class (or PCI_ANY_ID) -- cgit v1.2.3-55-g7522 From 78749542fc6d31ad41ecc350dea364e21362f985 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 10 Apr 2021 16:53:52 +0100 Subject: [netdevice] Ensure driver transmit() and poll() will not be re-entered When CONSOLE_SYSLOG is used, a DBG() from within a network device driver may cause its transmit() or poll() methods to be unexpectedly re-entered. Since these methods are not intended to be re-entrant, this can lead to undefined behaviour. Add an explicit re-entrancy guard to both methods. Note that this must operate at a per-netdevice level, since there are legitimate circumstances under which the netdev_tx() or netdev_poll() functions may be re-entered (e.g. when using VLAN devices). Signed-off-by: Michael Brown --- src/include/ipxe/netdevice.h | 6 ++++++ src/net/netdevice.c | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index b9c651c71..294f7b367 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -451,6 +451,12 @@ struct net_device { */ #define NETDEV_IRQ_UNSUPPORTED 0x0008 +/** Network device transmission is in progress */ +#define NETDEV_TX_IN_PROGRESS 0x0010 + +/** Network device poll is in progress */ +#define NETDEV_POLL_IN_PROGRESS 0x0020 + /** Link-layer protocol table */ #define LL_PROTOCOLS __table ( struct ll_protocol, "ll_protocols" ) diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 6e685630c..5df306e8d 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -297,30 +297,45 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { /* Enqueue packet */ list_add_tail ( &iobuf->list, &netdev->tx_queue ); + /* Guard against re-entry */ + if ( netdev->state & NETDEV_TX_IN_PROGRESS ) { + rc = -EBUSY; + goto err_busy; + } + netdev->state |= NETDEV_TX_IN_PROGRESS; + /* Avoid calling transmit() on unopened network devices */ if ( ! netdev_is_open ( netdev ) ) { rc = -ENETUNREACH; - goto err; + goto err_closed; } /* Discard packet (for test purposes) if applicable */ if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 ) - goto err; + goto err_fault; /* Map for DMA, if required */ if ( netdev->dma && ( ! dma_mapped ( &iobuf->map ) ) ) { if ( ( rc = iob_map_tx ( iobuf, netdev->dma ) ) != 0 ) - goto err; + goto err_map; } /* Transmit packet */ if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 ) - goto err; + goto err_transmit; + + /* Clear in-progress flag */ + netdev->state &= ~NETDEV_TX_IN_PROGRESS; profile_stop ( &net_tx_profiler ); return 0; - err: + err_transmit: + err_map: + err_fault: + err_closed: + netdev->state &= ~NETDEV_TX_IN_PROGRESS; + err_busy: netdev_tx_complete_err ( netdev, iobuf, rc ); return rc; } @@ -552,8 +567,18 @@ void netdev_rx_err ( struct net_device *netdev, */ void netdev_poll ( struct net_device *netdev ) { - if ( netdev_is_open ( netdev ) ) - netdev->op->poll ( netdev ); + /* Avoid calling poll() on unopened network devices */ + if ( ! netdev_is_open ( netdev ) ) + return; + + /* Guard against re-entry */ + if ( netdev->state & NETDEV_POLL_IN_PROGRESS ) + return; + + /* Poll device */ + netdev->state |= NETDEV_POLL_IN_PROGRESS; + netdev->op->poll ( netdev ); + netdev->state &= ~NETDEV_POLL_IN_PROGRESS; } /** -- cgit v1.2.3-55-g7522 From 3ae83222ce611afacd98419c0073aa98fb97f9e7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 10 Apr 2021 20:03:32 +0100 Subject: [cloud] Enable "poweroff" command in cloud images Signed-off-by: Michael Brown --- src/config/cloud/general.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/cloud/general.h b/src/config/cloud/general.h index 38799fd8e..fc881163a 100644 --- a/src/config/cloud/general.h +++ b/src/config/cloud/general.h @@ -6,3 +6,8 @@ * Google Compute Engine metadata server. */ #define HTTP_HACK_GCE + +/* Allow scripts to handle errors by powering down the VM to avoid + * incurring unnecessary costs. + */ +#define POWEROFF_CMD -- cgit v1.2.3-55-g7522 From 8ca43ccbc1984d60e50711ea326ca59ac03985d2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Apr 2021 20:30:20 +0100 Subject: [int13] Do not report INT 13 extension support for emulated floppies The INT 13 extensions provide a mechanism for accessing disks using linear (LBA) rather than C/H/S addressing. SAN protocols such as iSCSI invariably support only linear addresses and so iPXE currently provides LBA access to all SAN disks (with autodetection and emulation of an appropriate geometry for C/H/S accesses). Most BIOSes will not report support for INT 13 extensions for floppy disk drives, and some operating systems may be confused by a floppy drive that claims such support. Minimise surprise by reporting the existence of support for INT 13 extensions only for non-floppy drive numbers. Continue to provide support for all drive numbers, to avoid breaking operating systems that may unconditionally use the INT 13 extensions without first checking for support. Reported-by: Valdo Toost Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/int13.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c index 30530e197..d6c4d7ebf 100644 --- a/src/arch/x86/interface/pcbios/int13.c +++ b/src/arch/x86/interface/pcbios/int13.c @@ -678,10 +678,10 @@ static int int13_get_disk_type ( struct san_device *sandev, * @ret cx Extensions API support bitmap * @ret status Status code / API version */ -static int int13_extension_check ( struct san_device *sandev __unused, +static int int13_extension_check ( struct san_device *sandev, struct i386_all_regs *ix86 ) { - if ( ix86->regs.bx == 0x55aa ) { + if ( ( ix86->regs.bx == 0x55aa ) && ! int13_is_fdd ( sandev ) ) { DBGC2 ( sandev, "INT13 extensions installation check\n" ); ix86->regs.bx = 0xaa55; ix86->regs.cx = ( INT13_EXTENSION_LINEAR | -- cgit v1.2.3-55-g7522 From 85d179f2c65d0a2afe9122b844a90c011d551ae1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 14 Apr 2021 16:33:41 +0100 Subject: [xen] Support scatter-gather to allow for jumbo frames The use of jumbo frames for the Xen netfront virtual NIC requires the use of scatter-gather ("feature-sg"), with the receive descriptor ring becoming a list of page-sized buffers and the backend using as many page buffers as required for each packet. Since iPXE's abstraction of an I/O buffer does not include any sort of scatter-gather list, this requires an extra allocation and copy on the receive datapath for any packet that spans more than a single page. This support is required in order to successfully boot an AWS EC2 virtual machine (with non-enhanced networking) via iSCSI if jumbo frames are enabled, since the netback driver used in EC2 seems not to allow "feature-sg" to be renegotiated once the Linux kernel driver takes over. Signed-off-by: Michael Brown --- src/drivers/net/netfront.c | 188 +++++++++++++++++++++++++++++++------------- src/drivers/net/netfront.h | 16 +++- src/include/ipxe/xengrant.h | 7 +- 3 files changed, 154 insertions(+), 57 deletions(-) diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index be210850a..1203e585c 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -56,7 +56,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_uniqify ( EINFO_EIO, -NETIF_RSP_DROPPED, \ "Packet dropped" ) #define EIO_NETIF_RSP( status ) \ - EUNIQ ( EINFO_EIO, -(status), \ + EUNIQ ( EINFO_EIO, ( -(status) & 0x1f ), \ EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED ) /****************************************************************************** @@ -326,6 +326,7 @@ static int netfront_create_ring ( struct netfront_nic *netfront, struct netfront_ring *ring ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; + physaddr_t addr; unsigned int i; int rc; @@ -345,11 +346,11 @@ static int netfront_create_ring ( struct netfront_nic *netfront, } /* Grant access to shared ring */ + addr = virt_to_phys ( ring->sring.raw ); if ( ( rc = xengrant_permit_access ( xen, ring->ref, xendev->backend_id, - 0, ring->sring.raw ) ) != 0 ) { + 0, addr ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not permit access to " - "%#08lx: %s\n", xendev->key, - virt_to_phys ( ring->sring.raw ), strerror ( rc ) ); + "%#08lx: %s\n", xendev->key, addr, strerror ( rc ) ); goto err_permit_access; } @@ -358,10 +359,8 @@ static int netfront_create_ring ( struct netfront_nic *netfront, ring->ref ) ) != 0 ) goto err_write_num; - DBGC ( netfront, "NETFRONT %s %s=\"%d\" [%08lx,%08lx)\n", - xendev->key, ring->ref_key, ring->ref, - virt_to_phys ( ring->sring.raw ), - ( virt_to_phys ( ring->sring.raw ) + PAGE_SIZE ) ); + DBGC ( netfront, "NETFRONT %s %s=\"%d\" [%08lx,%08lx)\n", xendev->key, + ring->ref_key, ring->ref, addr, ( addr + PAGE_SIZE ) ); return 0; netfront_rm ( netfront, ring->ref_key ); @@ -378,7 +377,8 @@ static int netfront_create_ring ( struct netfront_nic *netfront, * * @v netfront Netfront device * @v ring Descriptor ring - * @v iobuf I/O buffer + * @v addr Physical address + * @v iobuf Associated I/O buffer, or NULL * @v id Buffer ID to fill in * @v ref Grant reference to fill in * @ret rc Return status code @@ -387,8 +387,9 @@ static int netfront_create_ring ( struct netfront_nic *netfront, * ring. */ static int netfront_push ( struct netfront_nic *netfront, - struct netfront_ring *ring, struct io_buffer *iobuf, - uint16_t *id, grant_ref_t *ref ) { + struct netfront_ring *ring, physaddr_t addr, + struct io_buffer *iobuf, uint16_t *id, + grant_ref_t *ref ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; unsigned int next_id; @@ -402,19 +403,15 @@ static int netfront_push ( struct netfront_nic *netfront, next_id = ring->ids[ ring->id_prod & ( ring->count - 1 ) ]; next_ref = ring->refs[next_id]; - /* Grant access to I/O buffer page. I/O buffers are naturally - * aligned, so we never need to worry about crossing a page - * boundary. - */ + /* Grant access to page containing address */ if ( ( rc = xengrant_permit_access ( xen, next_ref, xendev->backend_id, - 0, iobuf->data ) ) != 0 ) { + 0, addr ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not permit access to " - "%#08lx: %s\n", xendev->key, - virt_to_phys ( iobuf->data ), strerror ( rc ) ); + "%#08lx: %s\n", xendev->key, addr, strerror ( rc ) ); return rc; } - /* Store I/O buffer */ + /* Store associated I/O buffer, if any */ assert ( ring->iobufs[next_id] == NULL ); ring->iobufs[next_id] = iobuf; @@ -434,7 +431,7 @@ static int netfront_push ( struct netfront_nic *netfront, * @v netfront Netfront device * @v ring Descriptor ring * @v id Buffer ID - * @ret iobuf I/O buffer + * @ret iobuf Associated I/O buffer, if any */ static struct io_buffer * netfront_pull ( struct netfront_nic *netfront, struct netfront_ring *ring, @@ -451,7 +448,6 @@ static struct io_buffer * netfront_pull ( struct netfront_nic *netfront, /* Retrieve I/O buffer */ iobuf = ring->iobufs[id]; - assert ( iobuf != NULL ); ring->iobufs[id] = NULL; /* Free buffer ID */ @@ -494,6 +490,22 @@ static void netfront_destroy_ring ( struct netfront_nic *netfront, ring->sring.raw = NULL; } +/** + * Discard partially received I/O buffers + * + * @v netfront Netfront device + */ +static void netfront_discard ( struct netfront_nic *netfront ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Discard all buffers in the list */ + list_for_each_entry_safe ( iobuf, tmp, &netfront->rx_partial, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } +} + /****************************************************************************** * * Network device interface @@ -512,6 +524,7 @@ static void netfront_refill_rx ( struct net_device *netdev ) { struct io_buffer *iobuf; struct netif_rx_request *request; unsigned int refilled = 0; + physaddr_t addr; int notify; int rc; @@ -524,24 +537,24 @@ static void netfront_refill_rx ( struct net_device *netdev ) { /* Wait for next refill */ break; } + addr = virt_to_phys ( iobuf->data ); /* Add to descriptor ring */ request = RING_GET_REQUEST ( &netfront->rx_fring, netfront->rx_fring.req_prod_pvt ); - if ( ( rc = netfront_push ( netfront, &netfront->rx, + if ( ( rc = netfront_push ( netfront, &netfront->rx, addr, iobuf, &request->id, &request->gref ) ) != 0 ) { netdev_rx_err ( netdev, iobuf, rc ); break; } DBGC2 ( netfront, "NETFRONT %s RX id %d ref %d is %#08lx+%zx\n", - xendev->key, request->id, request->gref, - virt_to_phys ( iobuf->data ), iob_tailroom ( iobuf ) ); + xendev->key, request->id, request->gref, addr, + iob_tailroom ( iobuf ) ); /* Move to next descriptor */ netfront->rx_fring.req_prod_pvt++; refilled++; - } /* Push new descriptors and notify backend if applicable */ @@ -593,6 +606,10 @@ static int netfront_open ( struct net_device *netdev ) { if ( ( rc = netfront_write_flag ( netfront, "request-rx-copy" ) ) != 0 ) goto err_request_rx_copy; + /* Inform backend that we can support scatter-gather */ + if ( ( rc = netfront_write_flag ( netfront, "feature-sg" ) ) != 0 ) + goto err_feature_sg; + /* Disable checksum offload, since we will always do the work anyway */ if ( ( rc = netfront_write_flag ( netfront, "feature-no-csum-offload" ) ) != 0 ) @@ -632,6 +649,8 @@ static int netfront_open ( struct net_device *netdev ) { err_feature_rx_notify: netfront_rm ( netfront, "feature-no-csum-offload" ); err_feature_no_csum_offload: + netfront_rm ( netfront, "feature-sg" ); + err_feature_sg: netfront_rm ( netfront, "request-rx-copy" ); err_request_rx_copy: netfront_destroy_event ( netfront ); @@ -675,11 +694,15 @@ static void netfront_close ( struct net_device *netdev ) { /* Delete flags */ netfront_rm ( netfront, "feature-rx-notify" ); netfront_rm ( netfront, "feature-no-csum-offload" ); + netfront_rm ( netfront, "feature-sg" ); netfront_rm ( netfront, "request-rx-copy" ); /* Destroy event channel */ netfront_destroy_event ( netfront ); + /* Discard any partially received I/O buffers */ + netfront_discard ( netfront ); + /* Destroy receive descriptor ring, freeing any outstanding * I/O buffers. */ @@ -703,34 +726,66 @@ static int netfront_transmit ( struct net_device *netdev, struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; struct netif_tx_request *request; + physaddr_t addr; + size_t len; + size_t remaining; + size_t frag_len; + unsigned int offset; + unsigned int count; + unsigned int more; int notify; int rc; + /* Calculate number of page buffers required */ + addr = virt_to_phys ( iobuf->data ); + len = iob_len ( iobuf ); + offset = ( addr & ( PAGE_SIZE - 1 ) ); + count = ( ( offset + len + PAGE_SIZE - 1 ) / PAGE_SIZE ); + /* Check that we have space in the ring */ - if ( netfront_ring_is_full ( &netfront->tx ) ) { + if ( netfront_ring_space ( &netfront->tx ) < count ) { DBGC ( netfront, "NETFRONT %s out of transmit descriptors\n", xendev->key ); return -ENOBUFS; } /* Add to descriptor ring */ - request = RING_GET_REQUEST ( &netfront->tx_fring, - netfront->tx_fring.req_prod_pvt ); - if ( ( rc = netfront_push ( netfront, &netfront->tx, iobuf, - &request->id, &request->gref ) ) != 0 ) { - return rc; - } - request->offset = ( virt_to_phys ( iobuf->data ) & ( PAGE_SIZE - 1 ) ); - request->flags = NETTXF_data_validated; - request->size = iob_len ( iobuf ); - DBGC2 ( netfront, "NETFRONT %s TX id %d ref %d is %#08lx+%zx\n", - xendev->key, request->id, request->gref, - virt_to_phys ( iobuf->data ), iob_len ( iobuf ) ); + remaining = len; + while ( remaining ) { + + /* Calculate length of this fragment */ + frag_len = ( PAGE_SIZE - offset ); + if ( frag_len >= remaining ) { + frag_len = remaining; + more = 0; + } else { + more = NETTXF_more_data; + } - /* Consume descriptor */ - netfront->tx_fring.req_prod_pvt++; + /* Populate request */ + request = RING_GET_REQUEST ( &netfront->tx_fring, + netfront->tx_fring.req_prod_pvt ); + if ( ( rc = netfront_push ( netfront, &netfront->tx, addr, + ( more ? NULL : iobuf ), + &request->id, + &request->gref ) ) != 0 ) { + return rc; + } + request->flags = ( NETTXF_data_validated | more ); + request->offset = offset; + request->size = ( ( remaining == len ) ? len : frag_len ); + DBGC2 ( netfront, "NETFRONT %s TX id %d ref %d is " + "%#08lx+%zx%s\n", xendev->key, request->id, + request->gref, addr, frag_len, ( more ? "..." : "" ) ); + + /* Move to next descriptor */ + netfront->tx_fring.req_prod_pvt++; + addr += frag_len; + remaining -= frag_len; + offset = 0; + } - /* Push new descriptor and notify backend if applicable */ + /* Push new descriptors and notify backend if applicable */ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->tx_fring, notify ); if ( notify ) netfront_send_event ( netfront ); @@ -748,7 +803,7 @@ static void netfront_poll_tx ( struct net_device *netdev ) { struct xen_device *xendev = netfront->xendev; struct netif_tx_response *response; struct io_buffer *iobuf; - unsigned int status; + int status; int rc; /* Consume any unconsumed responses */ @@ -761,10 +816,11 @@ static void netfront_poll_tx ( struct net_device *netdev ) { /* Retrieve from descriptor ring */ iobuf = netfront_pull ( netfront, &netfront->tx, response->id ); status = response->status; - if ( status == NETIF_RSP_OKAY ) { + if ( status >= NETIF_RSP_OKAY ) { DBGC2 ( netfront, "NETFRONT %s TX id %d complete\n", xendev->key, response->id ); - netdev_tx_complete ( netdev, iobuf ); + if ( iobuf ) + netdev_tx_complete ( netdev, iobuf ); } else { rc = -EIO_NETIF_RSP ( status ); DBGC2 ( netfront, "NETFRONT %s TX id %d error %d: %s\n", @@ -786,6 +842,7 @@ static void netfront_poll_rx ( struct net_device *netdev ) { struct netif_rx_response *response; struct io_buffer *iobuf; int status; + int more; size_t len; int rc; @@ -799,21 +856,45 @@ static void netfront_poll_rx ( struct net_device *netdev ) { /* Retrieve from descriptor ring */ iobuf = netfront_pull ( netfront, &netfront->rx, response->id ); status = response->status; - if ( status >= 0 ) { - len = status; - iob_reserve ( iobuf, response->offset ); - iob_put ( iobuf, len ); - DBGC2 ( netfront, "NETFRONT %s RX id %d complete " - "%#08lx+%zx\n", xendev->key, response->id, - virt_to_phys ( iobuf->data ), len ); - netdev_rx ( netdev, iobuf ); - } else { + more = ( response->flags & NETRXF_more_data ); + + /* Report errors */ + if ( status < 0 ) { rc = -EIO_NETIF_RSP ( status ); DBGC2 ( netfront, "NETFRONT %s RX id %d error %d: %s\n", xendev->key, response->id, status, strerror ( rc ) ); + netfront_discard ( netfront ); netdev_rx_err ( netdev, iobuf, rc ); + continue; } + + /* Add to partial receive list */ + len = status; + iob_reserve ( iobuf, response->offset ); + iob_put ( iobuf, len ); + DBGC2 ( netfront, "NETFRONT %s RX id %d complete " + "%#08lx+%zx%s\n", xendev->key, response->id, + virt_to_phys ( iobuf->data ), len, + ( more ? "..." : "" ) ); + list_add_tail ( &iobuf->list, &netfront->rx_partial ); + + /* Wait until complete packet has been received */ + if ( more ) + continue; + + /* Reassemble complete packet */ + iobuf = iob_concatenate ( &netfront->rx_partial ); + if ( ! iobuf ) { + DBGC2 ( netfront, "NETFRONT %s RX reassembly failed\n", + xendev->key ); + netfront_discard ( netfront ); + netdev_rx_err ( netdev, NULL, -ENOMEM ); + continue; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iobuf ); } } @@ -871,6 +952,7 @@ static int netfront_probe ( struct xen_device *xendev ) { netdev->dev = &xendev->dev; netfront = netdev->priv; netfront->xendev = xendev; + INIT_LIST_HEAD ( &netfront->rx_partial ); DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n", xendev->key, xendev->backend, xendev->backend_id ); diff --git a/src/drivers/net/netfront.h b/src/drivers/net/netfront.h index c95ed2645..dca3ff1c5 100644 --- a/src/drivers/net/netfront.h +++ b/src/drivers/net/netfront.h @@ -65,7 +65,7 @@ struct netfront_ring { size_t count; /** I/O buffers, indexed by buffer ID */ struct io_buffer **iobufs; - /** I/O buffer grant references, indexed by buffer ID */ + /** Grant references, indexed by buffer ID */ grant_ref_t *refs; /** Buffer ID ring */ @@ -116,6 +116,18 @@ netfront_ring_fill ( struct netfront_ring *ring ) { return fill_level; } +/** + * Calculate descriptor ring remaining space + * + * @v ring Descriptor ring + * @v space Number of unused entries + */ +static inline __attribute__ (( always_inline )) unsigned int +netfront_ring_space ( struct netfront_ring *ring ) { + + return ( ring->count - netfront_ring_fill ( ring ) ); +} + /** * Check whether or not descriptor ring is full * @@ -164,6 +176,8 @@ struct netfront_nic { struct io_buffer *rx_iobufs[NETFRONT_NUM_RX_DESC]; /** Receive I/O buffer IDs */ uint8_t rx_ids[NETFRONT_NUM_RX_DESC]; + /** Partial receive I/O buffer list */ + struct list_head rx_partial; /** Event channel */ struct evtchn_send event; diff --git a/src/include/ipxe/xengrant.h b/src/include/ipxe/xengrant.h index 451a3ceee..fcb7a7157 100644 --- a/src/include/ipxe/xengrant.h +++ b/src/include/ipxe/xengrant.h @@ -166,16 +166,17 @@ xengrant_invalidate ( struct xen_hypervisor *xen, grant_ref_t ref ) { * @v ref Grant reference * @v domid Domain ID * @v subflags Additional flags - * @v page Page start + * @v addr Physical address within page * @ret rc Return status code */ static inline __attribute__ (( always_inline )) int xengrant_permit_access ( struct xen_hypervisor *xen, grant_ref_t ref, - domid_t domid, unsigned int subflags, void *page ) { + domid_t domid, unsigned int subflags, + physaddr_t addr ) { struct grant_entry_header *hdr = xengrant_header ( xen, ref ); struct grant_entry_v1 *v1 = xengrant_v1 ( hdr ); union grant_entry_v2 *v2 = xengrant_v2 ( hdr ); - unsigned long frame = ( virt_to_phys ( page ) / PAGE_SIZE ); + unsigned long frame = ( addr / PAGE_SIZE ); /* Fail (for test purposes) if applicable */ if ( ( XENGRANT_FAIL_RATE > 0 ) && -- cgit v1.2.3-55-g7522