The reference implementation of Dhcp6Dxe in EDK2 has a fatal flaw: the
code in EfiDhcp6Stop() will poll the network in a tight loop until
either a response is received or a timer tick (at TPL_CALLBACK)
occurs. When EfiDhcp6Stop() is called at TPL_CALLBACK or higher, this
will result in an endless loop and an apparently frozen system.
Since this is the reference implementation of Dhcp6Dxe, it is likely
that almost all platforms have the same problem.
Fix by vetoing the broken driver. If the upstream driver is ever
fixed and a new version number issued, then we could plausibly test
against the version number exposed via the driver binding protocol.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Editable strings currently require a fixed-size buffer, which is
inelegant and limits the potential for creating interactive forms with
a variable number of edit box widgets.
Remove this limitation by switching to using a dynamically allocated
buffer for editable strings and edit box widgets.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
If we do not have a current working URI (after applying the EFI device
path settings and any cached DHCP settings), then an attempt to
download autoexec.ipxe will fail since there is no base URI from which
to resolve the full autoexec.ipxe URI.
Avoid this potentially confusing error message by attempting the
download only if we have successfully obtained a current working URI.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add a new setting to provide access to the link layer protocol type
from scripts. This can be useful in order to skip configuring
interfaces based on their link layer protocol or, conversely,
configure only selected interface types (Ethernet, IPoIB, etc.)
Example script:
set idx:int32 0
:loop
isset ${net${idx}/mac} || exit 0
iseq ${net${idx}/linktype} IPoIB && goto try_next ||
autoboot net${idx} ||
:try_next
inc idx && goto loop
Signed-off-by: Pavel Krotkiy <porsh@nebius.com>
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
We currently attempt to obtain the autoexec.ipxe script via early use
of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or EFI_PXE_BASE_CODE_PROTOCOL
interfaces to obtain an opaque block of memory, which is then
registered as an image at an appropriate point during our startup
sequence. The early use of these existent interfaces allows us to
obtain the script even if our subsequent actions (e.g. disconnecting
drivers in order to connect up our own) may cause the script to become
inaccessible.
This mirrors the approach used under BIOS, where the autoexec.ipxe
script is provided by the prefix (e.g. as an initrd image when using
the .lkrn build of iPXE) and so must be copied into a normally
allocated image from wherever it happens to previously exist in
memory.
We do not currently have support for downloading an autoexec.ipxe
script if we were ourselves downloaded via UEFI HTTP boot.
There is an EFI_HTTP_PROTOCOL defined within the UEFI specification,
but it is so poorly designed as to be unusable for the simple purpose
of downloading an additional file from the same directory. It
provides almost nothing more than a very slim wrapper around
EFI_TCP4_PROTOCOL (or EFI_TCP6_PROTOCOL). It will not handle
redirection, content encoding, retries, or even fundamentals such as
the Content-Length header, leaving all of this up to the caller.
The UEFI HTTP Boot driver will install an EFI_LOAD_FILE_PROTOCOL
instance on the loaded image's device handle. This looks promising at
first since it provides the LoadFile() API call which is specified to
accept an arbitrary filename parameter. However, experimentation (and
inspection of the code in EDK2) reveals a multitude of problems that
prevent this from being usable. Calling LoadFile() will idiotically
restart the entire DHCP process (and potentially pop up a UI requiring
input from the user for e.g. a wireless network password). The
filename provided to LoadFile() will be ignored. Any downloaded file
will be rejected unless it happens to match one of the limited set of
types expected by the UEFI HTTP Boot driver. The list of design
failures and conceptual mismatches is fairly impressive.
Choose to bypass every possible aspect of UEFI HTTP support, and
instead use our own HTTP client and network stack to download the
autoexec.ipxe script over a temporary MNP network device. Since this
approach works for TFTP as well as HTTP, drop the direct use of
EFI_PXE_BASE_CODE_PROTOCOL. For consistency and simplicity, also drop
the direct use of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and rely upon our
existing support to access local files via "file:" URIs.
This approach results in console output during the "iPXE initialising
devices...ok" message that appears while startup is in progress.
Remove the trailing "ok" so that this intermediate output appears at a
sensible location on the screen. The welcome banner that will be
printed immediately afterwards provides an indication that startup has
completed successfully even absent the explicit "ok".
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Retain a reference to the cached DHCPACK until the late startup phase,
and allow it to be recycled for reuse. This allows the cached DHCPACK
to be used for a temporary MNP network device and then subsequently
reused for the corresponding real network device.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
An MNP network device may be temporarily and non-destructively
installed on top of an existing UEFI network stack without having to
disconnect existing drivers.
Add the ability to create such a temporary network device.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Split out the code that allocates our internal struct efi_device
representations, to allow for the creation of temporary MNP devices in
order to download the autoexec.ipxe script.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add an abbreviated "Not found" error message for an HTTP 404 status
code, so that any automatic attempt to download a non-existent
autoexec.ipxe script produces only a minimal error message.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add an abbreviated "Not found" error message for a TFTP "file not
found" error code, so that any automatic attempt to download a
non-existent autoexec.ipxe script produces only a minimal error
message.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add an abbreviated "Not found" error message for an EFI_NOT_FOUND
error encountered when attempting to open a file on a local
filesystem, so that any automatic attempt to download a non-existent
autoexec.ipxe script produces only a minimal error message.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
iPXE is designed around fully asynchronous I/O, including asynchronous
connection opening. Almost all errors are therefore necessarily
reported as occurring during an in-progress download, rather than
occurring at the time that the URI is opened.
Local file access is currently an exception to this: errors such as
nonexistent files will be encountered while opening the URI. This
results in mildly unexpected error messages of the form "Could not
start download", rather than the usual pattern of showing the URI, the
initial progress dots, and then the error message.
Fix this inconsistency by deferring the local filesystem access until
the local file download process is running.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Some URI schemes allow for a path name to be specified via the opaque
component of the URI (e.g. "file:/script.ipxe" to specify a path on
the filesystem from which iPXE itself was loaded). Files loaded from
such paths will currently fail to be assigned an appropriate name,
since only the path component of the URI will be used to construct a
default image name.
Fix by falling back to attempt deriving an image name from the opaque
component of a URI, if no path component is specified.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
For unknown reasons, miscellaneous versions of gcc seem to struggle
with the static assertions used to ensure the correct layout of the
GCM structures.
Adjust the assertions to use offsetof() rather than direct pointer
comparison, on the basis that offsetof() must be a compile-time
constant value.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The UEFI HTTP boot mechanism is extraordinarily badly designed, even
by the standards of the UEFI specification in general. It has the
symptoms of a feature that has been designed entirely in terms of user
stories, without any consideration at all being given to the
underlying technical architecture. It does work, provided that you
are doing precisely and only what was envisioned by the product owner.
If you want to try anything outside the bounds of the product owner's
extremely limited imagination, then you are almost certainly about to
enter a world of pain.
As one very minor example of this: the cached DHCP packet is not
available when using HTTP boot. The UEFI HTTP boot code does perform
DHCP, but it pointlessly and unhelpfully throws away the DHCP packet
and trashes the network interface configuration before handing over to
the downloaded executable.
Work around this imbecility by parsing and applying the few network
configuration settings that are persisted into the loaded image's
device path. This is limited to very basic information such as the IP
address, gateway address, and DNS server address, but it does at least
provide enough for a functional routing table.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
We want exclusive access to the network device, both for performance
reasons and because we perform operations such as EAPoL that affect
the entire link. We currently drive the network card via either a
native hardware driver or via the SNP or NII/UNDI interfaces, both of
which grant us this exclusive access.
Add an alternative driver that drives the network card non-exclusively
via the EFI_MANAGED_NETWORK_PROTOCOL interface. This can function as
a fallback for situations where neither SNP nor NII/UNDI interfaces
are functional, and also opens up the possibility of non-destructively
installing a temporary network device over which to download the
autoexec.ipxe script.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
When using a service binding protocol, CreateChild() will create a new
protocol instance (and optionally a new handle). The caller will then
typically open this new protocol instance with BY_DRIVER attributes,
since the service binding mechanism has no equivalent of the driver
binding protocol's Stop() method, and there is therefore no other way
for the caller to be informed if the protocol instance is about to
become invalid (e.g. because the service driver wants to remove the
child).
The caller cannot ask CreateChild() to install the new protocol
instance on the original handle (i.e. the service binding handle),
since the whole point of the service binding protocol is to allow for
the existence of multiple children, and UEFI does not permit multiple
instances of the same protocol to be installed on a handle.
Our current drivers all open the original handle (as passed to our
driver binding's Start() method) with BY_DRIVER attributes, and so the
same handle will be passed to our Stop() method. This changes when
our driver must use a separate handle, as described above.
Add an optional "child handle" field to struct efi_device (on the
assumption that we will not have any drivers that need to create
multiple children), and generalise efidev_find() to match on either
the original handle or the child handle.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The EFI service binding abstraction is used to add and remove child
handles for multiple different protocols. Provide a common interface
for doing so.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Commit 4c5b794 ("[efi] Use the SNP protocol instance to match the SNP
chainloading device") switched the chainloaded device matching logic
to use a target protocol instance rather than the loaded image's
device handle, on the basis that we want to bind to the parent SNP
device rather than to a duplicate SNP protocol instance installed onto
an IPv4 or IPv6 child device handle.
It is possible that our calls to DisconnectController() and
ConnectController() will cause the target protocol instance to be
uninstalled and reinstalled, which may change the value of the
protocol instance pointer. Allow for this by identifying and matching
against the uppermost handle that initially has this target protocol
instance installed.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
When booted via HTTP, our loaded image's device path will include the
URI from which we were downloaded. Set this as the current working
URI, so that an embedded script may perform subsequent downloads
relative to the iPXE binary, or construct explicit relative paths via
the ${cwduri} setting.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
iPXE maintains a concept of a current working URI, which is used when
resolving relative URIs and allows scripts to download files using
URIs relative to the script itself.
There are situations in which it is valuable for a script to be able
to access the URI explicitly as a string, not just implicitly as a
base URI for subsequent downloads. For example, when booting a Fedora
installer, the "inst.repo" command-line parameter may be used to pass
the URI of the repository to the installer.
Expose the current working URI as ${cwuri}. Since relative URIs may
be constructed as strings only from a directory URI (not from a full
URI), also expose the current working directory URI as ${cwduri}.
This feature may be used as e.g.
#!ipxe
echo Booting from ${cwuri}
prompt -k 0x197e -t 2000 Press F12 to install Fedora... || exit
kernel images/pxeboot/vmlinux inst.repo=${cwduri}
initrd images/pxeboot/initrd.img
boot
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The Mellanox/Nvidia UEFI driver is built from the same codebase as the
iPXE driver, and appears to contain the bug that was fixed in commit
c11734e ("[golan] Use ETH_HLEN for inline header size"). This results
in identical failures when using the SNP or NII interface (via
e.g. snponly.efi) to drive a Mellanox card while EAPoL is enabled.
Work around the underlying UEFI driver bug by padding transmit I/O
buffers to the minimum Ethernet frame length before passing them to
the underlying driver's transmit function.
This padding is not technically necessary, since almost all modern
hardware will insert transmit padding as necessary (and where the
hardware does not support doing so, the underlying UEFI driver is
responsible for adding any necessary padding). However, it is
guaranteed to be harmless (other than a miniscule performance impact):
the Ethernet specification requires zero padding up to the minimum
frame length for packets that are transmitted onto the wire, and so
the receiver will see the same packet whether or not we manually
insert this padding in software.
The additional padding causes the underlying Mellanox driver to avoid
its faulty code path, since it will never be asked to transmit a very
short packet.
Tested-by: Eric Hagberg <ehagberg@janestreet.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The driver does not correctly handle very short transmitted packets
such as EAPoL-Start where the entire DMA content lies within the
current send work queue entry inline header length of 18 bytes.
Fix by reducing the inline header length to the Ethernet frame header
length of 14 bytes.
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Older versions of gcc (observed with gcc 4.8.5 on CentOS 7) complain
about having the label "err_ioremap" at the end of a compound
statement in bios_mp_start_all(). The label is correctly placed,
since it immediately follows the iounmap() that would be required to
undo a successful ioremap() in the non-error case.
Fix by adding an explicit "return" immediately after the label.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Some SNP implementations (observed with a wifi adapter in a Dell
Latitude 3440 laptop) seem to require additional space in the
allocated receive buffers, otherwise full-length packets will be
silently dropped.
The EDK2 MnpDxe driver happens to allocate an additional 8 bytes of
padding (4 for a VLAN tag, 4 for the Ethernet frame checksum). Match
this behaviour since drivers are very likely to have been tested
against MnpDxe.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Intel and AMD distribute microcode updates, which are typically
applied by the BIOS and/or the booted operating system.
BIOS updates can be difficult to obtain and cumbersome to apply, and
are often neglected. Operating system updates may be subject to
strict change control processes, particularly for production
workloads. There is therefore value in being able to update the
microcode at boot time using a freshly downloaded microcode update
file, particularly in scenarios where the physical hardware and the
installed operating system are controlled by different parties (such
as in a public cloud infrastructure).
Add support for parsing Intel and AMD microcode update images, and for
applying the updates to all CPUs in the system.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Provide an implementation of the iPXE multiprocessor API for BIOS,
based on sending broadcast INIT and SIPI interprocessor interrupts to
start up all application processors.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Application processors are started via INIT and SIPI interprocessor
interrupts: the INIT places the processor into a "wait for SIPI"
state, and the SIPI then starts the processor in real mode at a
page-aligned address derived from the SIPI vector number.
Add support for installing a real-mode SIPI handler that will switch
the CPU into protected mode with flat physical addressing, load
initial register contents, and then jump to the address of a
protected-mode SIPI handler. No stack pointer is set up, to avoid the
need to allocate stack space for each available processor.
We use 32-bit physical addressing in order to minimise the changes
required for a 64-bit build. The existing long mode transition code
relies on the existence of the stack, so we cannot easily switch the
application processor into long mode. We could use 32-bit virtual
addressing, but this runtime environment does not currently exist
outside of librm.S itself in a 64-bit build, and using it would
complicate the implementation of the protected-mode SIPI handler.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Provide an implementation of the iPXE multiprocessor API for EFI,
based on using EFI_MP_SERVICES to start up a wrapper function on all
application processors.
Note that the processor numbers used by EFI_MP_SERVICES are opaque
integers that bear no relation to the underlying CPU identity
(e.g. the APIC ID), and so we must rely on our own (architecture-
specific) implementation to determine the relevant CPU identifiers.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Define an API for executing very limited functions on application
processors in a multiprocessor system, along with an x86-only
implementation.
The normal iPXE runtime environment is effectively non-existent on
application processors. There is no ability to make firmware calls
(e.g. to write to a console), and there may be no stack space
available.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The return status from efi_block_local() indicates whether or not the
handle is eligible to be assigned a local virtual drive number. There
will always be several enumerated EFI_BLOCK_IO_PROTOCOL handles that
are not eligible for a local virtual drive number (e.g. the handles
corresponding to partitions, rather than to complete disks), and this
is not an interesting error to report.
Do not report errors from efi_block_local() as the overall error
status for a SAN boot, since doing so would be likely to mask a much
more relevant error from having previously attempted to scan for a
matching filesystem within an eligible block device handle.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add a "--label" option that can be used to specify a filesystem label,
to be matched against the FAT volume label.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add an "--extra" option that can be used to specify an extra
(non-boot) filename that must exist within the booted filesystem.
Note that only files within the FAT-formatted bootable partition will
be visible to this filter. Files within the operating system's root
disk (e.g. "/etc/redhat-release") are not generally accessible to the
firmware and so cannot be used as the existence check filter filename.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add a "--uuid" option which may be used to specify a boot device UUID,
to be matched against the GPT partition GUID.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
EFI provides no API for determining the partition GUID (if any) for a
specified device handle. The partition GUID appears to be exposed
only as part of the device path.
Add efi_path_guid() to extract the partition GUID (if any) from a
device path.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The drive specification alone does not necessarily contain enough
information to perform a SAN boot (or local disk boot) under UEFI. If
the next-stage bootloader is installed in the EFI system partition
under a non-standard name (e.g. "\EFI\debian\grubx64.efi") then this
explicit boot filename must also be specified.
Generalise this concept to use a "SAN boot configuration parameters"
structure (currently containing only the optional explicit boot
filename), to allow for easy expansion to provide other parameters
such as the partition UUID or volume label.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Extend the EFI SAN boot code to allow for booting from a local disk,
as is already possible with the BIOS SAN boot code.
There is unfortunately no direct UEFI equivalent of the BIOS drive
number. The UEFI shell does provide numbered mappings fs0:, blk0:,
etc, but these numberings exist only while the UEFI shell is running
and are not necessarily stable between shell invocations or across
reboots.
A substantial amount of existing third-party documentation for iPXE
will suggest using "sanboot --drive 0x80" to boot from a local disk
(when no SAN drives are present), since this suggestion has been
present in the official documentation for the "sanboot" command for
almost thirteen years. We therefore aim to ensure that this
instruction will also work for UEFI, i.e. that in a situation where
there are local disks but no SAN disks, then the first local disk will
be treated as being drive 0x80.
We therefore assign local disks the virtual drive numbers 0x80, 0x81,
etc, matching the numbering typically used in a BIOS environment.
Where a SAN disk is already occupying one of these drive numbers, the
local disks' virtual drive numbers will be incremented as necessary.
This provides a rough approximation of the equivalent functionality
under BIOS, where existing local disks' drive numbers are remapped to
make way for SAN disks.
We do not make any attempt to sort the list of local disks: the order
used for allocating virtual drive numbers will be whatever order is
returned by LocateHandle(). This will typically match the creation
order of the EFI handles, which will typically match the hardware
enumeration order of the devices, which will typically match user
expectations as to which local disk is first, second, etc.
We explicitly do not attempt to match the numbering used by the UEFI
shell (which initially sorts in increasing order of device path, but
does not renumber when new devices are added or removed). We can
never guarantee matching this partly transient UEFI shell numbering,
so it is best not to set any expectation that it will be matched.
(Using local drive numbers starting at 0x80 helps to avoid setting up
this impossible expectation, since the UEFI shell uses local drive
numbers starting at zero.)
Since floppy disks are essentially non-existent in any plausible UEFI
system, overload "--drive 0" to mean "boot from any drive containing
the specified (or default) boot filename".
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Maintain the SAN device list in order of drive number, and provide
sandev_next() to locate the first SAN device at or above a given drive
number.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
SAN devices created by iPXE are visible to the firmware, and may be
accessed using the firmware's standard block I/O device interface
(e.g. INT 13 for BIOS, or EFI_BLOCK_IO_PROTOCOL for UEFI). The iPXE
code to perform a SAN boot acts as a client of this standard block I/O
device interface, even when the underlying block I/O is being
performed by iPXE itself.
We rely on this separation to allow the "sanboot" command to be used
to boot from a local disk: since the code to perform a SAN boot does
not need direct access to an underlying iPXE SAN device, it may be
used to boot from any device providing the firmware's standard block
I/O device interface.
Clean up the EFI SAN boot code to require only a drive number and an
EFI_BLOCK_IO_PROTOCOL handle, in preparation for adding support for
booting from a local disk under UEFI.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The "sanboot" command allows a custom boot filename to be specified
via the "--filename" option. We currently rely on LoadImage() to
perform both the existence check and to load the image ready for
execution. This may give a false negative result if Secure Boot is
enabled and the boot file is not correctly signed.
Carry out the existence check using EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
separately from loading the image via LoadImage().
Signed-off-by: Michael Brown <mcb30@ipxe.org>
We currently use the SAN device pointer as the debug message stream
identifier. This pointer is not always available: for example, when
booting from a local disk there is no underlying SAN device.
Switch to using the drive number as the debug message colour stream
identifier, so that all block device debug messages may be colourised
consistently.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
We currently call ConvertDevicePathToText() with DisplayOnly=TRUE when
constructing a device path to appear within a debug message. For
ATAPI device paths, this will unfortunately omit some key information:
the textual representation will not indicate which ATA bus or drive is
represented. This can lead to misleading debug messages that appear
to refer to identical devices.
Fix by setting DisplayOnly=FALSE to select the long form of device
path textual representations.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The ":uuid" and ":guid" settings types are currently format-only: it
is possible to format a setting as a UUID (via e.g. "show foo:uuid")
but it is not currently possible to parse a string into a UUID setting
(via e.g. "set foo:uuid 406343fe-998b-44be-8a28-44ca38cb202b").
Use uuid_aton() to implement parsing of these settings types, and add
appropriate test cases for both.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add uuid_aton() to parse a UUID value from a string (analogous to
inet_aton(), inet6_aton(), sock_aton(), etc), treating it as a
32-digit hex string with optional hyphen separators. The placement of
the separators is not checked: each byte within the hex string may be
separated by a hyphen, or not separated at all.
Add dedicated self-tests for UUID parsing and formatting (already
partially covered by the ":uuid" and ":guid" settings self-tests).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The UEFI shim installs wrappers around several boot services functions
before invoking its next stage bootloader, in an attempt to enforce
its desired behaviour upon the aforementioned bootloader. For
example, shim checks that the bootloader has either invoked
StartImage() or has called into the "shim lock protocol" before
allowing an ExitBootServices() call to proceed.
When invoking a shim, iPXE will also install boot services function
wrappers in order to work around assorted bugs in the UEFI shim code
that would otherwise prevent it from being used to boot a kernel. For
details on these workarounds, see commits 28184b7 ("[efi] Add support
for executing images via a shim") and 5b43181 ("[efi] Support versions
of shim that perform SBAT verification").
Using boot services function wrappers in this way is not intrinsically
problematic, provided that wrappers are installed before starting the
wrapped program, and uninstalled only after the wrapped program exits.
This strict ordering requirement ensures that all layers of wrappers
are called in the expected order, and that no calls are issued through
a no-longer-valid function pointer.
Unfortunately, the UEFI shim does not respect this strict ordering
requirement, and will instead uninstall (and reinstall) its wrappers
midway through the execution of the wrapped program. This leaves the
wrapped program with an inconsistent view of the boot services table,
leading to incorrect behaviour.
This results in a boot failure when a first shim is used to boot iPXE,
which then uses a second shim to boot a Linux kernel:
- First shim installs StartImage() and ExitBootServices() wrappers
- First shim invokes iPXE via its own PE loader
- iPXE installs ExitBootServices() wrapper
- iPXE invokes second shim via StartImage()
At this point, the first shim's StartImage() wrapper will illegally
uninstall its ExitBootServices() wrapper, without first checking that
nothing else has modified the ExitBootServices function pointer. This
effectively bypasses iPXE's own ExitBootServices() wrapper, which
causes a boot failure since the code within that wrapper does not get
called.
A proper fix would be for shim to install its wrappers before starting
the image and uninstall its wrappers only after the started image has
exited. Instead of repeatedly uninstalling and reinstalling its
wrappers while the wrapped program is running, shim should simply use
a flag to keep track of whether or not it needs to modify the
behaviour of the wrapped calls.
Experience shows that there is unfortunately no point in trying to get
a fix for this upstreamed into shim. We therefore work around the
shim bug by removing our ExitBootServices() wrapper and moving the
relevant code into our GetMemoryMap() wrapper.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add support for EAP-MSCHAPv2 (note that this is not the same as
PEAP-MSCHAPv2), controllable via the build configuration option
EAP_METHOD_MSCHAPV2 in config/general.h.
Our model for EAP does not encompass mutual authentication: we will
starting sending plaintext packets (e.g. DHCP requests) over the link
even before EAP completes, and our only use for an EAP success is to
mark the link as unblocked.
We therefore ignore the content of the EAP-MSCHAPv2 success request
(containing the MS-CHAPv2 authenticator response) and just send back
an EAP-MSCHAPv2 success response, so that the EAP authenticator will
complete the process and send through the real EAP success packet
(which will, in turn, cause us to unblock the link).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
RFC 3748 states that implementations must support the MD5-Challenge
method. However, some network environments may wish to disable it as
a matter of policy.
Allow support for MD5-Challenge to be controllable via the build
configuration option EAP_METHOD_MD5 in config/general.h.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add debug messages for each EAP Request and Response, and to show the
list of methods offered when sending a Nak.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Several new relocations types have been added in LoongArch ABI version
2.10. In particular:
- R_LARCH_B16 (18-bit PC-relative jump)
- R_LARCH_B21 (23-bit PC-relative jump)
- R_LARCH_PCREL20_S2 (22-bit PC-relative offset)
Also relocation relaxations have been introduced. Recent GCC (13.2)
and binutils 2.41+ use these types of relocations, which confuses
elf2efi tool. As a result, iPXE EFI images for LoongArch fail to
build with the following error:
Unrecognised relocation type 103
Fix by ignoring R_LARCH_B{16,21} and R_LARCH_PCREL20_S2 (as with other
PC-relative relocations), and by ignoring relaxations (R_LARCH_RELAX).
Relocation relaxations are basically optimizations: ignoring them
results in a correct binary (although it might be suboptimal).
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Done with the help of this Perl script:
$MARKER = 'PCI_ROM'; # a regex
$AB = 1; # At Begin
@HEAD = ();
@ITEMS = ();
@TAIL = ();
foreach $fn (@ARGV) {
open(IN, $fn) or die "Can't open file '$fn': $!\n";
while (<IN>) {
if (/$MARKER/) {
push @ITEMS, $_;
$AB = 0; # not anymore at begin
}
else {
if ($AB) {
push @HEAD, $_;
}
else {
push @TAIL, $_;
}
}
}
} continue {
close IN;
open(OUT, ">$fn") or die "Can't open file '$fn' for output: $!\n";
print OUT @HEAD;
print OUT sort @ITEMS;
print OUT @TAIL;
close OUT;
# For a next file
$AB = 1;
@HEAD = ();
@ITEMS = ();
@TAIL = ();
}
Executed that script while src/drivers/ as current working directory,
provided '$(grep -rl PCI_ROM)' as argument.
Signed-off-by: Geert Stappers <stappers@stappers.it>
Inspection of the generated assembly shows that gcc will often emit
standalone implementations of frequently invoked functions such as
digest_update(), which contain no logic and exist only as syntactic
sugar.
Force inlining of these functions to reduce the overall binary size.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add an implementation of the authentication portions of the MS-CHAPv2
algorithm as defined in RFC 2759, along with the single test vector
provided therein.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Certificates issued by Let's Encrypt have two options for their chain
of trust: the chain can either terminate in the self-signed ISRG Root
X1 root certificate, or in an intermediate ISRG Root X1 certificate
that is signed in turn by the self-signed DST Root CA X3 root
certificate. This is a historical artifact: when Let's Encrypt first
launched as a project, the chain ending in DST Root CA X3 was used
since existing clients would not have recognised the ISRG Root X1
certificate as a trusted root certificate.
The DST Root CA X3 certificate expired in September 2021, and so is no
longer trusted by clients (such as iPXE) that validate the expiry
times of all certificates in the certificate chain.
In order to maintain usability of certificates on older Android
devices, the default certificate chain provided by Let's Encrypt still
terminates in DST Root CA X3, even though that certificate has now
expired. On newer devices which include ISRG Root X1 as a trusted
root certificate, the intermediate version of ISRG Root X1 in the
certificate chain is ignored and validation is performed as though the
chain had terminated in the self-signed ISRG Root X1 root certificate.
On older Android devices which do not include ISRG Root X1 as a
trusted root certificate, the validation succeeds since Android
chooses to ignore expiry times for root certificates and so continues
to trust the DST Root CA X3 root certificate.
This backwards compatibility hack unfortunately breaks the cross-
signing mechanism used by iPXE, which assumes that the certificate
chain will always terminate in a non-expired root certificate.
Generalise the validator's cross-signed certificate download mechanism
to walk up the certificate chain in the event of a failure, attempting
to find a replacement cross-signed certificate chain starting from the
next level up. This allows the validator to step over the expired
(and hence invalidatable) DST Root CA X3 certificate, and instead
download the cross-signed version of the ISRG Root X1 certificate.
This generalisation also gives us the ability to handle servers that
provide a full certificate chain including their root certificate:
iPXE will step over the untrusted public root certificate and attempt
to find a cross-signed version of it instead.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Downloading a cross-signed certificate chain to partially replace
(rather than simply extend) an existing chain will require the ability
to discard all certificates after a specified link in the chain.
Extract the relevant logic from x509_free_chain() and expose it
separately as x509_truncate().
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Some versions of gcc (observed with gcc 4.8.5 in CentOS 7) will report
spurious build_assert() failures for some assertions about structure
layouts. There is no clear pattern as to what causes these spurious
failures, and the build assertion does succeed in that no unresolvable
symbol reference is generated in the compiled code.
Adjust the assertions to work around these apparent compiler issues.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
We build with -Werror by default so that any warning is treated as an
error and aborts the build. The build system allows NO_WERROR=1 to be
used to override this behaviour, in order to allow builds to succeed
when spurious warnings occur (e.g. when using a newer compiler that
includes checks for which the codebase is not yet prepared).
Some versions of gcc (observed with gcc 4.8.5 in CentOS 7) will report
spurious build_assert() failures: the compilation will fail due to an
allegedly unelided call to the build assertion's external function
declared with __attribute__((error)) even though the compiler does
manage to successfully elide the call (as verified by the fact that
there are no unresolvable symbol references in the compiler output).
Change build_assert() to declare __attribute__((warning)) instead of
__attribute__((error)) on its extern function. This will still abort
a normal build if the assertion fails, but may be overridden using
NO_WERROR=1 if necessary to work around a spurious assertion failure.
Note that if the build assertion has genuinely failed (i.e. if the
compiler has genuinely not been able to elide the call) then the
object will still contain an unresolvable symbol reference that will
cause the link to fail (which matches the behaviour of the old
linker_assert() mechanism).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The DES block cipher dates back to the 1970s. It is no longer
relevant for use in TLS cipher suites, but it is still used by the
MS-CHAPv2 authentication protocol which remains unfortunately common
for 802.1x port authentication.
Add an implementation of the DES block cipher, complete with the
extremely comprehensive test vectors published by NBS (the precursor
to NIST) in the form of an utterly adorable typewritten and hand-drawn
paper document.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
A block cipher in ECB mode has no concept of an initialisation vector,
and any data provided to cipher_setiv() for an ECB cipher will be
ignored. There is no requirement within our cipher algorithm
abstraction for a dummy initialisation vector to be provided.
Remove the entirely spurious dummy 16-byte initialisation vector from
the ECB test cases.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The CBC_CIPHER() macro contains some accidentally hardcoded references
to an underlying AES cipher, instead of using the cipher specified in
the macro parameters.
Fix by using the macro parameter as required.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Coverity reported that tls_send_plaintext() failed to check the return
status from tls_generate_random(), which could potentially result in
uninitialised random data being used as the block initialisation
vector (instead of intentionally random data).
Add the missing return status check, and separate out the error
handling code paths (since on the successful exit code path there will
be no need to free either the plaintext or the ciphertext anyway).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
When ExitBootServices() invokes efi_shutdown_hook(), there may be
nothing to generate an interrupt since the timer is disabled in the
first step of ExitBootServices(). Additionally, for VMs OVMF masks
everything from the PIC (except the timer) by default. This means
that calling cpu_nap() may hang indefinitely. This was seen in
practice in netfront_reset() when running in a VM on XenServer.
Fix this by skipping the halt if an EFI shutdown is in progress.
Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Allow the choice of key exchange algorithms to be controlled via build
configuration options in config/crypto.h, as is already done for the
choices of public-key algorithms, cipher algorithms, and digest
algorithms.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
DHE and ECDHE use essentially the same mechanism for verifying the
signature over the Diffie-Hellman parameters, though the format of the
parameters is different between the two methods.
Split out the verification of the parameter signature so that it may
be shared between the DHE and ECDHE key exchange algorithms.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The construction of the key material for the pending cipher suites
from the TLS master secret must happen regardless of which key
exchange algorithm is in use, and the key material is not required to
send the ClientKeyExchange handshake (which is sent before changing
cipher suites).
Centralise the call to tls_generate_keys() after performing key
exchange via the selected algorithm.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Define an individual local structure for each extension and a single
structure for the list of extensions. This makes it viable to add
extensions such as the Supported Elliptic Curves extension, which must
not be present if the list of curves is empty.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Define an abstraction of an elliptic curve with a fixed generator and
one supported operation (scalar multiplication of a curve point).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
RFC7748 states that it is entirely optional for X25519 Diffie-Hellman
implementations to check whether or not the result is the all-zero
value (indicating that an attacker sent a malicious public key with a
small order). RFC8422 states that implementations in TLS must abort
the handshake if the all-zero value is obtained.
Return an error if the all-zero value is obtained, so that the TLS
code will not require knowledge specific to the X25519 curve.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add an implementation of the X25519 key exchange algorithm as defined
in RFC7748.
This implementation is inspired by and partially based upon the paper
"Implementing Curve25519/X25519: A Tutorial on Elliptic Curve
Cryptography" by Martin Kleppmann, available for download from
https://www.cl.cam.ac.uk/teaching/2122/Crypto/curve25519.pdf
The underlying modular addition, subtraction, and multiplication
operations are completely redesigned for substantially improved
efficiency compared to the TweetNaCl implementation studied in that
paper (approximately 5x-10x faster and with 70% less memory usage).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The slightly incomprehensible LoongArch64 implementation for
bigint_subtract() is observed to produce incorrect results for some
input values.
Replace the suspicious LoongArch64 implementations of bigint_add(),
bigint_subtract(), bigint_rol() and bigint_ror(), and add a test case
for a subtraction that was producing an incorrect result with the
previous implementation.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add a helper function bigint_swap() that can be used to conditionally
swap a pair of big integers in constant time.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Big integers may be efficiently copied using bigint_shrink() (which
will always copy only the size of the destination integer), but this
is potentially confusing to a reader of the code.
Provide bigint_copy() as an alias for bigint_shrink() so that the
intention of the calling code may be more obvious.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Big integer multiplication is currently used only as part of modular
exponentiation, where both multiplicand and multiplier will be the
same size.
Relax this requirement to allow for the use of big integer
multiplication in other contexts.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
We currently implement build-time assertions via a mechanism that
generates a call to an undefined external function that will cause the
link to fail unless the compiler can prove that the asserted condition
is true (and thereby eliminate the undefined function call).
This assertion mechanism can be used for conditions that are not
amenable to the use of static_assert(), since static_assert() will not
allow for proofs via dead code elimination.
Add __attribute__((error(...))) to the undefined external function, so
that the error is raised at compile time rather than at link time.
This allows us to provide a more meaningful error message (which will
include the file name and line number, as with any other compile-time
error), and avoids the need for the caller to specify a unique symbol
name for the external function.
Change the name from linker_assert() to build_assert(), since the
assertion now takes place at compile time rather than at link time.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Expose static_assert() via assert.h and migrate link-time assertions
to build-time assertions where possible.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Newer versions of the GNU assembler (observed with binutils 2.41) will
complain about the ".arch i386" in files assembled with "as --64",
with the message "Error: 64bit mode not supported on 'i386'".
In files such as stack.S that contain no instructions to be assembled,
the ".arch i386" is redundant and may be removed entirely.
In the remaining files, fix by moving ".arch i386" below the relevant
".code16" or ".code32" directive, so that the assembler is no longer
expecting 64-bit instructions to be used by the time that the ".arch
i386" directive is encountered.
Reported-by: Ali Mustakim <alim@forwardcomputers.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The .text directive is entirely redundant when followed by a .section
directive giving an explicit section name and attributes.
Remove these unnecessary directives to simplify the code.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
RFC 3748 states that support for MD5-Challenge is mandatory for EAP
implementations. The MD5 and CHAP code is already included in the
default build since it is required by iSCSI, and so this does not
substantially increase the binary size.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Allow the ${netX/username} setting to be used to specify an EAP
identity to be returned in response to a Request-Identity, and provide
a mechanism for responding with a NAK to indicate which authentication
types we support.
If no identity is specified then fall back to the current behaviour of
not sending any Request-Identity response, so that switches will time
out and switch to MAC Authentication Bypass (MAB) if applicable.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
EAP responses (including our own) may be broadcast by switches but are
not of interest to us and can be safely ignored if received.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Ensure that .gitignore rules do not cover any files that do exist
within the repository.
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Support scanning for the 64-bit SMBIOS3 entry point in addition to the
32-bit SMBIOS2 entry point.
Prefer use of the 32-bit entry point if present, since this is
guaranteed to be within accessible memory.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add definitions for relocation types that may be missing on older
versions of the host system's elf.h.
This mirrors wimboot commit 47f6298 ("[efi] Add potentially missing
relocation types").
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The result of multiplying a uint16_t by another uint16_t will be a
signed int. Comparing this against a size_t will perform an unwanted
sign extension.
Fix by explicitly casting e_phnum to an unsigned int, thereby matching
the data type used for the loop index variable (and avoiding the
unwanted sign extension).
This mirrors wimboot commit 15f6162 ("[efi] Fix Coverity warning about
unintended sign extension").
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Add additional PC-relative relocation types that may be encountered
when converting binaries compiled with clang.
This mirrors the relevant elf2efi portions of wimboot commit 7910830
("[build] Support building with the clang compiler").
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The clang compiler does not (and apparently will not ever) allow for
variable-length arrays within structs.
Work around this limitation by using a fixed-length array to hold the
PDB filename in the debug section.
This mirrors wimboot commit f52c3ff ("[efi] Allow compiling elf2efi
with clang").
Signed-off-by: Michael Brown <mcb30@ipxe.org>
The function efi_pecoff_debug_name() (called by efi_handle_name()) is
used to extract a filename from the debug data directory entry located
within a PE/COFF image. The name is copied into a temporary static
buffer to allow for modifications, but the code currently erroneously
modifies the original name within the loaded PE/COFF image.
Fix by performing the modification on the copy in the temporary
buffer, as originally intended.
Signed-off-by: Michael Brown <mcb30@ipxe.org>