Compare commits

...

14 Commits

Author SHA1 Message Date
81e3351b9b [efi] Disable EFI watchdog timer when shutting down to boot an OS
The UEFI specification mandates that the EFI watchdog timer should be
disabled by the platform firmware as part of the ExitBootServices()
call, but some platforms (e.g. Hyper-V) are observed to occasionally
forget to do so, resulting in a reboot approximately five minutes
after starting the operating system.

Work around these firmware bugs by disabling the watchdog timer
ourselves.

Requested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-24 15:43:46 +00:00
562c74e1ea [efi] Run ExitBootServices shutdown hook at TPL_NOTIFY
On some systems (observed with the Thunderbolt ports on a ThinkPad X1
Extreme Gen3 and a ThinkPad P53), if the IOMMU is enabled then the
system firmware will install an ExitBootServices notification event
that disables bus mastering on the Thunderbolt xHCI controller and all
PCI bridges, and destroys any extant IOMMU mappings.  This leaves the
xHCI controller unable to perform any DMA operations.

As described in commit 236299b ("[xhci] Avoid DMA during shutdown if
firmware has disabled bus mastering"), any subsequent DMA operation
attempted by the xHCI controller will end up completing after the
operating system kernel has reenabled bus mastering, resulting in a
DMA operation to an area of memory that the hardware is no longer
permitted to access and, on Windows with the Driver Verifier enabled,
a STOP 0xE6 (DRIVER_VERIFIER_DMA_VIOLATION).

That commit avoids triggering any DMA attempts during the shutdown of
the xHCI controller itself.  However, this is not a complete solution
since any attached and opened USB device (e.g. a USB NIC) may
asynchronously trigger DMA attempts that happen to occur after bus
mastering has been disabled but before we reset the xHCI controller.

Avoid this problem by installing our own ExitBootServices notification
event at TPL_NOTIFY, thereby causing it to be invoked before the
firmware's own ExitBootServices notification event that disables bus
mastering.

This unsurprisingly causes the shutdown hook itself to be invoked at
TPL_NOTIFY, which causes a fatal error when later code attempts to
raise the TPL to TPL_CALLBACK (which is a lower TPL).  Work around
this problem by redefining the "internal" iPXE TPL to be variable, and
set this internal TPL to TPL_NOTIFY when the shutdown hook is invoked.

Avoid calling into an underlying SNP protocol instance from within our
shutdown hook at TPL_NOTIFY, since the underlying SNP driver may
attempt to raise the TPL to TPL_CALLBACK (which would cause a fatal
error).  Failing to shut down the underlying SNP device is safe to do
since the underlying device must, in any case, have installed its own
ExitBootServices hook if any shutdown actions are required.

Reported-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-23 15:55:01 +00:00
0f4cc4b5a7 [build] Include EFI system partition table entry in isohybrid images
Add the "--uefi" option when invoking isohybrid on an EFI-bootable
image, to create a partition mapping to the EFI system partition
embedded within the ISO image.

This allows the resulting isohybrid image to be booted on UEFI systems
that will not recognise an El Torito boot catalog on a non-CDROM
device.

Originally-fixed-by: Christian Hesse <mail@eworm.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-23 15:26:55 +00:00
a046da21a4 [efi] Raise TPL during driver unload entry point
The efi_unload() function is currently missing the calls to raise and
restore the TPL.  This has the side effect of causing iPXE to return
from the driver unload entry point at TPL_CALLBACK, which will cause
unexpected behaviour (typically a system lockup) shortly afterwards.

Fix by adding the missing calls to raise and restore the TPL.

Debugged-by: Petr Borsodi <petr.borsodi@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-22 12:50:38 +00:00
3ad27fbe78 [intel] Add PCI ID for Intel X553 0x15e4
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-22 12:42:18 +00:00
b6045a8cbb [efi] Modify global system table when wrapping a loaded image
The EFI loaded image protocol allows an image to be provided with a
custom system table, and we currently use this mechanism to wrap any
boot services calls made by the loaded image in order to provide
strace-like debugging via DEBUG=efi_wrap.

The ExitBootServices() call will modify the global system table,
leaving the loaded image using a system table that is no longer
current.  When DEBUG=efi_wrap is used, this generally results in the
machine locking up at the point that the loaded operating system calls
ExitBootServices().

Fix by modifying the global EFI system table to point to our wrapper
functions, instead of providing a custom system table via the loaded
image protocol.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-21 13:34:10 +00:00
51612b6e69 [efi] Do not attempt to use console output after ExitBootServices()
A successful call to ExitBootServices() will result in the EFI console
becoming unusable.  Ensure that the EFI wrapper produces a complete
line of debug output before calling the wrapped ExitBootServices()
method, and attempt subsequent debug output only if the call fails.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-21 13:24:24 +00:00
236299baa3 [xhci] Avoid DMA during shutdown if firmware has disabled bus mastering
On some systems (observed with the Thunderbolt ports on a ThinkPad X1
Extreme Gen3 and a ThinkPad P53), the system firmware will disable bus
mastering on the xHCI controller and all PCI bridges at the point that
ExitBootServices() is called if the IOMMU is enabled.  This leaves the
xHCI controller unable to shut down cleanly since all commands will
fail with a timeout.

Commit 85eb961 ("[xhci] Allow for permanent failure of the command
mechanism") allows us to detect that this has happened and respond
cleanly.  However, some unidentified hardware component (either the
xHCI controller or one of the PCI bridges) seems to manage to enqueue
the attempted DMA operation and eventually complete it after the
operating system kernel has reenabled bus mastering.  This results in
a DMA operation to an area of memory that the hardware is no longer
permitted to access.  On Windows with the Driver Verifier enabled,
this will result in a STOP 0xE6 (DRIVER_VERIFIER_DMA_VIOLATION).

Work around this problem by detecting when bus mastering has been
disabled, and immediately failing the device to avoid initiating any
further DMA attempts.

Reported-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-11-12 22:27:25 +00:00
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
27 changed files with 468 additions and 180 deletions

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

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

@ -576,7 +576,7 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb,
cdb.IFnum = nii->nii->IfNum;
/* Raise task priority level */
tpl = bs->RaiseTPL ( TPL_CALLBACK );
tpl = bs->RaiseTPL ( efi_internal_tpl );
/* Issue command */
DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n",

View File

@ -164,6 +164,10 @@ static int snpnet_transmit ( struct net_device *netdev,
EFI_STATUS efirc;
int rc;
/* Do nothing if shutdown is in progress */
if ( efi_shutdown_in_progress )
return -ECANCELED;
/* Defer the packet if there is already a transmission in progress */
if ( snp->txbuf ) {
netdev_tx_defer ( netdev, iobuf );
@ -283,6 +287,10 @@ static void snpnet_poll_rx ( struct net_device *netdev ) {
*/
static void snpnet_poll ( struct net_device *netdev ) {
/* Do nothing if shutdown is in progress */
if ( efi_shutdown_in_progress )
return;
/* Process any TX completions */
snpnet_poll_tx ( netdev );
@ -426,8 +434,9 @@ static void snpnet_close ( struct net_device *netdev ) {
EFI_STATUS efirc;
int rc;
/* Shut down NIC */
if ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) {
/* Shut down NIC (unless whole system shutdown is in progress) */
if ( ( ! efi_shutdown_in_progress ) &&
( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) {
rc = -EEFI ( efirc );
DBGC ( snp, "SNP %s could not shut down: %s\n",
netdev->name, strerror ( rc ) );
@ -589,8 +598,9 @@ void snpnet_stop ( struct efi_device *efidev ) {
/* Unregister network device */
unregister_netdev ( netdev );
/* Stop SNP protocol */
if ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) {
/* Stop SNP protocol (unless whole system shutdown is in progress) */
if ( ( ! efi_shutdown_in_progress ) &&
( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) ) {
rc = -EEFI ( efirc );
DBGC ( device, "SNP %s could not stop: %s\n",
efi_handle_name ( device ), strerror ( rc ) );

View File

@ -481,6 +481,7 @@ static struct pci_device_id intelx_nics[] = {
PCI_ROM ( 0x8086, 0x15ab, "x552", "X552", 0 ),
PCI_ROM ( 0x8086, 0x15c8, "x553t", "X553/X557-AT", 0 ),
PCI_ROM ( 0x8086, 0x15ce, "x553-sfp", "X553 (SFP+)", 0 ),
PCI_ROM ( 0x8086, 0x15e4, "x553a", "X553", 0 ),
PCI_ROM ( 0x8086, 0x15e5, "x553", "X553", 0 ),
};

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;

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;
}
@ -3412,14 +3459,36 @@ static int xhci_probe ( struct pci_device *pci ) {
static void xhci_remove ( struct pci_device *pci ) {
struct xhci_device *xhci = pci_get_drvdata ( pci );
struct usb_bus *bus = xhci->bus;
uint16_t command;
/* Some systems are observed to disable bus mastering on
* Thunderbolt controllers before we get a chance to shut
* down. Detect this and avoid attempting any DMA operations,
* which are guaranteed to fail and may end up spuriously
* completing after the operating system kernel starts up.
*/
pci_read_config_word ( pci, PCI_COMMAND, &command );
if ( ! ( command & PCI_COMMAND_MASTER ) ) {
DBGC ( xhci, "XHCI %s DMA was disabled\n", xhci->name );
xhci_fail ( xhci );
}
/* Unregister and free USB bus */
unregister_usb_bus ( bus );
free_usb_bus ( bus );
/* Reset device and undo any PCH-specific fixes */
xhci_reset ( xhci );
if ( xhci->quirks & XHCI_PCH )
xhci_pch_undo ( xhci, pci );
/* Release ownership back to BIOS */
xhci_legacy_release ( xhci );
/* Unmap registers */
iounmap ( xhci->regs );
/* Free device */
free ( xhci );
}

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

@ -223,6 +223,7 @@ extern EFI_HANDLE efi_image_handle;
extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path;
extern EFI_SYSTEM_TABLE *efi_systab;
extern EFI_TPL efi_internal_tpl;
extern EFI_TPL efi_external_tpl;
extern int efi_shutdown_in_progress;

View File

@ -10,7 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
extern EFI_SYSTEM_TABLE * efi_wrap_systab ( void );
extern EFI_BOOT_SERVICES * efi_wrap_bs ( void );
extern void efi_wrap ( EFI_HANDLE handle );
#endif /* _IPXE_EFI_WRAP_H */

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

@ -104,8 +104,8 @@ static void efi_entropy_disable ( void ) {
/* Close timer tick event */
bs->CloseEvent ( tick );
/* Return to TPL_CALLBACK */
bs->RaiseTPL ( TPL_CALLBACK );
/* Return to internal TPL */
bs->RaiseTPL ( efi_internal_tpl );
}
/**

View File

@ -47,6 +47,9 @@ EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path;
*/
EFI_SYSTEM_TABLE * _C2 ( PLATFORM, _systab );
/** Internal task priority level */
EFI_TPL efi_internal_tpl = TPL_CALLBACK;
/** External task priority level */
EFI_TPL efi_external_tpl = TPL_APPLICATION;
@ -79,6 +82,17 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
void *context __unused ) {
/* This callback is invoked at TPL_NOTIFY in order to ensure
* that we have an opportunity to shut down cleanly before
* other shutdown hooks perform destructive operations such as
* disabling the IOMMU.
*
* Modify the internal task priority level so that no code
* attempts to raise from TPL_NOTIFY to TPL_CALLBACK (which
* would trigger a fatal exception).
*/
efi_internal_tpl = TPL_NOTIFY;
/* Mark shutdown as being in progress, to indicate that large
* parts of the system (e.g. timers) are no longer functional.
*/
@ -273,7 +287,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
* bother doing so when ExitBootServices() is called.
*/
if ( ( efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_CALLBACK, efi_shutdown_hook,
TPL_NOTIFY, efi_shutdown_hook,
NULL, &efi_shutdown_event ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( systab, "EFI could not create ExitBootServices event: "
@ -316,9 +330,13 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_SYSTEM_TABLE *systab = efi_systab;
struct efi_saved_tpl tpl;
DBGC ( systab, "EFI image unloading\n" );
/* Raise TPL */
efi_raise_tpl ( &tpl );
/* Shut down */
shutdown_exit();
@ -336,6 +354,9 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) {
DBGC ( systab, "EFI image unloaded\n" );
/* Restore TPL */
efi_restore_tpl ( &tpl );
return 0;
}
@ -366,7 +387,7 @@ __attribute__ (( noreturn )) void __stack_chk_fail ( void ) {
}
/**
* Raise task priority level to TPL_CALLBACK
* Raise task priority level to internal level
*
* @v tpl Saved TPL
*/
@ -377,7 +398,7 @@ void efi_raise_tpl ( struct efi_saved_tpl *tpl ) {
tpl->previous = efi_external_tpl;
/* Raise TPL and record previous TPL as new external TPL */
tpl->current = bs->RaiseTPL ( TPL_CALLBACK );
tpl->current = bs->RaiseTPL ( efi_internal_tpl );
efi_external_tpl = tpl->current;
}

View File

@ -137,7 +137,7 @@ static unsigned long efi_currticks ( void ) {
efi_jiffies++;
} else {
bs->RestoreTPL ( efi_external_tpl );
bs->RaiseTPL ( TPL_CALLBACK );
bs->RaiseTPL ( efi_internal_tpl );
}
return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) );

View File

@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <ipxe/retry.h>
#include <ipxe/timer.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_watchdog.h>
@ -80,3 +81,36 @@ static void efi_watchdog_expired ( struct retry_timer *timer,
/** Watchdog holdoff timer */
struct retry_timer efi_watchdog = TIMER_INIT ( efi_watchdog_expired );
/**
* Disable watching when shutting down to boot an operating system
*
* @v booting System is shutting down for OS boot
*/
static void efi_watchdog_shutdown ( int booting ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_STATUS efirc;
int rc;
/* If we are shutting down to boot an operating system, then
* disable the boot services watchdog timer. The UEFI
* specification mandates that the platform firmware does this
* as part of the ExitBootServices() call, but some platforms
* (e.g. Hyper-V) are observed to occasionally forget to do
* so, resulting in a reboot approximately five minutes after
* starting the operating system.
*/
if ( booting &&
( ( efirc = bs->SetWatchdogTimer ( 0, 0, 0, NULL ) ) != 0 ) ) {
rc = -EEFI ( efirc );
DBGC ( &efi_watchdog, "EFI could not disable watchdog timer: "
"%s\n", strerror ( rc ) );
/* Nothing we can do */
}
}
/** Watchdog startup/shutdown function */
struct startup_fn efi_watchdog_startup_fn __startup_fn ( STARTUP_EARLY ) = {
.name = "efi_watchdog",
.shutdown = efi_watchdog_shutdown,
};

View File

@ -195,6 +195,47 @@ static const char * efi_timer_delay ( EFI_TIMER_DELAY type ) {
}
}
/**
* Dump information about a loaded image
*
* @v handle Image handle
*/
static void efi_dump_image ( EFI_HANDLE handle ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
union {
EFI_LOADED_IMAGE_PROTOCOL *image;
void *intf;
} loaded;
EFI_STATUS efirc;
int rc;
/* Open loaded image protocol */
if ( ( efirc = bs->OpenProtocol ( handle,
&efi_loaded_image_protocol_guid,
&loaded.intf, efi_image_handle, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
rc = -EEFI ( efirc );
DBGC ( colour, "WRAP %s could not get loaded image protocol: "
"%s\n", efi_handle_name ( handle ), strerror ( rc ) );
return;
}
/* Dump image information */
DBGC ( colour, "WRAP %s at base %p has protocols:\n",
efi_handle_name ( handle ), loaded.image->ImageBase );
DBGC_EFI_PROTOCOLS ( colour, handle );
DBGC ( colour, "WRAP %s parent", efi_handle_name ( handle ) );
DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->ParentHandle ));
DBGC ( colour, "WRAP %s device", efi_handle_name ( handle ) );
DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->DeviceHandle ));
DBGC ( colour, "WRAP %s file", efi_handle_name ( handle ) );
DBGC ( colour, " %s\n", efi_devpath_text ( loaded.image->FilePath ) );
/* Close loaded image protocol */
bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid,
efi_image_handle, NULL );
}
/**
* Wrap RaiseTPL()
*
@ -655,9 +696,9 @@ efi_load_image_wrapper ( BOOLEAN boot_policy, EFI_HANDLE parent_image_handle,
DBGC ( colour, "%s ", efi_handle_name ( *image_handle ) );
DBGC ( colour, ") -> %p\n", retaddr );
/* Wrap the new image */
/* Dump information about loaded image */
if ( efirc == 0 )
efi_wrap ( *image_handle );
efi_dump_image ( *image_handle );
return efirc;
}
@ -735,11 +776,14 @@ efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) {
void *retaddr = __builtin_return_address ( 0 );
EFI_STATUS efirc;
DBGC ( colour, "ExitBootServices ( %s, %#llx ) ",
DBGC ( colour, "ExitBootServices ( %s, %#llx ) -> %p\n",
efi_handle_name ( image_handle ),
( ( unsigned long long ) map_key ) );
( ( unsigned long long ) map_key ), retaddr );
efirc = bs->ExitBootServices ( image_handle, map_key );
DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr );
if ( efirc != 0 ) {
DBGC ( colour, "ExitBootServices ( ... ) = %s -> %p\n",
efi_status ( efirc ), retaddr );
}
return efirc;
}
@ -1129,12 +1173,11 @@ efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl,
}
/**
* Build table wrappers
* Build boot services table wrapper
*
* @ret systab Wrapped system table
* @ret bs Wrapped boot services table
*/
EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) {
static EFI_SYSTEM_TABLE efi_systab_wrapper;
EFI_BOOT_SERVICES * efi_wrap_bs ( void ) {
static EFI_BOOT_SERVICES efi_bs_wrapper;
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
@ -1194,12 +1237,7 @@ EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) {
= efi_uninstall_multiple_protocol_interfaces_wrapper;
efi_bs_wrapper.CreateEventEx = efi_create_event_ex_wrapper;
/* Build system table wrapper */
memcpy ( &efi_systab_wrapper, efi_systab,
sizeof ( efi_systab_wrapper ) );
efi_systab_wrapper.BootServices = &efi_bs_wrapper;
return &efi_systab_wrapper;
return &efi_bs_wrapper;
}
/**
@ -1208,42 +1246,20 @@ EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) {
* @v handle Image handle
*/
void efi_wrap ( EFI_HANDLE handle ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
union {
EFI_LOADED_IMAGE_PROTOCOL *image;
void *intf;
} loaded;
EFI_STATUS efirc;
int rc;
static EFI_SYSTEM_TABLE efi_systab_copy;
/* Do nothing unless debugging is enabled */
if ( ! DBG_LOG )
return;
/* Open loaded image protocol */
if ( ( efirc = bs->OpenProtocol ( handle,
&efi_loaded_image_protocol_guid,
&loaded.intf, efi_image_handle, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
rc = -EEFI ( efirc );
DBGC ( colour, "WRAP %s could not get loaded image protocol: "
"%s\n", efi_handle_name ( handle ), strerror ( rc ) );
return;
/* Construct modified system table */
if ( efi_systab != &efi_systab_copy ) {
memcpy ( &efi_systab_copy, efi_systab,
sizeof ( efi_systab_copy ) );
efi_systab->BootServices = efi_wrap_bs();
efi_systab = &efi_systab_copy;
}
/* Provide system table wrapper to image */
loaded.image->SystemTable = efi_wrap_systab();
DBGC ( colour, "WRAP %s at base %p has protocols:\n",
efi_handle_name ( handle ), loaded.image->ImageBase );
DBGC_EFI_PROTOCOLS ( colour, handle );
DBGC ( colour, "WRAP %s parent", efi_handle_name ( handle ) );
DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->ParentHandle ));
DBGC ( colour, "WRAP %s device", efi_handle_name ( handle ) );
DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->DeviceHandle ));
DBGC ( colour, "WRAP %s file", efi_handle_name ( handle ) );
DBGC ( colour, " %s\n", efi_devpath_text ( loaded.image->FilePath ) );
/* Close loaded image protocol */
bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid,
efi_image_handle, NULL );
/* Dump image information */
efi_dump_image ( handle );
}

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

@ -308,6 +308,9 @@ if [ -n "${ISOIMG}" ] ; then
${ISOARGS} "${ISODIR}"
if isohybrid --version >/dev/null 2>&1 ; then
ISOHYBRIDARGS=
if [ -n "${EFI}" ] ; then
ISOHYBRIDARGS="${ISOHYBRIDARGS} --uefi"
fi
if [ -n "${SOURCE_DATE_EPOCH:-}" ] ; then
ISOHYBRIDARGS="${ISOHYBRIDARGS} --id ${SOURCE_DATE_EPOCH}"
fi