Commit Graph

6770 Commits

Author SHA1 Message Date
Michael Brown
122777f789 [hci] Allow tab key to be used to cycle through UI elements
Add support for wraparound scrolling and allow the tab key to be used
to move forward through a list of elements, wrapping back around to
the beginning of the list on overflow.

This is mildly useful for a menu, and likely to be a strong user
expectation for an interactive form.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-20 13:14:35 -07:00
Michael Brown
76e0933d78 [hci] Rename "item" command's first parameter from "label" to "name"
Switch terminology for the "item" command from "item <label> <text>"
to "item <name> <text>", in preparation for repurposing the "item"
command to cover interactive forms as well as menus.

Since this renaming affects only a positional parameter, it does not
break compatibility with any existing scripts.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-18 15:17:03 -07:00
Michael Brown
bf98eae5da [hci] Split out msg() and alert() from settings UI code
The msg() and alert() functions currently defined in settings_ui.c
provide a general-purpose facility for printing messages centred on
the screen.

Split this out to a separate file to allow for reuse by the form
presentation code.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-18 15:08:01 -07:00
Michael Brown
bb4a10696f [hci] Draw all widgets on the standard screen
The curses concept of a window has been supported but never actively
used in iPXE since the mucurses library was first implemented in 2006.

Simplify the code by removing the ability to place a widget set in a
specified window, and instead use the standard screen for all drawing
operations.

This simplification allows the widget set parameter to be omitted for
the draw_widget() and edit_widget() operations, since the only reason
for its inclusion was to provide access to the specified window.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-18 14:46:31 -07:00
Michael Brown
e965f179e1 [libc] Add stpcpy()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-05-31 10:11:22 +01:00
Michael Brown
dc118c5369 [hci] Provide a general concept of a text widget set
Create a generic abstraction of a text widget, refactor the existing
editable text box widget to use this abstraction, add an
implementation of a non-editable text label widget, and generalise the
login user interface to use this generic widget abstraction.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-05-15 14:22:01 +01:00
Michael Brown
d7e58c5a81 [test] Add test cases for editable strings
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-17 15:56:08 +01:00
Michael Brown
b01781a2b8 [hci] Fix semantics of replace_string() to match code comments
The comments for replace_string() state that a successful return
status guarantees that the dynamically allocated string pointer is no
longer NULL (even if it was initially NULL and the replacement string
is NULL or empty).  This is relied upon by readline() to guarantee
that it will always return a non-NULL string if successful.

The code behaviour does not currently match this comment: an empty
replacement string may result in a successful return status even if
the (single-byte) allocation fails.

Fix up the code behaviour to match the comments, and to additionally
ensure that the edit history is filled in even in the event of an
allocation failure.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-17 15:55:28 +01:00
Michael Brown
cb95b5b378 [efi] Veto the Dhcp6Dxe driver on all platforms
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>
2024-04-16 14:19:01 +01:00
Michael Brown
40b5112440 [hci] Use dynamically allocated buffers for editable strings
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>
2024-04-15 15:59:49 +01:00
Michael Brown
27ecc36c0b [efi] Do not attempt to download autoexec.ipxe without a valid base URI
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>
2024-04-15 14:28:38 +01:00
Pavel Krotkiy
59f27d6935 [netdevice] Add "linktype" setting
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>
2024-04-03 12:53:46 +01:00
Michael Brown
165995b7e9 [efi] Restructure handling of autoexec.ipxe script
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>
2024-04-03 00:03:49 +01:00
Michael Brown
b940d54235 [cachedhcp] Allow cached DHCPACK to apply to temporary network devices
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>
2024-04-02 22:59:50 +01:00
Michael Brown
b66f6025fa [efi] Add the ability to create a temporary MNP network device
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>
2024-03-29 14:46:13 +00:00
Michael Brown
b52b4a46d9 [efi] Allow for allocating EFI devices from arbitrary handles
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>
2024-03-29 14:46:13 +00:00
Michael Brown
764e34f15a [http] Add error table entry for HTTP 404 Not Found error
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>
2024-03-29 14:46:13 +00:00
Michael Brown
afae881782 [tftp] Add error table entry for TFTP "file not found" error code
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>
2024-03-29 14:46:13 +00:00
Michael Brown
43deab89c3 [efi] Add error table entry for local filesystem EFI_NOT_FOUND error
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>
2024-03-29 14:46:13 +00:00
Michael Brown
19f39bc07a [efi] Report local file errors during download, rather than on opening
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>
2024-03-29 14:41:06 +00:00
Michael Brown
f39b48d5f8 [image] Allow opaque URI component to provide image name
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>
2024-03-29 14:12:10 +00:00
Michael Brown
37850e0e85 [build] Fix build failures with random versions of gcc
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>
2024-03-27 14:28:47 +00:00
Michael Brown
9bbe77669c [efi] Extract basic network settings from loaded image device path
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>
2024-03-26 15:57:58 +00:00
Michael Brown
170bbfd487 [efi] Add efi_path_mac() to parse a MAC address from an EFI device path
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-26 15:16:33 +00:00
Michael Brown
dcad73ca5a [efi] Add support for driving EFI_MANAGED_NETWORK_PROTOCOL devices
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>
2024-03-25 17:58:33 +00:00
Michael Brown
da5188f3ea [efi] Allow for drivers to be located via child handles
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>
2024-03-25 17:58:33 +00:00
Michael Brown
ca483a196c [efi] Add helper functions for service binding protocols
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>
2024-03-25 17:58:33 +00:00
Michael Brown
a15ce00182 [efi] Match chainloaded device by uppermost matching handle
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>
2024-03-25 17:58:33 +00:00
Michael Brown
390bce9516 [efi] Set current working URI from our own device path URI, if present
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>
2024-03-19 15:13:59 +00:00
Michael Brown
1a84facf12 [efi] Add efi_path_uri() to parse a URI from an EFI device path
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-19 15:01:25 +00:00
Michael Brown
88c2a01e1a [settings] Expose current working URI and directory URI via settings
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>
2024-03-19 13:33:21 +00:00
Michael Brown
926816c58f [efi] Pad transmit buffer length to work around vendor driver bugs
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>
2024-03-18 22:52:05 +00:00
Rabia Manaa
c11734eee0 [golan] Use ETH_HLEN for inline header size
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>
2024-03-17 22:55:32 +00:00
Michael Brown
fa4bda617d [build] Fix building on older versions of gcc
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>
2024-03-17 17:49:05 +00:00
Michael Brown
bac967d51a [snp] Allocate additional padding for receive buffers
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>
2024-03-16 23:28:34 +00:00
Michael Brown
17882e76af [ucode] Add support for updating x86 microcode
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>
2024-03-15 17:43:49 +00:00
Michael Brown
1344e13a03 [bios] Provide a multiprocessor API for BIOS
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>
2024-03-15 17:30:21 +00:00
Michael Brown
a67f913d66 [librm] Add support for installing a startup IPI handler
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>
2024-03-15 13:27:06 +00:00
Michael Brown
89bb926a04 [efi] Provide a multiprocessor API for EFI
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>
2024-03-15 13:26:53 +00:00
Michael Brown
1ab4d3079d [mp] Define an API for multiprocessor functions
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>
2024-03-15 13:26:53 +00:00
Michael Brown
df2f23e333 [efi] Update to current EDK2 headers
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-13 14:48:44 +00:00
Michael Brown
226531ed36 [ci] Update action versions to silence GitHub warnings
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-07 23:25:35 +00:00
Michael Brown
06e229590c [efi] Do not report return status from efi_block_local()
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>
2024-03-07 14:11:46 +00:00
Michael Brown
24a855f1fc [block] Allow SAN boot device to be identified by filesystem label
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>
2024-03-07 14:11:46 +00:00
Michael Brown
62b6d36335 [block] Allow SAN boot device to be identified by an extra filename
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>
2024-03-07 13:31:29 +00:00
Michael Brown
cea22d76e4 [block] Allow SAN boot device to be identified by UUID
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>
2024-03-06 16:11:29 +00:00
Michael Brown
c4471e3408 [efi] Add efi_path_guid() utility function
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>
2024-03-06 16:11:29 +00:00
Michael Brown
636ccb4ca5 [block] Allow for additional SAN boot parameters alongside filename
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>
2024-03-06 12:19:22 +00:00
Michael Brown
b1c13cc43e [efi] Allow booting from local disks via the "sanboot" command
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>
2024-03-05 13:25:35 +00:00
Michael Brown
8da22a59ee [block] Allow for iteration over SAN device list in drive number order
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>
2024-03-05 13:17:35 +00:00