Compare commits

...

42 Commits
eisa ... shim5

Author SHA1 Message Date
c4a652929c [efi] Support versions of shim that perform SBAT verification
The UEFI shim implements a fairly nicely designed revocation mechanism
designed around the concept of security generations.  Unfortunately
nobody in the shim community has thus far added the relevant metadata
to the Linux kernel, with the result that current versions of shim are
incapable of booting current versions of the Linux kernel.

Experience shows that there is unfortunately no point in trying to get
a fix for this upstreamed into shim.  We therefore default to working
around this undesirable behaviour by patching data read from the
"SbatLevel" variable used to hold SBAT configuration.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-23 14:57:52 +01:00
d2e1601cf4 [efi] Separate GetMemoryMap() wrapper from shim unlocker
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-23 14:52:30 +01:00
95b8338f0d [efi] Add "shim" command
Allow a shim to be used to facilitate booting a kernel using a script
such as:

    kernel /images/vmlinuz console=ttyS0,115200n8
    initrd /images/initrd.img
    shim /images/shimx64.efi
    boot

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-22 15:37:11 +01:00
28184b7c22 [efi] Add support for executing images via a shim
Add support for using a shim as a helper to execute an EFI image.
When a shim has been specified via shim(), the shim image will be
passed to LoadImage() instead of the selected EFI image and the
command line will be prepended with the name of the selected EFI
image.  The selected EFI image will be accessible to the shim via the
virtual filesystem as a hidden file.

Reduce the Secure Boot attack surface by removing, where possible, the
spurious requirement for a third party second stage loader binary such
as GRUB to be used solely in order to call the "shim lock protocol"
entry point.

Do not install the EFI PXE APIs when using a shim, since if shim finds
EFI_PXE_BASE_CODE_PROTOCOL on the loaded image's device handle then it
will attempt to download files afresh instead of using the files
already downloaded by iPXE and exposed via the EFI_SIMPLE_FILE_SYSTEM
protocol.  (Experience shows that there is no point in trying to get a
fix for this upstreamed into shim.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-22 15:37:11 +01:00
3c214f0465 [efi] Add definitions for the UEFI shim lock protocol
The UEFI shim includes a "shim lock protocol" that can be used by a
third party second stage loader such as GRUB to verify a kernel image.

Add definitions for the relevant portions of this protocol interface.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-22 15:37:11 +01:00
ce2200d5fb [efi] Add efi_asprintf() and efi_vasprintf()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-22 15:10:16 +01:00
c4a8d90387 [image] Generalise concept of selected image
Most image flags are independent values: any combination of flags may
be set for any image, and the flags for one image are independent of
the flags for any other image.  The "selected" flag does not follow
this pattern: at most one image may be marked as selected at any time.

When invoking a kernel via the UEFI shim, there will be multiple
"special" images: the selected kernel itself, the shim image, and
potentially a shim-signed GRUB binary to be used as a crutch to assist
shim in loading the kernel (since current versions of the UEFI shim
are not capable of directly loading a Linux kernel).

Remove the "selected" image flag and replace it with a general concept
of an image tag with the same semantics: a given tag may be assigned
to at most one image, an image may be found by its tag only while the
image is currently registered, and a tag will survive unregistration
and reregistration of an image (if it has not already been assigned to
a new image).  For visual consistency, also replace the current image
pointer with a current image tag.

The image pointer stored within the image tag holds only a weak
reference to the image, since the selection of an image should not
prevent that image from being freed.  (The strong reference to the
currently executing image is held locally within the execution scope
of image_exec(), and is logically separate from the current image
pointer.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-17 14:42:03 +01:00
79d85e29aa [efi] Attempt to detect EFI images that fail Secure Boot verification
An EFI image that is rejected by LoadImage() due to failing Secure
Boot verification is still an EFI image.  Unfortunately, the extremely
broken UEFI Secure Boot model provides no way for us to unambiguously
determine that a valid EFI executable image was rejected only because
it failed signature verification.  We must therefore use heuristics to
guess whether not an image that was rejected by LoadImage() could
still be loaded via a separate PE loader such as the UEFI shim.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-17 14:40:50 +01:00
d27cd8196d [ci] Work around Ubuntu packaging metadata issues
The libc6-dbg:i386 package has spontaneously started failing to
install from the Azure package repositories used by the GitHub Actions
runners, with the somewhat recalcitrant error message:

 libc6:i386: Depends: libgcc-s1:i386 but it is not going to be installed

Work around this unexplained issue by explicitly requesting
installation of the libgcc-s1:i386 package.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-15 14:56:28 +01:00
03eea19c19 [efi] Allow currently selected image to be opened as "grub*.efi"
Versions 15.4 and earlier of the UEFI shim are incapable of correctly
parsing the command line in order to extract the second stage loader
filename, and will always attempt to load "grubx64.efi" or equivalent.

Versions 15.3 and later of the UEFI shim are currently incapable of
loading a Linux kernel directly anyway, since the kernel does not
include SBAT metadata.  These versions will require a genuine
shim-signed GRUB binary to be used as a crutch to assist shim in
loading a Linux kernel.

This leaves versions 15.2 and earlier of the UEFI shim (as currently
used in e.g. RHEL7) as being capable of directly loading a Linux
kernel, but incorrectly attempting to load it using the filename
"grubx64.efi" or equivalent.  To support the bugs in these older
versions of the UEFI shim, allow the currently selected image to be
opened via any filename of the form "grub*.efi".

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-05 14:54:20 +01:00
0bb0aea878 [efi] Allow currently executing image to be opened via virtual filesystem
When invoking a kernel via the UEFI shim, the kernel image must be
accessible via EFI_SIMPLE_FILE_SYSTEM_PROTOCOL but must not be present
in the magic initrd constructed from all registered images.

Re-register a currently executing EFI image and mark it as hidden,
thereby allowing it to be accessed via the virtual filesystem exposed
via EFI_SIMPLE_FILE_SYSTEM_PROTOCOL without appearing in the magic
initrd contents.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-05 14:54:20 +01:00
f9beb20e99 [image] Allow for images to be hidden from lists of all images
When invoking a kernel via the UEFI shim, the kernel (and potentially
also a helper binary such as GRUB) must be accessible via the virtual
filesystem exposed via EFI_SIMPLE_FILE_SYSTEM_PROTOCOL but must not be
present in the magic initrd constructed from all registered images.

Allow for images to be flagged as hidden, which will cause them to be
excluded from API-level lists of all images such as the virtual
filesystem directory contents, the magic initrd, or the Multiboot
module list.  Hidden images remain visible to iPXE commands including
"imgstat", which will show a "[HIDDEN]" flag for such images.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-05 14:54:20 +01:00
f93e6b712f [efi] Show original filenames in debug messages
Show the original filename as used by the consumer when calling our
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL's Open() method.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-05 13:05:28 +01:00
22cc65535a [efi] Allow downloaded images to take precedence over constructed files
Try searching for a matching registered image before checking for
fixed filenames (such as "initrd.magic" for the dynamically generated
magic initrd file).  This minimises surprise by ensuring that an
explicitly downloaded image will always be used verbatim.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-05 13:05:28 +01:00
bd13697446 [efi] Allow for sections to be excluded from the generated PE file
Hybrid bzImage and UEFI binaries (such as wimboot) include a bzImage
header within a section starting at offset zero, with the PE header
effectively occupying unused space within this section.  This section
should not appear as a named section in the resulting PE file.

Allow for the existence of hidden sections that do not result in a
section header being written to the PE file.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-04-10 17:02:45 +01:00
9fb28080d9 [efi] Allow elf2efi to be used for hybrid binaries
Hybrid 32-bit BIOS and 64-bit UEFI binaries (such as wimboot) may
include R_X86_64_32 relocation records for the 32-bit BIOS portions.
These should be ignored when generating PE relocation records, since
they apply only to code that cannot be executed within the context of
the 64-bit UEFI binary, and creating a 4-byte relocation record is
invalid in a binary that may be relocated anywhere within the 64-bit
address space (see commit 907cffb "[efi] Disallow R_X86_64_32
relocations").

Add a "--hybrid" option to elf2efi, which will cause R_X86_64_32
relocation records to be silently discarded.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-04-10 16:51:51 +01:00
1e4c3789e9 [efi] Shrink size of data directory in PE header
Hybrid bzImage and UEFI binaries (such as wimboot) require the PE
header to be kept as small as possible, since the bzImage header
starts at a fixed offset 0x1f1.

The EFI_IMAGE_OPTIONAL_HEADER structures in PeImage.h define an
optional header containing 16 data directory entries, of which the
last eight are unused in binaries that we create.  Shrink the data
directory to contain only the first eight entries, to minimise the
overall size of the PE header.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-04-10 16:51:49 +01:00
0d04635ef0 [efi] Remove redundant zero padding in PE header
Hybrid bzImage and UEFI binaries (such as wimboot) require the PE
header to be kept as small as possible, since the bzImage header
starts at a fixed offset 0x1f1.

The PE header currently includes 128 bytes of zero padding between the
DOS and NT header portions.  This padding has been present since
commit 81d92c6 ("[efi] Add EFI image format and basic runtime
environment") first added support for EFI images in iPXE, and was
included on the basis of matching the observed behaviour of the
Microsoft toolchain.  There appears to be no requirement for this
padding to exist: EDK2 binaries built with gcc include only 64 bytes
of zero padding, Linux kernel binaries include 66 bytes of non-zero
padding, and wimboot binaries include no padding at all.

Remove the unnecessary padding between the DOS and NT header portions
to minimise the overall size of the PE header.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-04-10 16:50:10 +01:00
1d1cf74a5e [tls] Handle fragmented handshake records
Originally-implemented-by: Christopher Schenk <christopher@cschenk.net>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-30 23:38:43 +01:00
aa368ba529 [tls] Pass I/O buffer to received record handlers
Prepare for the possibility that a record handler may choose not to
consume the entire record by passing the I/O buffer and requiring the
handler to mark consumed data using iob_pull().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-30 23:37:55 +01:00
2c6a15d2a3 [tls] Clean up change cipher spec record handling
Define and use data structures and constants for the (single-byte)
change cipher spec records.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-30 16:57:12 +01:00
09e8a15408 [efi] Claim fixed device paths by uninstalling device path protocol
As documented in commits 6a004be ("[efi] Support the initrd
autodetection mechanism in newer Linux kernels") and 04e60a2 ("[efi]
Omit EFI_LOAD_FILE2_PROTOCOL for a zero-length initrd"), the choice in
Linux of using a fixed device path requires bootloaders to allow for
the fact that a previous bootloader may have already installed a
handle with the fixed device path.

We currently deal with this situation by reusing the existing handle,
replacing the EFI_LOAD_FILE2_PROTOCOL instance with our own.  Simplify
the code by instead uninstalling the EFI_DEVICE_PATH_PROTOCOL instance
from the existing handle (if present), thereby allowing the creation
of a new handle to succeed.

Create the new handle only if we have a non-empty initrd to provide.
This works around bugs in bootloaders such as the systemd EFI stub
that fail to allow for the existence of multiple-bootloader chains.
(The workaround is not comprehensive: if the user has downloaded other
images in iPXE before invoking the systemd Unified Kernel Image (UKI),
then the systemd EFI stub will still crash and burn since it fails to
allow for the fact that a previous bootloader has already installed a
handle with the fixed device path.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-15 16:48:35 +00:00
bf25e23d07 [intel] Add workaround for I210 reset hardware bugs
The Intel I210's packet buffer size registers reset only on power up,
not when a reset signal is asserted.  This can lead to the inability
to pass traffic in the event that the DMA TX Maximum Packet Size
(which does reset to its default value on reset) is bigger than the TX
Packet Buffer Size.

For example, an operating system may be using the time sensitive
networking features of the I210 and the registers may be programmed
correctly, but then a reset signal is asserted and iPXE on the next
boot will be unable to use the I210.

Mimic what Linux does and forcibly set the registers to their default
values.

Signed-off-by: Matt Parrella <parrella.matthew@gmail.com>
2023-03-14 14:44:32 +00:00
8f1c120119 [dhcp] Unregister ProxyDHCP and PXEBS settings on a successful DHCPACK
When a DHCP transaction does not result in the registration of a new
"proxydhcp" or "pxebs" settings block, any existing settings blocks
are currently left unaltered.

This can cause surprising behaviour.  For example: when chainloading
iPXE, the "proxydhcp" and "pxebs" settings blocks may be prepopulated
using cached values from the previous PXE bootloader.  If iPXE
performs a subsequent DHCP request, then the DHCP or ProxyDHCP servers
may choose to respond differently to iPXE.  The response may choose to
omit the ProxyDHCP or PXEBS stages, in which case no new "proxydhcp"
or "pxebs" settings blocks may be registered.  This will result in
iPXE using a combination of both old and new DHCP responses.

Fix by assuming that a successful DHCPACK effectively acquires
ownership of the "proxydhcp" and "pxebs" settings blocks, and that any
existing settings blocks should therefore be unregistered.

Reported-by: Henry Tung <htung@palantir.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-14 11:35:30 +00:00
54fcb7c29c [efi] Use image name instead of pointer value in debug messages
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-07 14:18:00 +00:00
9e1f7a3659 [image] Always unregister currently executing image
We unregister script images during their execution, to prevent a
"boot" command from re-executing the containing script.  This also has
the side effect of preventing executing scripts from showing up within
the Linux magic initrd image (or the Multiboot module list).

Additional logic in bzimage.c and efi_file.c prevents a currently
executing kernel from showing up within the magic initrd image.
Similar logic in multiboot.c prevents the Multiboot kernel from
showing up as a Multiboot module.

This still leaves some corner cases that are not covered correctly.
For example: when using a gzip-compressed kernel image, nothing will
currently hide the original compressed image from the magic initrd.

Fix by moving the logic that temporarily unregisters the current image
from script_exec() to image_exec(), so that it applies to all image
types, and simplify the magic initrd and Multiboot module list
construction logic on the basis that no further filtering of the
registered image list is necessary.

This change has the side effect of hiding currently executing EFI
images from the virtual filesystem exposed by iPXE.  For example, when
using iPXE to boot wimboot, the wimboot binary itself will no longer
be visible within the virtual filesystem.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-07 12:22:19 +00:00
e51e7bbad7 [image] Consistently use for_each_image() to iterate over images
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-06 16:56:37 +00:00
523788ccda [intelx] Add PCI IDs for Intel 82599 10GBASE-T NIC
Signed-off-by: Forest Crossman <cyrozap@gmail.com>
2023-03-05 18:22:18 -06:00
96bb6ba441 [params] Allow for arbitrary HTTP request headers to be specified
Extend the request parameter mechanism to allow for arbitrary HTTP
headers to be specified via e.g.:

  params
  param --header Referer http://www.example.com
  imgfetch http://192.168.0.1/script.ipxe##params

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-01 12:20:02 +00:00
33cb56cf1b [params] Rename "form parameter" to "request parameter"
Prepare for the parameter mechanism to be generalised to specifying
request parameters that are passed via mechanisms other than an
application/x-www-form-urlencoded form.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-01 11:55:04 +00:00
60531ff6e2 [http] Use POST method only if the form parameter list is non-empty
An attempt to use an existent but empty form parameter list will
currently result in an invalid POST request since the Content-Length
header will be missing.

Fix by using GET instead of POST if the form parameter list is empty.
This is a non-breaking change (since the current behaviour produces an
invalid request), and simplifies the imminent generalisation of the
parameter list concept to handle both header and form parameters.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-03-01 11:12:44 +00:00
04e60a278a [efi] Omit EFI_LOAD_FILE2_PROTOCOL for a zero-length initrd
When the Linux kernel is being used with no initrd, iPXE will still
provide a zero-length initrd.magic file within the virtual filesystem.
As of commit 6a004be ("[efi] Support the initrd autodetection
mechanism in newer Linux kernels"), this zero-length file will also be
exposed via an EFI_LOAD_FILE2_PROTOCOL instance on a handle with a
fixed device path.

The correct handling of zero-length files via EFI_LOAD_FILE2_PROTOCOL
is unfortunately not well defined.

Linux expects the first call to LoadFile() to always fail with
EFI_BUFFER_TOO_SMALL.  When the initrd is genuinely zero-length, iPXE
will return success since the buffer is not too small to hold the
(zero-length) file.  This causes Linux to immediately report a
spurious EFI_LOAD_ERROR boot failure.

We could change the logic in iPXE's efi_file_load() to always return
EFI_BUFFER_TOO_SMALL if Buffer is NULL on entry.  Since the correct
behaviour of LoadFile() in the corner case of a zero-length file is
left undefined by the UEFI specification, this would be permissible.

Unfortunately this approach would not fix the problem.  If we return
EFI_BUFFER_TOO_SMALL and set the file length to zero, then Linux will
call the boot services AllocatePages() method with a zero length.  In
at least the EDK2 implementation, this combination of parameters will
cause AllocatePages() to return EFI_OUT_OF_RESOURCES, and Linux will
again report a boot failure.

Another approach would be to install the initrd device path handle
only if we have a non-empty initrd to offer.  Unfortunately this would
lead to a failure in yet another corner case: if a previous bootloader
has installed an initrd device path handle (e.g. to pass a boot script
to iPXE) then we must not leave that initrd in place, since then our
loaded kernel would end up seeing the wrong initrd content.

The cleanest fix seems to be to ensure that the initrd device path
handle is installed with the EFI_DEVICE_PATH_PROTOCOL instance present
but with the EFI_LOAD_FILE2_PROTOCOL instance absent (and forcibly
uninstalled if necessary), matching the state in which we leave the
handle after uninstalling our virtual filesystem.  Linux will then not
find any handle that supports EFI_LOAD_FILE2_PROTOCOL within the fixed
device path, and so will fall through to trying other mechanisms to
locate the initrd.

Reported-by: Chris Bradshaw <cwbshaw@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-28 12:30:54 +00:00
471599dc77 [efi] Split out EFI_RNG_PROTOCOL as a separate entropy source
Commit 7ca801d ("[efi] Use the EFI_RNG_PROTOCOL as an entropy source
if available") added EFI_RNG_PROTOCOL as an alternative entropy source
via an ad-hoc mechanism specific to efi_entropy.c.

Split out EFI_RNG_PROTOCOL to a separate entropy source, and allow the
entropy core to handle the selection of RDRAND, EFI_RNG_PROTOCOL, or
timer ticks as the active source.

The fault detection logic added in commit a87537d ("[efi] Detect and
disable seriously broken EFI_RNG_PROTOCOL implementations") may be
removed completely, since the failure will already be detected by the
generic ANS X9.82-mandated repetition count test and will now be
handled gracefully by the entropy core.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-20 14:53:10 +00:00
7d71cf318a [rng] Allow for entropy sources that fail during startup tests
Provide per-source state variables for the repetition count test and
adaptive proportion test, to allow for the situation in which an
entropy source can be enabled but then fails during the startup tests,
thereby requiring an alternative entropy source to be used.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-20 14:53:10 +00:00
6625e49cea [tables] Allow any lvalue to be used as a table iterator
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-20 13:46:45 +00:00
9f17d1116d [rng] Allow entropy source to be selected at runtime
As noted in commit 3c83843 ("[rng] Check for several functioning RTC
interrupts"), experimentation shows that Hyper-V cannot be trusted to
reliably generate RTC interrupts.  (As noted in commit f3ba0fb
("[hyperv] Provide timer based on the 10MHz time reference count
MSR"), Hyper-V appears to suffer from a general problem in reliably
generating any legacy interrupts.)  An alternative entropy source is
therefore required for an image that may be used in a Hyper-V Gen1
virtual machine.

The x86 RDRAND instruction provides a suitable alternative entropy
source, but may not be supported by all CPUs.  We must therefore allow
for multiple entropy sources to be compiled in, with the single active
entropy source selected only at runtime.

Restructure the internal entropy API to allow a working entropy source
to be detected and chosen at runtime.

Enable the RDRAND entropy source for all x86 builds, since it is
likely to be substantially faster than any other source.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-17 21:29:51 +00:00
2733c4763a [iscsi] Limit maximum transfer size to MaxBurstLength
We currently specify only the iSCSI default value for MaxBurstLength
and ignore any negotiated value, since our internal block device API
allows only for receiving directly into caller-allocated buffers and
so we have no intrinsic limit on burst length.

A conscientious target may however refuse to attempt a transfer that
we request for a number of blocks that would exceed the negotiated
maximum burst length.

Fix by recording the negotiated maximum burst length and using it to
limit the maximum number of blocks per transfer as reported by the
SCSI layer.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-16 13:27:25 +00:00
cff857461b [rng] Add RDRAND as an entropy source
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-15 22:43:33 +00:00
6a004be0cc [efi] Support the initrd autodetection mechanism in newer Linux kernels
Linux 5.7 added the ability to autodetect an initrd by searching for a
handle via a fixed vendor-specific "Linux initrd device path" and then
locating and using the EFI_LOAD_FILE2_PROTOCOL instance on that
handle.

This maps quite naturally onto our existing concept of a "magic
initrd" as introduced for EFI in commit e5f0255 ("[efi] Provide an
"initrd.magic" file for use by UEFI kernels").

Add an EFI_LOAD_FILE2_PROTOCOL instance to our EFI virtual files
(backed by simply calling the existing EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
method to read from the file), and install the protocol instance for
the "initrd.magic" virtual file onto a new device handle that also
provides the Linux initrd device path.

The design choice in Linux of using a single fixed device path makes
this unfortunately messy to support, since device paths must be unique
within a system.  When multiple bootloaders are used (e.g. GRUB
loading iPXE loading Linux) then only one bootloader can ever install
the device path onto a handle.  Subsequent bootloaders must locate the
existing handle and replace the load file protocol instance with their
own.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-15 17:36:47 +00:00
cf9ad00afc [efi] Fix debug message when reading from EFI virtual files
Show the requested range when a caller reads from a virtual file via
the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interface.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-15 17:20:39 +00:00
76a286530a [image] Check delimiters when parsing command-line key-value arguments
The Linux kernel bzImage image format and the CPIO archive constructor
will parse the image command line for certain arguments of the form
"key=value".  This parsing is currently implemented using strstr() in
a way that can cause a false positive suffix match.  For example, a
command line containing "highmem=<n>" would erroneously be treated as
containing a value for "mem=<n>".

Fix by centralising the logic used for parsing such arguments, and
including a check that the argument immediately follows a whitespace
delimiter (or is at the start of the string).

Reported-by: Filippo Giunchedi <filippo@esaurito.net>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-14 11:13:45 +00:00
3c83843e11 [rng] Check for several functioning RTC interrupts
Commit 74222cd ("[rng] Check for functioning RTC interrupt") added a
check that the RTC is capable of generating interrupts via the legacy
PIC, since this mechanism appears to be broken in some Hyper-V virtual
machines.

Experimentation shows that the RTC is sometimes capable of generating
a single interrupt, but will then generate no subsequent interrupts.
This currently causes rtc_entropy_check() to falsely detect that the
entropy gathering mechanism is functional.

Fix by checking for several RTC interrupts before declaring that it is
a functional entropy source.

Reported-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-11 15:11:51 +00:00
65 changed files with 2775 additions and 1043 deletions

View File

@ -49,7 +49,8 @@ jobs:
sudo apt update
sudo apt install -y -o Acquire::Retries=50 \
mtools syslinux isolinux \
libc6-dev-i386 libc6-dbg:i386 valgrind
libc6-dev-i386 valgrind \
libgcc-s1:i386 libc6-dbg:i386
- name: Build (BIOS)
run: |
make -j 4 -C src

View File

@ -1,12 +0,0 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* ARM-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_ENTROPY_H */

View File

@ -1,12 +0,0 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* LoongArch64-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_ENTROPY_H */

103
src/arch/x86/core/rdrand.c Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Hardware random number generator
*
*/
#include <errno.h>
#include <ipxe/cpuid.h>
#include <ipxe/entropy.h>
#include <ipxe/drbg.h>
struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED );
/** Number of times to retry RDRAND instruction */
#define RDRAND_RETRY_COUNT 16
/** Colour for debug messages */
#define colour &rdrand_entropy
/**
* Enable entropy gathering
*
* @ret rc Return status code
*/
static int rdrand_entropy_enable ( void ) {
struct x86_features features;
/* Check that RDRAND is supported */
x86_features ( &features );
if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_RDRAND ) ) {
DBGC ( colour, "RDRAND not supported\n" );
return -ENOTSUP;
}
/* Data returned by RDRAND is theoretically full entropy, up
* to a security strength of 128 bits, so assume that each
* sample contains exactly 8 bits of entropy.
*/
if ( DRBG_SECURITY_STRENGTH > 128 )
return -ENOTSUP;
entropy_init ( &rdrand_entropy, MIN_ENTROPY ( 8.0 ) );
return 0;
}
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*/
static int rdrand_get_noise ( noise_sample_t *noise ) {
unsigned int result;
unsigned int discard_c;
unsigned int ok;
/* Issue RDRAND, retrying until CF is set */
__asm__ ( "\n1:\n\t"
"rdrand %0\n\t"
"sbb %1, %1\n\t"
"loopz 1b\n\t"
: "=r" ( result ), "=r" ( ok ), "=c" ( discard_c )
: "2" ( RDRAND_RETRY_COUNT ) );
if ( ! ok ) {
DBGC ( colour, "RDRAND failed to become ready\n" );
return -EBUSY;
}
*noise = result;
return 0;
}
/** Hardware random number generator entropy source */
struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED ) = {
.name = "rdrand",
.enable = rdrand_entropy_enable,
.get_noise = rdrand_get_noise,
};

View File

@ -247,19 +247,17 @@ static void bzimage_update_header ( struct image *image,
*
* @v image bzImage file
* @v bzimg bzImage context
* @v cmdline Kernel command line
* @ret rc Return status code
*/
static int bzimage_parse_cmdline ( struct image *image,
struct bzimage_context *bzimg,
char *cmdline ) {
struct bzimage_context *bzimg ) {
const char *vga;
const char *mem;
char *sep;
char *vga;
char *mem;
char *end;
/* Look for "vga=" */
if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
vga += 4;
if ( ( vga = image_argument ( image, "vga=" ) ) ) {
sep = strchr ( vga, ' ' );
if ( sep )
*sep = '\0';
@ -270,10 +268,10 @@ static int bzimage_parse_cmdline ( struct image *image,
} else if ( strcmp ( vga, "ask" ) == 0 ) {
bzimg->vid_mode = BZI_VID_MODE_ASK;
} else {
bzimg->vid_mode = strtoul ( vga, &vga, 0 );
if ( *vga ) {
bzimg->vid_mode = strtoul ( vga, &end, 0 );
if ( *end ) {
DBGC ( image, "bzImage %p strange \"vga=\" "
"terminator '%c'\n", image, *vga );
"terminator '%c'\n", image, *end );
}
}
if ( sep )
@ -281,10 +279,9 @@ static int bzimage_parse_cmdline ( struct image *image,
}
/* Look for "mem=" */
if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
mem += 4;
bzimg->mem_limit = strtoul ( mem, &mem, 0 );
switch ( *mem ) {
if ( ( mem = image_argument ( image, "mem=" ) ) ) {
bzimg->mem_limit = strtoul ( mem, &end, 0 );
switch ( *end ) {
case 'G':
case 'g':
bzimg->mem_limit <<= 10;
@ -302,7 +299,7 @@ static int bzimage_parse_cmdline ( struct image *image,
break;
default:
DBGC ( image, "bzImage %p strange \"mem=\" "
"terminator '%c'\n", image, *mem );
"terminator '%c'\n", image, *end );
break;
}
bzimg->mem_limit -= 1;
@ -316,11 +313,10 @@ static int bzimage_parse_cmdline ( struct image *image,
*
* @v image bzImage image
* @v bzimg bzImage context
* @v cmdline Kernel command line
*/
static void bzimage_set_cmdline ( struct image *image,
struct bzimage_context *bzimg,
const char *cmdline ) {
struct bzimage_context *bzimg ) {
const char *cmdline = ( image->cmdline ? image->cmdline : "" );
size_t cmdline_len;
/* Copy command line down to real-mode portion */
@ -359,8 +355,8 @@ static size_t bzimage_load_initrd ( struct image *image,
size_t offset;
size_t pad_len;
/* Do not include kernel image itself as an initrd */
if ( initrd == image )
/* Skip hidden images */
if ( initrd->flags & IMAGE_HIDDEN )
return 0;
/* Create cpio header for non-prebuilt images */
@ -410,10 +406,6 @@ static int bzimage_check_initrds ( struct image *image,
/* Calculate total loaded length of initrds */
for_each_image ( initrd ) {
/* Skip kernel */
if ( initrd == image )
continue;
/* Calculate length */
len += bzimage_load_initrd ( image, initrd, UNULL );
len = bzimage_align ( len );
@ -528,7 +520,6 @@ static void bzimage_load_initrds ( struct image *image,
*/
static int bzimage_exec ( struct image *image ) {
struct bzimage_context bzimg;
char *cmdline = ( image->cmdline ? image->cmdline : "" );
int rc;
/* Read and parse header from image */
@ -551,7 +542,7 @@ static int bzimage_exec ( struct image *image ) {
}
/* Parse command line for bootloader parameters */
if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
if ( ( rc = bzimage_parse_cmdline ( image, &bzimg ) ) != 0)
return rc;
/* Check that initrds can be loaded */
@ -568,7 +559,7 @@ static int bzimage_exec ( struct image *image ) {
bzimg.rm_filesz, bzimg.pm_sz );
/* Store command line */
bzimage_set_cmdline ( image, &bzimg, cmdline );
bzimage_set_cmdline ( image, &bzimg );
/* Prepare for exiting. Must do this before loading initrds,
* since loading the initrds will corrupt the external heap.

View File

@ -204,8 +204,8 @@ static int multiboot_add_modules ( struct image *image, physaddr_t start,
break;
}
/* Do not include kernel image itself as a module */
if ( module_image == image )
/* Skip hidden images */
if ( module_image->flags & IMAGE_HIDDEN )
continue;
/* Page-align the module */

View File

@ -1,14 +0,0 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* x86-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/rtc_entropy.h>
#endif /* _BITS_ENTROPY_H */

View File

@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_cpuid ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 )
#define ERRFILE_rdtsc_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 )
#define ERRFILE_acpi_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00130000 )
#define ERRFILE_rdrand ( ERRFILE_ARCH | ERRFILE_CORE | 0x00140000 )
#define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )

View File

@ -39,6 +39,9 @@ struct x86_features {
/** Get standard features */
#define CPUID_FEATURES 0x00000001UL
/** RDRAND instruction is supported */
#define CPUID_FEATURES_INTEL_ECX_RDRAND 0x40000000UL
/** Hypervisor is present */
#define CPUID_FEATURES_INTEL_ECX_HYPERVISOR 0x80000000UL

View File

@ -1,62 +0,0 @@
#ifndef _IPXE_RTC_ENTROPY_H
#define _IPXE_RTC_ENTROPY_H
/** @file
*
* RTC-based entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#ifdef ENTROPY_RTC
#define ENTROPY_PREFIX_rtc
#else
#define ENTROPY_PREFIX_rtc __rtc_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) {
/* The min-entropy has been measured on several platforms
* using the entropy_sample test code. Modelling the samples
* as independent, and using a confidence level of 99.99%, the
* measurements were as follows:
*
* qemu-kvm : 7.38 bits
* VMware : 7.46 bits
* Physical hardware : 2.67 bits
*
* We choose the lowest of these (2.67 bits) and apply a 50%
* safety margin to allow for some potential non-independence
* of samples.
*/
return MIN_ENTROPY ( 1.3 );
}
extern uint8_t rtc_sample ( void );
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*/
static inline __always_inline int
ENTROPY_INLINE ( rtc, get_noise ) ( noise_sample_t *noise ) {
/* Get sample */
*noise = rtc_sample();
/* Always successful */
return 0;
}
#endif /* _IPXE_RTC_ENTROPY_H */

View File

@ -39,9 +39,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/cpuid.h>
#include <ipxe/entropy.h>
struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL );
/** Maximum time to wait for an RTC interrupt, in milliseconds */
#define RTC_MAX_WAIT_MS 100
/** Number of RTC interrupts to check for */
#define RTC_CHECK_COUNT 3
/** RTC interrupt handler */
extern void rtc_isr ( void );
@ -145,6 +150,7 @@ static void rtc_disable_int ( void ) {
* @ret rc Return status code
*/
static int rtc_entropy_check ( void ) {
unsigned int count = 0;
unsigned int i;
/* Check that RTC interrupts are working */
@ -158,14 +164,18 @@ static int rtc_entropy_check ( void ) {
"cli\n\t" );
/* Check for RTC interrupt flag */
if ( rtc_flag )
return 0;
if ( rtc_flag ) {
rtc_flag = 0;
if ( ++count >= RTC_CHECK_COUNT )
return 0;
}
/* Delay */
mdelay ( 1 );
}
DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
DBGC ( &rtc_flag, "RTC timed out waiting for interrupt %d/%d\n",
( count + 1 ), RTC_CHECK_COUNT );
return -ETIMEDOUT;
}
@ -195,6 +205,21 @@ static int rtc_entropy_enable ( void ) {
if ( ( rc = rtc_entropy_check() ) != 0 )
goto err_check;
/* The min-entropy has been measured on several platforms
* using the entropy_sample test code. Modelling the samples
* as independent, and using a confidence level of 99.99%, the
* measurements were as follows:
*
* qemu-kvm : 7.38 bits
* VMware : 7.46 bits
* Physical hardware : 2.67 bits
*
* We choose the lowest of these (2.67 bits) and apply a 50%
* safety margin to allow for some potential non-independence
* of samples.
*/
entropy_init ( &rtc_entropy, MIN_ENTROPY ( 1.3 ) );
return 0;
err_check:
@ -218,11 +243,12 @@ static void rtc_entropy_disable ( void ) {
}
/**
* Measure a single RTC tick
* Get noise sample
*
* @ret delta Length of RTC tick (in TSC units)
* @ret noise Noise sample
* @ret rc Return status code
*/
uint8_t rtc_sample ( void ) {
static int rtc_get_noise ( noise_sample_t *noise ) {
uint32_t before;
uint32_t after;
uint32_t temp;
@ -257,10 +283,14 @@ uint8_t rtc_sample ( void ) {
: "=a" ( after ), "=d" ( before ), "=Q" ( temp )
: "2" ( 0 ) );
return ( after - before );
*noise = ( after - before );
return 0;
}
PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
PROVIDE_ENTROPY_INLINE ( rtc, get_noise );
/** RTC entropy source */
struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL ) = {
.name = "rtc",
.enable = rtc_entropy_enable,
.disable = rtc_entropy_disable,
.get_noise = rtc_get_noise,
};

View File

@ -290,6 +290,9 @@ REQUIRE_OBJECT ( cert_cmd );
#ifdef IMAGE_MEM_CMD
REQUIRE_OBJECT ( image_mem_cmd );
#endif
#ifdef SHIM_CMD
REQUIRE_OBJECT ( shim_cmd );
#endif
/*
* Drag in miscellaneous objects

View File

@ -1,10 +1,8 @@
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
@ -23,18 +21,31 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <config/entropy.h>
/** @file
*
* Nonexistent entropy source
* Entropy configuration options
*
*
* This source provides no entropy and must NOT be used in a
* security-sensitive environment.
*/
#include <ipxe/entropy.h>
PROVIDE_REQUIRING_SYMBOL();
PROVIDE_ENTROPY_INLINE ( null, min_entropy_per_sample );
PROVIDE_ENTROPY_INLINE ( null, entropy_enable );
PROVIDE_ENTROPY_INLINE ( null, entropy_disable );
PROVIDE_ENTROPY_INLINE ( null, get_noise );
/*
* Drag in entropy sources
*/
#ifdef ENTROPY_RTC
REQUIRE_OBJECT ( rtc_entropy );
#endif
#ifdef ENTROPY_EFITICK
REQUIRE_OBJECT ( efi_entropy );
#endif
#ifdef ENTROPY_EFIRNG
REQUIRE_OBJECT ( efi_rng );
#endif
#ifdef ENTROPY_LINUX
REQUIRE_OBJECT ( linux_entropy );
#endif
#ifdef ENTROPY_RDRAND
REQUIRE_OBJECT ( rdrand );
#endif

View File

@ -19,7 +19,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define SMBIOS_EFI
#define SANBOOT_EFI
#define BOFM_EFI
#define ENTROPY_EFI
#define ENTROPY_EFITICK
#define ENTROPY_EFIRNG
#define TIME_EFI
#define REBOOT_EFI
#define ACPI_EFI
@ -46,10 +47,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define USB_BLOCK /* USB block devices */
#define REBOOT_CMD /* Reboot command */
#define SHIM_CMD /* EFI shim command */
#if defined ( __i386__ ) || defined ( __x86_64__ )
#define IOAPI_X86
#define NAP_EFIX86
#define ENTROPY_RDRAND
#define CPUID_CMD /* x86 CPU feature detection command */
#define UNSAFE_STD /* Avoid setting direction flag */
#endif

View File

@ -33,4 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define SANBOOT_PROTO_FCP
#define SANBOOT_PROTO_HTTP
#if defined ( __i386__ ) || defined ( __x86_64__ )
#define ENTROPY_RDRAND
#endif
#endif /* CONFIG_DEFAULTS_LINUX_H */

View File

@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define SMBIOS_PCBIOS
#define SANBOOT_PCBIOS
#define ENTROPY_RTC
#define ENTROPY_RDRAND
#define TIME_RTC
#define REBOOT_PCBIOS
#define ACPI_RSDP

View File

@ -150,7 +150,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
//#define POWEROFF_CMD /* Power off command */
//#define IMAGE_TRUST_CMD /* Image trust management commands */
//#define PCI_CMD /* PCI commands */
//#define PARAM_CMD /* Form parameter commands */
//#define PARAM_CMD /* Request parameter commands */
//#define NEIGHBOUR_CMD /* Neighbour management commands */
//#define PING_CMD /* Ping command */
//#define CONSOLE_CMD /* Console command */
@ -160,6 +160,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
//#define CERT_CMD /* Certificate management commands */
//#define IMAGE_MEM_CMD /* Read memory command */
#define IMAGE_ARCHIVE_CMD /* Archive image management commands */
//#define SHIM_CMD /* EFI shim command */
/*
* ROM-specific options

View File

@ -77,17 +77,12 @@ size_t cpio_name_len ( struct image *image ) {
*/
static void cpio_parse_cmdline ( struct image *image,
struct cpio_header *cpio ) {
const char *cmdline;
char *arg;
const char *arg;
char *end;
unsigned int mode;
/* Skip image filename */
cmdline = ( cpio_name ( image ) + cpio_name_len ( image ) );
/* Look for "mode=" */
if ( ( arg = strstr ( cmdline, "mode=" ) ) ) {
arg += 5;
if ( ( arg = image_argument ( image, "mode=" ) ) ) {
mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
if ( *end && ( *end != ' ' ) ) {
DBGC ( image, "CPIO %p strange \"mode=\" "

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <libgen.h>
@ -55,8 +56,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** List of registered images */
struct list_head images = LIST_HEAD_INIT ( images );
/** Image selected for execution */
struct image_tag selected_image __image_tag = {
.name = "SELECTED",
};
/** Currently-executing image */
struct image *current_image;
struct image_tag current_image __image_tag = {
.name = "CURRENT",
};
/** Current image trust requirement */
static int require_trusted_images = 0;
@ -71,8 +79,13 @@ static int require_trusted_images_permanent = 0;
*/
static void free_image ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt );
struct image_tag *tag;
DBGC ( image, "IMAGE %s freed\n", image->name );
for_each_table_entry ( tag, IMAGE_TAGS ) {
if ( tag->image == image )
tag->image = NULL;
}
free ( image->name );
free ( image->cmdline );
uri_put ( image->uri );
@ -260,12 +273,6 @@ int register_image ( struct image *image ) {
return rc;
}
/* Avoid ending up with multiple "selected" images on
* re-registration
*/
if ( image_find_selected() )
image->flags &= ~IMAGE_SELECTED;
/* Add to image list */
image_get ( image );
image->flags |= IMAGE_REGISTERED;
@ -311,7 +318,7 @@ void unregister_image ( struct image *image ) {
struct image * find_image ( const char *name ) {
struct image *image;
list_for_each_entry ( image, &images, list ) {
for_each_image ( image ) {
if ( strcmp ( image->name, name ) == 0 )
return image;
}
@ -319,6 +326,23 @@ struct image * find_image ( const char *name ) {
return NULL;
}
/**
* Find image by tag
*
* @v tag Image tag
* @ret image Executable image, or NULL
*/
struct image * find_image_tag ( struct image_tag *tag ) {
struct image *image;
for_each_image ( image ) {
if ( tag->image == image )
return image;
}
return NULL;
}
/**
* Execute image
*
@ -345,14 +369,13 @@ int image_exec ( struct image *image ) {
if ( image->uri )
churi ( image->uri );
/* Preserve record of any currently-running image */
saved_current_image = current_image;
/* Set as currently running image */
saved_current_image = image_tag ( image, &current_image );
/* Take out a temporary reference to the image. This allows
* the image to unregister itself if necessary, without
* automatically freeing itself.
/* Take out a temporary reference to the image, so that it
* does not get freed when temporarily unregistered.
*/
current_image = image_get ( image );
image_get ( image );
/* Check that this image can be executed */
if ( ! ( image->type && image->type->exec ) ) {
@ -370,6 +393,9 @@ int image_exec ( struct image *image ) {
/* Record boot attempt */
syslog ( LOG_NOTICE, "Executing \"%s\"\n", image->name );
/* Temporarily unregister the image during its execution */
unregister_image ( image );
/* Try executing the image */
if ( ( rc = image->type->exec ( image ) ) != 0 ) {
DBGC ( image, "IMAGE %s could not execute: %s\n",
@ -386,6 +412,10 @@ int image_exec ( struct image *image ) {
image->name, strerror ( rc ) );
}
/* Re-register image (unless due to be replaced) */
if ( ! image->replacement )
register_image ( image );
/* Pick up replacement image before we drop the original
* image's temporary reference. The replacement image must
* already be registered, so we don't need to hold a temporary
@ -412,7 +442,7 @@ int image_exec ( struct image *image ) {
image_put ( image );
/* Restore previous currently-running image */
current_image = saved_current_image;
image_tag ( saved_current_image, &current_image );
/* Reset current working directory */
churi ( old_cwuri );
@ -435,7 +465,7 @@ int image_exec ( struct image *image ) {
* registered until the currently-executing image returns.
*/
int image_replace ( struct image *replacement ) {
struct image *image = current_image;
struct image *image = current_image.image;
int rc;
/* Sanity check */
@ -471,37 +501,17 @@ int image_replace ( struct image *replacement ) {
* @ret rc Return status code
*/
int image_select ( struct image *image ) {
struct image *tmp;
/* Unselect all other images */
for_each_image ( tmp )
tmp->flags &= ~IMAGE_SELECTED;
/* Check that this image can be executed */
if ( ! ( image->type && image->type->exec ) )
return -ENOEXEC;
/* Mark image as selected */
image->flags |= IMAGE_SELECTED;
image_tag ( image, &selected_image );
return 0;
}
/**
* Find selected image
*
* @ret image Executable image, or NULL
*/
struct image * image_find_selected ( void ) {
struct image *image;
for_each_image ( image ) {
if ( image->flags & IMAGE_SELECTED )
return image;
}
return NULL;
}
/**
* Change image trust requirement
*
@ -569,3 +579,33 @@ struct image * image_memory ( const char *name, userptr_t data, size_t len ) {
err_alloc_image:
return NULL;
}
/**
* Find argument within image command line
*
* @v image Image
* @v key Argument search key (including trailing delimiter)
* @ret value Argument value, or NULL if not found
*/
const char * image_argument ( struct image *image, const char *key ) {
const char *cmdline = image->cmdline;
const char *search;
const char *match;
const char *next;
/* Find argument */
for ( search = cmdline ; search ; search = next ) {
/* Find next occurrence, if any */
match = strstr ( search, key );
if ( ! match )
break;
next = ( match + strlen ( key ) );
/* Check preceding delimiter, if any */
if ( ( match == cmdline ) || isspace ( match[-1] ) )
return next;
}
return NULL;
}

View File

@ -25,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Form parameters
* Request parameters
*
*/
@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
static LIST_HEAD ( parameters );
/**
* Free form parameter list
* Free request parameter list
*
* @v refcnt Reference count
*/
@ -60,7 +60,7 @@ static void free_parameters ( struct refcnt *refcnt ) {
}
/**
* Find form parameter list by name
* Find request parameter list by name
*
* @v name Parameter list name (may be NULL)
* @ret params Parameter list, or NULL if not found
@ -78,7 +78,7 @@ struct parameters * find_parameters ( const char *name ) {
}
/**
* Create form parameter list
* Create request parameter list
*
* @v name Parameter list name (may be NULL)
* @ret params Parameter list, or NULL on failure
@ -118,15 +118,17 @@ struct parameters * create_parameters ( const char *name ) {
}
/**
* Add form parameter
* Add request parameter
*
* @v params Parameter list
* @v key Parameter key
* @v value Parameter value
* @v flags Parameter flags
* @ret param Parameter, or NULL on failure
*/
struct parameter * add_parameter ( struct parameters *params,
const char *key, const char *value ) {
const char *key, const char *value,
unsigned int flags ) {
struct parameter *param;
size_t key_len;
size_t value_len;
@ -147,11 +149,14 @@ struct parameter * add_parameter ( struct parameters *params,
param->key = key_copy;
strcpy ( value_copy, value );
param->value = value_copy;
param->flags = flags;
/* Add to list of parameters */
list_add_tail ( &param->list, &params->entries );
DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"\n",
params->name, param->key, param->value );
DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"%s%s\n",
params->name, param->key, param->value,
( ( param->flags & PARAMETER_FORM ) ? " (form)" : "" ),
( ( param->flags & PARAMETER_HEADER ) ? " (header)" : "" ) );
return param;
}

View File

@ -302,7 +302,7 @@ int parse_autovivified_setting ( char *text, struct named_setting *setting ) {
}
/**
* Parse form parameter list name
* Parse request parameter list name
*
* @v text Text
* @ret params Parameter list

View File

@ -51,59 +51,33 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
__einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" )
/**
* Calculate cutoff value for the repetition count test
* Initialise repetition count test
*
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Repetition Count Test defined in
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
* @v source Entropy source
*/
static inline __attribute__ (( always_inline )) unsigned int
repetition_count_cutoff ( void ) {
double max_repetitions;
unsigned int cutoff;
static void repetition_count_test_init ( struct entropy_source *source ) {
struct entropy_repetition_count_test *test =
&source->repetition_count_test;
/* The cutoff formula for the repetition test is:
*
* C = ( 1 + ( -log2(W) / H_min ) )
*
* where W is set at 2^(-30) (in ANS X9.82 Part 2 (October
* 2011 Draft) Section 8.5.2.1.3.1).
*/
max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) /
min_entropy_per_sample() ) );
/* Round up to a whole number of repetitions. We don't have
* the ceil() function available, so do the rounding by hand.
*/
cutoff = max_repetitions;
if ( cutoff < max_repetitions )
cutoff++;
linker_assert ( ( cutoff >= max_repetitions ), rounding_error );
/* Floating-point operations are not allowed in iPXE since we
* never set up a suitable environment. Abort the build
* unless the calculated number of repetitions is a
* compile-time constant.
*/
linker_assert ( __builtin_constant_p ( cutoff ),
repetition_count_cutoff_not_constant );
return cutoff;
/* Sanity checks */
assert ( test->repetition_count == 0 );
assert ( test->cutoff > 0 );
}
/**
* Perform repetition count test
*
* @v source Entropy source
* @v sample Noise sample
* @ret rc Return status code
*
* This is the Repetition Count Test defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 8.5.2.1.2.
*/
static int repetition_count_test ( noise_sample_t sample ) {
static noise_sample_t most_recent_sample;
static unsigned int repetition_count = 0;
static int repetition_count_test ( struct entropy_source *source,
noise_sample_t sample ) {
struct entropy_repetition_count_test *test =
&source->repetition_count_test;
/* A = the most recently seen sample value
* B = the number of times that value A has been seen in a row
@ -116,158 +90,71 @@ static int repetition_count_test ( noise_sample_t sample ) {
* the initial value of most_recent_sample is treated as being
* undefined.)
*/
if ( ( sample == most_recent_sample ) && ( repetition_count > 0 ) ) {
if ( ( sample == test->most_recent_sample ) &&
( test->repetition_count > 0 ) ) {
/* a) If the new sample = A, then B is incremented by one. */
repetition_count++;
test->repetition_count++;
/* i. If B >= C, then an error condition is raised
* due to a failure of the test
*/
if ( repetition_count >= repetition_count_cutoff() )
if ( test->repetition_count >= test->cutoff ) {
DBGC ( source, "ENTROPY %s excessively repeated "
"value %d (%d/%d)\n", source->name, sample,
test->repetition_count, test->cutoff );
return -EPIPE_REPETITION_COUNT_TEST;
}
} else {
/* b) Else:
* i. A = new sample
*/
most_recent_sample = sample;
test->most_recent_sample = sample;
/* ii. B = 1 */
repetition_count = 1;
test->repetition_count = 1;
}
return 0;
}
/**
* Window size for the adaptive proportion test
* Initialise adaptive proportion test
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows
* five possible window sizes: 16, 64, 256, 4096 and 65536.
*
* We expect to generate relatively few (<256) entropy samples during
* a typical iPXE run; the use of a large window size would mean that
* the test would never complete a single cycle. We use a window size
* of 64, which is the smallest window size that permits values of
* H_min down to one bit per sample.
* @v source Entropy source
*/
#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64
static void adaptive_proportion_test_init ( struct entropy_source *source ) {
struct entropy_adaptive_proportion_test *test =
&source->adaptive_proportion_test;
/**
* Combine adaptive proportion test window size and min-entropy
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret n_h (N,H) combined value
*/
#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) )
/* Sanity checks */
assert ( test->sample_count == 0 );
assert ( test->repetition_count == 0 );
assert ( test->cutoff > 0 );
/**
* Define a row of the adaptive proportion cutoff table
*
* @v h H (min-entropy)
* @v c16 Cutoff for N=16
* @v c64 Cutoff for N=64
* @v c256 Cutoff for N=256
* @v c4096 Cutoff for N=4096
* @v c65536 Cutoff for N=65536
*/
#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \
case APC_N_H ( 16, h ) : return c16; \
case APC_N_H ( 64, h ) : return c64; \
case APC_N_H ( 256, h ) : return c256; \
case APC_N_H ( 4096, h ) : return c4096; \
case APC_N_H ( 65536, h ) : return c65536;
/** Value used to represent "N/A" in adaptive proportion cutoff table */
#define APC_NA 0
/**
* Look up value in adaptive proportion test cutoff table
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret cutoff Cutoff
*
* This is the table of cutoff values defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) {
switch ( APC_N_H ( n, h ) ) {
APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 );
APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 );
APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 );
APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 );
APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 );
APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 );
APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 );
APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 );
APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 );
APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 );
APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 );
APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 );
APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 );
APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 );
APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 );
APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 );
APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 );
APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 );
APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 );
APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 );
default:
return APC_NA;
}
}
/**
* Calculate cutoff value for the adaptive proportion test
*
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Adaptive Proportion Test defined
* in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
adaptive_proportion_cutoff ( void ) {
unsigned int h;
unsigned int n;
unsigned int cutoff;
/* Look up cutoff value in cutoff table */
n = ADAPTIVE_PROPORTION_WINDOW_SIZE;
h = ( min_entropy_per_sample() / MIN_ENTROPY_SCALE );
cutoff = adaptive_proportion_cutoff_lookup ( n, h );
/* Fail unless cutoff value is a build-time constant */
linker_assert ( __builtin_constant_p ( cutoff ),
adaptive_proportion_cutoff_not_constant );
/* Fail if cutoff value is N/A */
linker_assert ( ( cutoff != APC_NA ),
adaptive_proportion_cutoff_not_applicable );
return cutoff;
/* Ensure that a new test run starts immediately */
test->sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE;
}
/**
* Perform adaptive proportion test
*
* @v source Entropy source
* @v sample Noise sample
* @ret rc Return status code
*
* This is the Adaptive Proportion Test for the Most Common Value
* defined in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.
*/
static int adaptive_proportion_test ( noise_sample_t sample ) {
static noise_sample_t current_counted_sample;
static unsigned int sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE;
static unsigned int repetition_count;
static int adaptive_proportion_test ( struct entropy_source *source,
noise_sample_t sample ) {
struct entropy_adaptive_proportion_test *test =
&source->adaptive_proportion_test;
/* A = the sample value currently being counted
* B = the number of samples examined in this run of the test so far
* S = the number of samples examined in this run of the test so far
* N = the total number of samples that must be observed in
* one run of the test, also known as the "window size" of
* the test
@ -284,37 +171,41 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
*/
/* 2. If S = N, then a new run of the test begins: */
if ( sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) {
if ( test->sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) {
/* a. A = the current sample */
current_counted_sample = sample;
test->current_counted_sample = sample;
/* b. S = 0 */
sample_count = 0;
test->sample_count = 0;
/* c. B = 0 */
repetition_count = 0;
test->repetition_count = 0;
} else {
/* Else: (the test is already running)
* a. S = S + 1
*/
sample_count++;
test->sample_count++;
/* b. If A = the current sample, then: */
if ( sample == current_counted_sample ) {
if ( sample == test->current_counted_sample ) {
/* i. B = B + 1 */
repetition_count++;
test->repetition_count++;
/* ii. If S (sic) > C then raise an error
* condition, because the test has
* detected a failure
*/
if ( repetition_count > adaptive_proportion_cutoff() )
if ( test->repetition_count > test->cutoff ) {
DBGC ( source, "ENTROPY %s excessively "
"repeated value %d (%d/%d)\n",
source->name, sample,
test->repetition_count, test->cutoff );
return -EPIPE_ADAPTIVE_PROPORTION_TEST;
}
}
}
@ -324,62 +215,180 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
/**
* Get entropy sample
*
* @v source Entropy source
* @ret entropy Entropy sample
* @ret rc Return status code
*
* This is the GetEntropy function defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 6.5.1.
*/
static int get_entropy ( entropy_sample_t *entropy ) {
static int rc = 0;
static int get_entropy ( struct entropy_source *source,
entropy_sample_t *entropy ) {
noise_sample_t noise;
int rc;
/* Any failure is permanent */
if ( rc != 0 )
return rc;
if ( ( rc = source->rc ) != 0 )
goto err_broken;
/* Get noise sample */
if ( ( rc = get_noise ( &noise ) ) != 0 )
return rc;
if ( ( rc = get_noise ( source, &noise ) ) != 0 )
goto err_get_noise;
/* Perform Repetition Count Test and Adaptive Proportion Test
* as mandated by ANS X9.82 Part 2 (October 2011 Draft)
* Section 8.5.2.1.1.
*/
if ( ( rc = repetition_count_test ( noise ) ) != 0 )
return rc;
if ( ( rc = adaptive_proportion_test ( noise ) ) != 0 )
return rc;
if ( ( rc = repetition_count_test ( source, noise ) ) != 0 )
goto err_repetition_count_test;
if ( ( rc = adaptive_proportion_test ( source, noise ) ) != 0 )
goto err_adaptive_proportion_test;
/* We do not use any optional conditioning component */
*entropy = noise;
return 0;
err_adaptive_proportion_test:
err_repetition_count_test:
err_get_noise:
source->rc = rc;
err_broken:
return rc;
}
/**
* Calculate number of samples required for startup tests
* Initialise startup test
*
* @ret num_samples Number of samples required
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
* that at least one full cycle of the continuous tests must be
* performed at start-up.
* @v source Entropy source
*/
static inline __attribute__ (( always_inline )) unsigned int
startup_test_count ( void ) {
unsigned int num_samples;
static void startup_test_init ( struct entropy_source *source ) {
struct entropy_startup_test *test = &source->startup_test;
/* At least max(N,C) samples shall be generated by the noise
* source for start-up testing.
*/
num_samples = repetition_count_cutoff();
if ( num_samples < adaptive_proportion_cutoff() )
num_samples = adaptive_proportion_cutoff();
linker_assert ( __builtin_constant_p ( num_samples ),
startup_test_count_not_constant );
/* Sanity check */
assert ( test->tested == 0 );
assert ( test->count > 0 );
}
return num_samples;
/**
* Perform startup test
*
* @v source Entropy source
* @ret rc Return status code
*/
static int startup_test ( struct entropy_source *source ) {
struct entropy_startup_test *test = &source->startup_test;
entropy_sample_t sample;
int rc;
/* Perform mandatory number of startup tests */
for ( ; test->tested < test->count ; test->tested++ ) {
if ( ( rc = get_entropy ( source, &sample ) ) != 0 ) {
DBGC ( source, "ENTROPY %s failed: %s\n",
source->name, strerror ( rc ) );
return rc;
}
}
return 0;
}
/**
* Enable entropy gathering
*
* @v source Entropy source
* @ret rc Return status code
*/
int entropy_enable ( struct entropy_source *source ) {
int rc;
/* Refuse to enable a previously failed source */
if ( ( rc = source->rc ) != 0 )
return rc;
/* Enable entropy source */
if ( ( rc = source->enable() ) != 0 ) {
DBGC ( source, "ENTROPY %s could not enable: %s\n",
source->name, strerror ( rc ) );
source->rc = rc;
return rc;
}
/* Sanity check */
assert ( source->min_entropy_per_sample > 0 );
/* Initialise test state if this source has not previously been used */
if ( source->startup_test.tested == 0 ) {
repetition_count_test_init ( source );
adaptive_proportion_test_init ( source );
startup_test_init ( source );
}
DBGC ( source, "ENTROPY %s enabled\n", source->name );
return 0;
}
/**
* Enable and test entropy source
*
* @v source Entropy source
* @ret rc Return status code
*/
static int entropy_enable_and_test ( struct entropy_source *source ) {
int rc;
/* Enable source */
if ( ( rc = entropy_enable ( source ) ) != 0 )
goto err_enable;
/* Test source */
if ( ( rc = startup_test ( source ) ) != 0 )
goto err_test;
DBGC ( source, "ENTROPY %s passed %d startup tests\n",
source->name, source->startup_test.count );
return 0;
err_test:
entropy_disable ( source );
err_enable:
assert ( source->rc == rc );
return rc;
}
/**
* Enable first working entropy source
*
* @v source Entropy source to fill in
* @ret rc Return status code
*/
static int entropy_enable_working ( struct entropy_source **source ) {
int rc;
/* Find the first working source */
rc = -ENOENT;
for_each_table_entry ( *source, ENTROPY_SOURCES ) {
if ( ( rc = entropy_enable_and_test ( *source ) ) == 0 )
return 0;
}
DBGC ( *source, "ENTROPY has no working sources: %s\n",
strerror ( rc ) );
return rc;
}
/**
* Disable entropy gathering
*
* @v source Entropy source
*/
void entropy_disable ( struct entropy_source *source ) {
/* Disable entropy gathering, if applicable */
if ( source->disable )
source->disable();
DBGC ( source, "ENTROPY %s disabled\n", source->name );
}
/**
@ -402,7 +411,7 @@ static uint32_t make_next_nonce ( void ) {
/**
* Obtain entropy input temporary buffer
*
* @v num_samples Number of entropy samples
* @v min_entropy Min-entropy required
* @v tmp Temporary buffer
* @v tmp_len Length of temporary buffer
* @ret rc Return status code
@ -412,47 +421,41 @@ static uint32_t make_next_nonce ( void ) {
* and condensing each entropy source output after each GetEntropy
* call) as defined in ANS X9.82 Part 4 (April 2011 Draft) Section
* 13.3.4.2.
*
* To minimise code size, the number of samples required is calculated
* at compilation time.
*/
int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
size_t tmp_len ) {
static unsigned int startup_tested = 0;
struct entropy_source *source;
struct {
uint32_t nonce;
entropy_sample_t sample;
} __attribute__ (( packed )) data;;
uint8_t df_buf[tmp_len];
min_entropy_t entropy_total;
unsigned int num_samples;
unsigned int i;
int rc;
/* Enable entropy gathering */
if ( ( rc = entropy_enable() ) != 0 )
return rc;
if ( ( rc = entropy_enable_working ( &source ) ) != 0 )
goto err_enable_working;
/* Perform mandatory startup tests, if not yet performed */
for ( ; startup_tested < startup_test_count() ; startup_tested++ ) {
if ( ( rc = get_entropy ( &data.sample ) ) != 0 )
goto err_get_entropy;
}
/* Sanity checks */
assert ( source->startup_test.count > 0 );
assert ( source->startup_test.tested >= source->startup_test.count );
/* 3. entropy_total = 0
*
* (Nothing to do; the number of entropy samples required has
* already been precalculated.)
*/
/* 3. entropy_total = 0 */
entropy_total = MIN_ENTROPY ( 0 );
/* 4. tmp = a fixed n-bit value, such as 0^n */
memset ( tmp, 0, tmp_len );
/* 5. While ( entropy_total < min_entropy ) */
while ( num_samples-- ) {
for ( num_samples = 0 ; entropy_total < min_entropy ; num_samples++ ) {
/* 5.1. ( status, entropy_bitstring, assessed_entropy )
* = GetEntropy()
* 5.2. If status indicates an error, return ( status, Null )
*/
if ( ( rc = get_entropy ( &data.sample ) ) != 0 )
if ( ( rc = get_entropy ( source, &data.sample ) ) != 0 )
goto err_get_entropy;
/* 5.3. nonce = MakeNextNonce() */
@ -466,19 +469,26 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
for ( i = 0 ; i < tmp_len ; i++ )
tmp[i] ^= df_buf[i];
/* 5.5. entropy_total = entropy_total + assessed_entropy
*
* (Nothing to do; the number of entropy samples
* required has already been precalculated.)
*/
/* 5.5. entropy_total = entropy_total + assessed_entropy */
entropy_total += source->min_entropy_per_sample;
}
/* Disable entropy gathering */
entropy_disable();
entropy_disable ( source );
DBGC ( source, "ENTROPY %s gathered %d bits in %d samples\n",
source->name, ( min_entropy / MIN_ENTROPY_SCALE ), num_samples );
return 0;
err_get_entropy:
entropy_disable();
entropy_disable ( source );
assert ( source->rc == rc );
err_enable_working:
return rc;
}
/* Drag in objects via entropy_enable */
REQUIRING_SYMBOL ( entropy_enable );
/* Drag in entropy configuration */
REQUIRE_OBJECT ( config_entropy );

View File

@ -609,6 +609,7 @@ static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd,
*/
static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
int rc ) {
struct scsi_device *scsidev = scsicmd->scsidev;
struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd );
struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16;
struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10;
@ -645,6 +646,9 @@ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
}
capacity.max_count = -1U;
/* Allow transport layer to update capacity */
block_capacity ( &scsidev->scsi, &capacity );
/* Return capacity to caller */
block_capacity ( &scsicmd->block, &capacity );

View File

@ -290,6 +290,18 @@ static int intel_reset ( struct intel_nic *intel ) {
pba, readl ( intel->regs + INTEL_PBA ) );
}
/* The Intel I210's packet buffer size registers reset only on
* power up. If an operating system changes these but then
* the computer recieves a reset signal without losing power,
* the registers will stay the same (but be incompatible with
* other register defaults), thus making the device unable to
* pass traffic.
*/
if ( intel->flags & INTEL_PBSIZE_RST ) {
writel ( INTEL_RXPBS_I210, intel->regs + INTEL_RXPBS );
writel ( INTEL_TXPBS_I210, intel->regs + INTEL_TXPBS );
}
/* Always reset MAC. Required to reset the TX and RX rings. */
writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL );
mdelay ( INTEL_RESET_DELAY_MS );
@ -1139,7 +1151,7 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ),
PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ),
PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ),
PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ),
PCI_ROM ( 0x8086, 0x1533, "i210", "I210", INTEL_PBSIZE_RST ),
PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ),
PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ),
PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ),
@ -1147,7 +1159,7 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", INTEL_NO_PHY_RST ),
PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ),
PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", INTEL_PBSIZE_RST ),
PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ),
PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ),
PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ),

View File

@ -138,6 +138,10 @@ struct intel_descriptor {
/** Packet Buffer Size */
#define INTEL_PBS 0x01008UL
/** Receive packet buffer size */
#define INTEL_RXPBS 0x02404UL
#define INTEL_RXPBS_I210 0x000000a2UL /**< I210 power-up default */
/** Receive Descriptor register block */
#define INTEL_RD 0x02800UL
@ -154,6 +158,10 @@ struct intel_descriptor {
/** Receive buffer length */
#define INTEL_RX_MAX_LEN 2048
/** Transmit packet buffer size */
#define INTEL_TXPBS 0x03404UL
#define INTEL_TXPBS_I210 0x04000014UL /**< I210 power-up default */
/** Transmit Descriptor register block */
#define INTEL_TD 0x03800UL
@ -319,6 +327,8 @@ enum intel_flags {
INTEL_NO_ASDE = 0x0008,
/** Reset may cause a complete device hang */
INTEL_RST_HANG = 0x0010,
/** PBSIZE registers must be explicitly reset */
INTEL_PBSIZE_RST = 0x0020,
};
/** The i219 has a seriously broken reset mechanism */

View File

@ -473,6 +473,7 @@ static struct pci_device_id intelx_nics[] = {
PCI_ROM ( 0x8086, 0x10f9, "82599-cx4", "82599 (CX4)", 0 ),
PCI_ROM ( 0x8086, 0x10fb, "82599-sfp", "82599 (SFI/SFP+)", 0 ),
PCI_ROM ( 0x8086, 0x10fc, "82599-xaui", "82599 (XAUI/BX4)", 0 ),
PCI_ROM ( 0x8086, 0x151c, "82599-tn", "82599 (TN)", 0 ),
PCI_ROM ( 0x8086, 0x1528, "x540t", "X540-AT2/X540-BT2", 0 ),
PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ),
PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ),

View File

@ -129,7 +129,7 @@ static int imgsingle_exec ( int argc, char **argv,
&image ) ) != 0 )
goto err_acquire;
} else {
image = image_find_selected();
image = find_image_tag ( &selected_image );
if ( ! image ) {
printf ( "No image selected\n" );
goto err_acquire;

View File

@ -25,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Form parameter commands
* Request parameter commands
*
*/
@ -90,12 +90,16 @@ static int params_exec ( int argc, char **argv ) {
struct param_options {
/** Parameter list name */
char *params;
/** Parameter is a header */
int header;
};
/** "param" option list */
static struct option_descriptor param_opts[] = {
OPTION_DESC ( "params", 'p', required_argument,
struct param_options, params, parse_string ),
OPTION_DESC ( "header", 'H', no_argument,
struct param_options, header, parse_flag ),
};
/** "param" command descriptor */
@ -114,6 +118,7 @@ static int param_exec ( int argc, char **argv ) {
struct param_options opts;
char *key;
char *value;
unsigned int flags;
struct parameters *params;
struct parameter *param;
int rc;
@ -132,12 +137,15 @@ static int param_exec ( int argc, char **argv ) {
goto err_parse_value;
}
/* Construct flags */
flags = ( opts.header ? PARAMETER_HEADER : PARAMETER_FORM );
/* Identify parameter list */
if ( ( rc = parse_parameters ( opts.params, &params ) ) != 0 )
goto err_parse_parameters;
/* Add parameter */
param = add_parameter ( params, key, value );
param = add_parameter ( params, key, value, flags );
if ( ! param ) {
rc = -ENOMEM;
goto err_add_parameter;
@ -154,7 +162,7 @@ static int param_exec ( int argc, char **argv ) {
return rc;
}
/** Form parameter commands */
/** Request parameter commands */
struct command param_commands[] __command = {
{
.name = "params",

117
src/hci/commands/shim_cmd.c Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <getopt.h>
#include <ipxe/command.h>
#include <ipxe/parseopt.h>
#include <ipxe/efi/efi_image.h>
#include <usr/imgmgmt.h>
#include <usr/shimmgmt.h>
/** @file
*
* EFI shim command
*
*/
/** "shim" options */
struct shim_options {
/** Download timeout */
unsigned long timeout;
/** Require third party loader */
int require_loader;
/** Allow PXE base code protocol */
int allow_pxe;
/** Allow SBAT variable access */
int allow_sbat;
};
/** "shim" option list */
static struct option_descriptor shim_opts[] = {
OPTION_DESC ( "timeout", 't', required_argument,
struct shim_options, timeout, parse_timeout ),
OPTION_DESC ( "require-loader", 'l', no_argument,
struct shim_options, require_loader, parse_flag ),
OPTION_DESC ( "allow-pxe", 'p', no_argument,
struct shim_options, allow_pxe, parse_flag ),
OPTION_DESC ( "allow-sbat", 's', no_argument,
struct shim_options, allow_sbat, parse_flag ),
};
/** "shim" command descriptor */
static struct command_descriptor shim_cmd =
COMMAND_DESC ( struct shim_options, shim_opts, 0, 1, NULL );
/**
* The "shim" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int shim_exec ( int argc, char **argv ) {
struct shim_options opts;
struct image *image = NULL;
struct image *kernel;
char *name_uri;
int download;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, &shim_cmd, &opts ) ) != 0 )
goto err_parse;
/* Decide whether or not to download images */
kernel = find_image_tag ( &selected_image );
download = ( ! ( kernel && efi_can_load ( kernel ) ) );
/* Parse name/URI string */
name_uri = argv[optind];
/* Acquire image, if applicable */
if ( download && name_uri &&
( ( rc = imgacquire ( name_uri, opts.timeout,
&image ) ) != 0 ) ) {
goto err_image;
}
/* (Un)register as shim */
if ( ( rc = shim ( image, opts.require_loader, opts.allow_pxe,
opts.allow_sbat ) ) != 0 )
goto err_shim;
err_shim:
err_image:
err_parse:
return rc;
}
/** Shim commands */
struct command shim_commands[] __command = {
{
.name = "shim",
.exec = shim_exec,
},
};

View File

@ -31,6 +31,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi_wrap.h>
#include <ipxe/efi/efi_pxe.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/efi_image.h>
#include <ipxe/efi/efi_shim.h>
#include <ipxe/image.h>
#include <ipxe/init.h>
#include <ipxe/features.h>
@ -109,18 +111,14 @@ efi_image_path ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
*/
static wchar_t * efi_image_cmdline ( struct image *image ) {
wchar_t *cmdline;
size_t len;
len = ( strlen ( image->name ) +
( image->cmdline ?
( 1 /* " " */ + strlen ( image->cmdline ) ) : 0 ) );
cmdline = zalloc ( ( len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
if ( ! cmdline )
/* Allocate and construct command line */
if ( efi_asprintf ( &cmdline, "%s%s%s", image->name,
( image->cmdline ? " " : "" ),
( image->cmdline ? image->cmdline : "" ) ) < 0 ) {
return NULL;
efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s",
image->name,
( image->cmdline ? " " : "" ),
( image->cmdline ? image->cmdline : "" ) );
}
return cmdline;
}
@ -138,47 +136,65 @@ static int efi_image_exec ( struct image *image ) {
EFI_LOADED_IMAGE_PROTOCOL *image;
void *interface;
} loaded;
struct image *shim;
struct image *exec;
EFI_HANDLE handle;
EFI_MEMORY_TYPE type;
wchar_t *cmdline;
unsigned int toggle;
EFI_STATUS efirc;
int rc;
/* Find an appropriate device handle to use */
snpdev = last_opened_snpdev();
if ( ! snpdev ) {
DBGC ( image, "EFIIMAGE %p could not identify SNP device\n",
image );
DBGC ( image, "EFIIMAGE %s could not identify SNP device\n",
image->name );
rc = -ENODEV;
goto err_no_snpdev;
}
/* Use shim instead of directly executing image if applicable */
shim = ( efi_can_load ( image ) ?
NULL : find_image_tag ( &efi_shim ) );
exec = ( shim ? shim : image );
if ( shim ) {
DBGC ( image, "EFIIMAGE %s executing via %s\n",
image->name, shim->name );
}
/* Re-register as a hidden image to allow for access via file I/O */
toggle = ( ~image->flags & IMAGE_HIDDEN );
image->flags |= IMAGE_HIDDEN;
if ( ( rc = register_image ( image ) ) != 0 )
goto err_register_image;
/* Install file I/O protocols */
if ( ( rc = efi_file_install ( snpdev->handle ) ) != 0 ) {
DBGC ( image, "EFIIMAGE %p could not install file protocol: "
"%s\n", image, strerror ( rc ) );
DBGC ( image, "EFIIMAGE %s could not install file protocol: "
"%s\n", image->name, strerror ( rc ) );
goto err_file_install;
}
/* Install PXE base code protocol */
if ( ( rc = efi_pxe_install ( snpdev->handle, snpdev->netdev ) ) != 0 ){
DBGC ( image, "EFIIMAGE %p could not install PXE protocol: "
"%s\n", image, strerror ( rc ) );
DBGC ( image, "EFIIMAGE %s could not install PXE protocol: "
"%s\n", image->name, strerror ( rc ) );
goto err_pxe_install;
}
/* Install iPXE download protocol */
if ( ( rc = efi_download_install ( snpdev->handle ) ) != 0 ) {
DBGC ( image, "EFIIMAGE %p could not install iPXE download "
"protocol: %s\n", image, strerror ( rc ) );
DBGC ( image, "EFIIMAGE %s could not install iPXE download "
"protocol: %s\n", image->name, strerror ( rc ) );
goto err_download_install;
}
/* Create device path for image */
path = efi_image_path ( image, snpdev->path );
path = efi_image_path ( exec, snpdev->path );
if ( ! path ) {
DBGC ( image, "EFIIMAGE %p could not create device path\n",
image );
DBGC ( image, "EFIIMAGE %s could not create device path\n",
image->name );
rc = -ENOMEM;
goto err_image_path;
}
@ -186,21 +202,30 @@ static int efi_image_exec ( struct image *image ) {
/* Create command line for image */
cmdline = efi_image_cmdline ( image );
if ( ! cmdline ) {
DBGC ( image, "EFIIMAGE %p could not create command line\n",
image );
DBGC ( image, "EFIIMAGE %s could not create command line\n",
image->name );
rc = -ENOMEM;
goto err_cmdline;
}
/* Install shim special handling if applicable */
if ( shim &&
( ( rc = efi_shim_install ( shim, snpdev->handle,
&cmdline ) ) != 0 ) ){
DBGC ( image, "EFIIMAGE %s could not install shim handling: "
"%s\n", image->name, strerror ( rc ) );
goto err_shim_install;
}
/* Attempt loading image */
handle = NULL;
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path,
user_to_virt ( image->data, 0 ),
image->len, &handle ) ) != 0 ) {
user_to_virt ( exec->data, 0 ),
exec->len, &handle ) ) != 0 ) {
/* Not an EFI image */
rc = -EEFI_LOAD ( efirc );
DBGC ( image, "EFIIMAGE %p could not load: %s\n",
image, strerror ( rc ) );
DBGC ( image, "EFIIMAGE %s could not load: %s\n",
image->name, strerror ( rc ) );
if ( efirc == EFI_SECURITY_VIOLATION ) {
goto err_load_image_security_violation;
} else {
@ -220,8 +245,8 @@ static int efi_image_exec ( struct image *image ) {
/* Some EFI 1.10 implementations seem not to fill in DeviceHandle */
if ( loaded.image->DeviceHandle == NULL ) {
DBGC ( image, "EFIIMAGE %p filling in missing DeviceHandle\n",
image );
DBGC ( image, "EFIIMAGE %s filling in missing DeviceHandle\n",
image->name );
loaded.image->DeviceHandle = snpdev->handle;
}
@ -251,14 +276,14 @@ static int efi_image_exec ( struct image *image ) {
/* Start the image */
if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) {
rc = -EEFI_START ( efirc );
DBGC ( image, "EFIIMAGE %p could not start (or returned with "
"error): %s\n", image, strerror ( rc ) );
DBGC ( image, "EFIIMAGE %s could not start (or returned with "
"error): %s\n", image->name, strerror ( rc ) );
goto err_start_image;
}
/* If image was a driver, connect it up to anything available */
if ( type == EfiBootServicesCode ) {
DBGC ( image, "EFIIMAGE %p connecting drivers\n", image );
DBGC ( image, "EFIIMAGE %s connecting drivers\n", image->name );
efi_driver_reconnect_all();
}
@ -286,6 +311,9 @@ static int efi_image_exec ( struct image *image ) {
if ( rc != 0 )
bs->UnloadImage ( handle );
err_load_image:
if ( shim )
efi_shim_uninstall();
err_shim_install:
free ( cmdline );
err_cmdline:
free ( path );
@ -296,6 +324,9 @@ static int efi_image_exec ( struct image *image ) {
err_pxe_install:
efi_file_uninstall ( snpdev->handle );
err_file_install:
unregister_image ( image );
err_register_image:
image->flags ^= toggle;
err_no_snpdev:
return rc;
}
@ -324,8 +355,8 @@ static int efi_image_probe ( struct image *image ) {
image->len, &handle ) ) != 0 ) {
/* Not an EFI image */
rc = -EEFI_LOAD ( efirc );
DBGC ( image, "EFIIMAGE %p could not load: %s\n",
image, strerror ( rc ) );
DBGC ( image, "EFIIMAGE %s could not load: %s\n",
image->name, strerror ( rc ) );
if ( efirc == EFI_SECURITY_VIOLATION ) {
goto err_load_image_security_violation;
} else {
@ -346,9 +377,75 @@ static int efi_image_probe ( struct image *image ) {
return rc;
}
/** EFI image type */
struct image_type efi_image_type __image_type ( PROBE_NORMAL ) = {
.name = "EFI",
.probe = efi_image_probe,
.exec = efi_image_exec,
/**
* Probe EFI PE image
*
* @v image EFI file
* @ret rc Return status code
*
* The extremely broken UEFI Secure Boot model provides no way for us
* to unambiguously determine that a valid EFI executable image was
* rejected by LoadImage() because it failed signature verification.
* We must therefore use heuristics to guess whether not an image that
* was rejected by LoadImage() could still be loaded via a separate PE
* loader such as the UEFI shim.
*/
static int efi_pe_image_probe ( struct image *image ) {
const UINT16 magic = ( ( sizeof ( UINTN ) == sizeof ( uint32_t ) ) ?
EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC :
EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC );
union {
EFI_IMAGE_DOS_HEADER dos;
EFI_IMAGE_OPTIONAL_HEADER_UNION pe;
} u;
/* Check for existence of DOS header */
if ( image->len < sizeof ( u.dos ) ) {
DBGC ( image, "EFIIMAGE %s too short for DOS header\n",
image->name );
return -ENOEXEC;
}
copy_from_user ( &u.dos, image->data, 0, sizeof ( u.dos ) );
if ( u.dos.e_magic != EFI_IMAGE_DOS_SIGNATURE ) {
DBGC ( image, "EFIIMAGE %s missing MZ signature\n",
image->name );
return -ENOEXEC;
}
/* Check for existence of PE header */
if ( ( image->len < u.dos.e_lfanew ) ||
( ( image->len - u.dos.e_lfanew ) < sizeof ( u.pe ) ) ) {
DBGC ( image, "EFIIMAGE %s too short for PE header\n",
image->name );
return -ENOEXEC;
}
copy_from_user ( &u.pe, image->data, u.dos.e_lfanew, sizeof ( u.pe ) );
if ( u.pe.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE ) {
DBGC ( image, "EFIIMAGE %s missing PE signature\n",
image->name );
return -ENOEXEC;
}
/* Check PE header magic */
if ( u.pe.Pe32.OptionalHeader.Magic != magic ) {
DBGC ( image, "EFIIMAGE %s incorrect magic %04x\n",
image->name, u.pe.Pe32.OptionalHeader.Magic );
return -ENOEXEC;
}
return 0;
}
/** EFI image types */
struct image_type efi_image_type[] __image_type ( PROBE_NORMAL ) = {
{
.name = "EFI",
.probe = efi_image_probe,
.exec = efi_image_exec,
},
{
.name = "EFIPE",
.probe = efi_pe_image_probe,
.exec = efi_image_exec,
},
};

View File

@ -197,11 +197,6 @@ static int script_exec ( struct image *image ) {
size_t saved_offset;
int rc;
/* Temporarily de-register image, so that a "boot" command
* doesn't throw us into an execution loop.
*/
unregister_image ( image );
/* Preserve state of any currently-running script */
saved_offset = script_offset;
@ -212,10 +207,6 @@ static int script_exec ( struct image *image ) {
/* Restore saved state */
script_offset = saved_offset;
/* Re-register image (unless we have been replaced) */
if ( ! image->replacement )
register_image ( image );
return rc;
}
@ -320,6 +311,7 @@ static int terminate_on_label_found ( int rc ) {
* @ret rc Return status code
*/
static int goto_exec ( int argc, char **argv ) {
struct image *image = current_image.image;
struct goto_options opts;
size_t saved_offset;
int rc;
@ -329,7 +321,7 @@ static int goto_exec ( int argc, char **argv ) {
return rc;
/* Sanity check */
if ( ! current_image ) {
if ( ! image ) {
rc = -ENOTTY;
printf ( "Not in a script: %s\n", strerror ( rc ) );
return rc;
@ -340,10 +332,10 @@ static int goto_exec ( int argc, char **argv ) {
/* Find label */
saved_offset = script_offset;
if ( ( rc = process_script ( current_image, goto_find_label,
if ( ( rc = process_script ( image, goto_find_label,
terminate_on_label_found ) ) != 0 ) {
script_offset = saved_offset;
DBGC ( current_image, "[%04zx] No such label :%s\n",
DBGC ( image, "[%04zx] No such label :%s\n",
script_offset, goto_label );
return rc;
}

View File

@ -0,0 +1,31 @@
#ifndef _IPXE_EFI_SHIM_LOCK_PROTOCOL_H
#define _IPXE_EFI_SHIM_LOCK_PROTOCOL_H
/** @file
*
* EFI "shim lock" protocol
*
*/
FILE_LICENCE ( BSD3 );
#define EFI_SHIM_LOCK_PROTOCOL_GUID \
{ 0x605dab50, 0xe046, 0x4300, \
{ 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }
#define SHIMAPI __asmcall
typedef
EFI_STATUS SHIMAPI
(*EFI_SHIM_LOCK_VERIFY) (
IN VOID *buffer,
IN UINT32 size
);
typedef struct _EFI_SHIM_LOCK_PROTOCOL {
EFI_SHIM_LOCK_VERIFY Verify;
VOID *Reserved1;
VOID *Reserved2;
} EFI_SHIM_LOCK_PROTOCOL;
#endif /*_IPXE_EFI_SHIM_LOCK_PROTOCOL_H */

View File

@ -197,6 +197,7 @@ extern EFI_GUID efi_pci_io_protocol_guid;
extern EFI_GUID efi_pci_root_bridge_io_protocol_guid;
extern EFI_GUID efi_pxe_base_code_protocol_guid;
extern EFI_GUID efi_serial_io_protocol_guid;
extern EFI_GUID efi_shim_lock_protocol_guid;
extern EFI_GUID efi_simple_file_system_protocol_guid;
extern EFI_GUID efi_simple_network_protocol_guid;
extern EFI_GUID efi_simple_pointer_protocol_guid;

View File

@ -1,35 +0,0 @@
#ifndef _IPXE_EFI_ENTROPY_H
#define _IPXE_EFI_ENTROPY_H
/** @file
*
* EFI entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#ifdef ENTROPY_EFI
#define ENTROPY_PREFIX_efi
#else
#define ENTROPY_PREFIX_efi __efi_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) {
/* We use essentially the same mechanism as for the BIOS
* RTC-based entropy source, and so assume the same
* min-entropy per sample.
*/
return MIN_ENTROPY ( 1.3 );
}
#endif /* _IPXE_EFI_ENTROPY_H */

View File

@ -0,0 +1,27 @@
#ifndef _IPXE_EFI_IMAGE_H
#define _IPXE_EFI_IMAGE_H
/** @file
*
* EFI images
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/image.h>
extern struct image_type efi_image_type[] __image_type ( PROBE_NORMAL );
/**
* Check if EFI image can be loaded directly
*
* @v image EFI image
* @ret can_load EFI image can be loaded directly
*/
static inline int efi_can_load ( struct image *image ) {
return ( image->type == efi_image_type );
}
#endif /* _IPXE_EFI_IMAGE_H */

View File

@ -0,0 +1,24 @@
#ifndef _IPXE_EFI_SHIM_H
#define _IPXE_EFI_SHIM_H
/** @file
*
* UEFI shim special handling
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/image.h>
#include <ipxe/efi/efi.h>
extern int efi_shim_require_loader;
extern int efi_shim_allow_pxe;
extern int efi_shim_allow_sbat;
extern struct image_tag efi_shim __image_tag;
extern int efi_shim_install ( struct image *shim, EFI_HANDLE handle,
wchar_t **cmdline );
extern void efi_shim_uninstall ( void );
#endif /* _IPXE_EFI_SHIM_H */

View File

@ -19,6 +19,8 @@ extern int efi_vssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt,
va_list args );
extern int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize,
const char *fmt, ... );
extern int efi_vasprintf ( wchar_t **strp, const char *fmt, va_list args );
extern int efi_asprintf ( wchar_t **strp, const char *fmt, ... );
/**
* Write a formatted string to a wide-character buffer

View File

@ -12,40 +12,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <ipxe/api.h>
#include <ipxe/hash_df.h>
#include <ipxe/sha256.h>
#include <ipxe/tables.h>
#include <config/entropy.h>
/**
* Calculate static inline entropy API function name
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @ret _subsys_func Subsystem API function
*/
#define ENTROPY_INLINE( _subsys, _api_func ) \
SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func )
/**
* Provide a entropy API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @v _func Implementing function
*/
#define PROVIDE_ENTROPY( _subsys, _api_func, _func ) \
PROVIDE_SINGLE_API ( ENTROPY_PREFIX_ ## _subsys, _api_func, _func )
/**
* Provide a static inline entropy API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
*/
#define PROVIDE_ENTROPY_INLINE( _subsys, _api_func ) \
PROVIDE_SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func )
/** A noise sample */
typedef uint8_t noise_sample_t;
@ -71,56 +42,148 @@ typedef unsigned int min_entropy_t;
#define MIN_ENTROPY( bits ) \
( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) )
/* Include all architecture-independent entropy API headers */
#include <ipxe/null_entropy.h>
#include <ipxe/efi/efi_entropy.h>
#include <ipxe/linux/linux_entropy.h>
/* Include all architecture-dependent entropy API headers */
#include <bits/entropy.h>
/**
* Repetition count test state
*
* This is the state for the repetition Count Test defined in ANS
* X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
*/
struct entropy_repetition_count_test {
/**
* A = the most recently seen sample value
*/
noise_sample_t most_recent_sample;
/**
* B = the number of times that value A has been seen in a row
*/
unsigned int repetition_count;
/**
* C = the cutoff value above which the repetition test should fail
*
* Filled in by entropy_init().
*/
unsigned int cutoff;
};
/**
* Enable entropy gathering
* Adaptive proportion test state
*
* @ret rc Return status code
* This is the state for the Adaptive Proportion Test for the Most
* Common Value defined in ANS X9.82 Part 2 (October 2011 Draft)
* Section 8.5.2.1.3.
*/
int entropy_enable ( void );
struct entropy_adaptive_proportion_test {
/**
* A = the sample value currently being counted
*/
noise_sample_t current_counted_sample;
/**
* S = the number of samples examined in this run of the test so far
*/
unsigned int sample_count;
/**
* B = the current number of times that S (sic) has been seen
* in the W (sic) samples examined so far
*/
unsigned int repetition_count;
/**
* C = the cutoff value above which the repetition test should fail
*
* Filled in by entropy_init().
*/
unsigned int cutoff;
};
/**
* Disable entropy gathering
* Startup test state
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
* that at least one full cycle of the continuous tests must be
* performed at start-up.
*/
void entropy_disable ( void );
struct entropy_startup_test {
/** Number of startup tests performed */
unsigned int tested;
/**
* Number of startup tests required for one full cycle
*
* Filled in by entropy_init().
*/
unsigned int count;
};
/**
* min-entropy per sample
/** An entropy source */
struct entropy_source {
/** Name */
const char *name;
/**
* min-entropy per sample
*
* min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in
* NIST SP 800-90 Appendix C.3 as
*
* H_min = -log2 ( p_max )
*
* where p_max is the probability of the most likely sample value.
*
* Filled in by entropy_init().
*/
min_entropy_t min_entropy_per_sample;
/** Repetition count test state */
struct entropy_repetition_count_test repetition_count_test;
/** Adaptive proportion test state */
struct entropy_adaptive_proportion_test adaptive_proportion_test;
/** Startup test state */
struct entropy_startup_test startup_test;
/**
* Failure status (if any)
*
* Any failure of an entropy source is regarded as permanent.
*/
int rc;
/**
* Enable entropy gathering
*
* @ret rc Return status code
*/
int ( * enable ) ( void );
/**
* Disable entropy gathering
*
*/
void ( * disable ) ( void );
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*
* This is the GetNoise function defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 6.5.2.
*/
int ( * get_noise ) ( noise_sample_t *noise );
};
/** Entropy source table */
#define ENTROPY_SOURCES __table ( struct entropy_source, "entropy_sources" )
/** Declare an entropy source */
#define __entropy_source( order ) __table_entry ( ENTROPY_SOURCES, order )
/** @defgroup entropy_source_order Entropy source order
*
* @ret min_entropy min-entropy of each sample
*
* min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in
* NIST SP 800-90 Appendix C.3 as
*
* H_min = -log2 ( p_max )
*
* where p_max is the probability of the most likely sample value.
*
* This must be a compile-time constant.
* @{
*/
min_entropy_t min_entropy_per_sample ( void );
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*
* This is the GetNoise function defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 6.5.2.
*/
int get_noise ( noise_sample_t *noise );
#define ENTROPY_PREFERRED 01 /**< Preferred entropy source */
#define ENTROPY_NORMAL 02 /**< Normal entropy source */
#define ENTROPY_FALLBACK 03 /**< Fallback entropy source */
extern int get_entropy_input_tmp ( unsigned int num_samples,
uint8_t *tmp, size_t tmp_len );
/** @} */
extern int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
size_t tmp_len );
/** Use SHA-256 as the underlying hash algorithm for Hash_df
*
@ -131,6 +194,22 @@ extern int get_entropy_input_tmp ( unsigned int num_samples,
/** Underlying hash algorithm output length (in bytes) */
#define ENTROPY_HASH_DF_OUTLEN_BYTES SHA256_DIGEST_SIZE
/**
* Get noise sample
*
* @v source Entropy source
* @ret noise Noise sample
* @ret rc Return status code
*
* This is the GetNoise function defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 6.5.2.
*/
static inline __attribute__ (( always_inline )) int
get_noise ( struct entropy_source *source, noise_sample_t *noise ) {
return source->get_noise ( noise );
}
/**
* Obtain entropy input
*
@ -145,8 +224,8 @@ extern int get_entropy_input_tmp ( unsigned int num_samples,
* each entropy source output after each GetEntropy call) as defined
* in ANS X9.82 Part 4 (April 2011 Draft) Section 13.3.4.2.
*
* To minimise code size, the number of samples required is calculated
* at compilation time.
* This function is inlined since the entropy amount and length inputs
* are always compile-time constants.
*/
static inline __attribute__ (( always_inline )) int
get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
@ -154,41 +233,16 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
size_t tmp_len = ( ( ( min_entropy_bits * 2 ) + 7 ) / 8 );
uint8_t tmp_buf[ tmp_len ];
uint8_t *tmp = ( ( tmp_len > max_len ) ? tmp_buf : data );
double min_samples;
unsigned int num_samples;
unsigned int n;
int rc;
/* Sanity checks */
linker_assert ( ( min_entropy_per_sample() <=
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ),
min_entropy_per_sample_is_impossibly_high );
/* Sanity check */
linker_assert ( ( min_entropy_bits <= ( 8 * max_len ) ),
entropy_buffer_too_small );
/* Round up minimum entropy to an integral number of bytes */
min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 );
/* Calculate number of samples required to contain sufficient entropy */
min_samples = ( MIN_ENTROPY ( min_entropy_bits ) /
min_entropy_per_sample() );
/* Round up to a whole number of samples. We don't have the
* ceil() function available, so do the rounding by hand.
*/
num_samples = min_samples;
if ( num_samples < min_samples )
num_samples++;
linker_assert ( ( num_samples >= min_samples ), rounding_error );
/* Floating-point operations are not allowed in iPXE since we
* never set up a suitable environment. Abort the build
* unless the calculated number of samples is a compile-time
* constant.
*/
linker_assert ( __builtin_constant_p ( num_samples ),
num_samples_not_constant );
/* (Unnumbered). The output length of the hash function shall
* meet or exceed the security strength indicated by the
* min_entropy parameter.
@ -218,8 +272,10 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
linker_assert ( __builtin_constant_p ( tmp_len ),
tmp_len_not_constant );
linker_assert ( ( n == ( 8 * tmp_len ) ), tmp_len_mismatch );
if ( ( rc = get_entropy_input_tmp ( num_samples, tmp, tmp_len ) ) != 0 )
if ( ( rc = get_entropy_input_tmp ( MIN_ENTROPY ( min_entropy_bits ),
tmp, tmp_len ) ) != 0 ) {
return rc;
}
/* 6. If ( n < min_length ), then tmp = tmp || 0^(min_length-n)
* 7. If ( n > max_length ), then tmp = df ( tmp, max_length )
@ -242,4 +298,231 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
}
}
/**
* Calculate cutoff value for the repetition count test
*
* @v min_entropy_per_sample Min-entropy per sample
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Repetition Count Test defined in
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_repetition_count_cutoff ( min_entropy_t min_entropy_per_sample ) {
double max_repetitions;
unsigned int cutoff;
/* The cutoff formula for the repetition test is:
*
* C = ( 1 + ( -log2(W) / H_min ) )
*
* where W is set at 2^(-30) (in ANS X9.82 Part 2 (October
* 2011 Draft) Section 8.5.2.1.3.1).
*/
max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) /
min_entropy_per_sample ) );
/* Round up to a whole number of repetitions. We don't have
* the ceil() function available, so do the rounding by hand.
*/
cutoff = max_repetitions;
if ( cutoff < max_repetitions )
cutoff++;
linker_assert ( ( cutoff >= max_repetitions ), rounding_error );
/* Floating-point operations are not allowed in iPXE since we
* never set up a suitable environment. Abort the build
* unless the calculated number of repetitions is a
* compile-time constant.
*/
linker_assert ( __builtin_constant_p ( cutoff ),
repetition_count_cutoff_not_constant );
return cutoff;
}
/**
* Window size for the adaptive proportion test
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows
* five possible window sizes: 16, 64, 256, 4096 and 65536.
*
* We expect to generate relatively few (<256) entropy samples during
* a typical iPXE run; the use of a large window size would mean that
* the test would never complete a single cycle. We use a window size
* of 64, which is the smallest window size that permits values of
* H_min down to one bit per sample.
*/
#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64
/**
* Combine adaptive proportion test window size and min-entropy
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret n_h (N,H) combined value
*/
#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) )
/**
* Define a row of the adaptive proportion cutoff table
*
* @v h H (min-entropy)
* @v c16 Cutoff for N=16
* @v c64 Cutoff for N=64
* @v c256 Cutoff for N=256
* @v c4096 Cutoff for N=4096
* @v c65536 Cutoff for N=65536
*/
#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \
case APC_N_H ( 16, h ) : return c16; \
case APC_N_H ( 64, h ) : return c64; \
case APC_N_H ( 256, h ) : return c256; \
case APC_N_H ( 4096, h ) : return c4096; \
case APC_N_H ( 65536, h ) : return c65536;
/** Value used to represent "N/A" in adaptive proportion cutoff table */
#define APC_NA 0
/**
* Look up value in adaptive proportion test cutoff table
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret cutoff Cutoff
*
* This is the table of cutoff values defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) {
switch ( APC_N_H ( n, h ) ) {
APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 );
APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 );
APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 );
APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 );
APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 );
APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 );
APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 );
APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 );
APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 );
APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 );
APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 );
APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 );
APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 );
APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 );
APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 );
APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 );
APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 );
APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 );
APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 );
APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 );
default:
return APC_NA;
}
}
/**
* Calculate cutoff value for the adaptive proportion test
*
* @v min_entropy_per_sample Min-entropy per sample
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Adaptive Proportion Test defined
* in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_adaptive_proportion_cutoff ( min_entropy_t min_entropy_per_sample ) {
unsigned int h;
unsigned int n;
unsigned int cutoff;
/* Look up cutoff value in cutoff table */
n = ADAPTIVE_PROPORTION_WINDOW_SIZE;
h = ( min_entropy_per_sample / MIN_ENTROPY_SCALE );
cutoff = entropy_adaptive_proportion_cutoff_lookup ( n, h );
/* Fail unless cutoff value is a compile-time constant */
linker_assert ( __builtin_constant_p ( cutoff ),
adaptive_proportion_cutoff_not_constant );
/* Fail if cutoff value is N/A */
linker_assert ( ( cutoff != APC_NA ),
adaptive_proportion_cutoff_not_applicable );
return cutoff;
}
/**
* Calculate number of samples required for startup tests
*
* @v repetition_count_cutoff Repetition count test cutoff value
* @v adaptive_proportion_cutoff Adaptive proportion test cutoff value
* @ret num_samples Number of samples required
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
* that at least one full cycle of the continuous tests must be
* performed at start-up.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_startup_test_count ( unsigned int repetition_count_cutoff,
unsigned int adaptive_proportion_cutoff ) {
unsigned int num_samples;
/* At least max(N,C) samples shall be generated by the noise
* source for start-up testing.
*/
num_samples = repetition_count_cutoff;
if ( num_samples < adaptive_proportion_cutoff )
num_samples = adaptive_proportion_cutoff;
linker_assert ( __builtin_constant_p ( num_samples ),
startup_test_count_not_constant );
return num_samples;
}
/**
* Initialise entropy source
*
* @v source Entropy source
* @v min_entropy_per_sample Min-entropy per sample
*
* The cutoff value calculations for the repetition count test and the
* adaptive proportion test are provided as static inline functions
* since the results will always be compile-time constants.
*/
static inline __attribute__ (( always_inline )) void
entropy_init ( struct entropy_source *source,
min_entropy_t min_entropy_per_sample ) {
unsigned int repetition_count_cutoff;
unsigned int adaptive_proportion_cutoff;
unsigned int startup_test_count;
/* Sanity check */
linker_assert ( min_entropy_per_sample > MIN_ENTROPY ( 0 ),
min_entropy_per_sample_is_zero );
linker_assert ( ( min_entropy_per_sample <=
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ),
min_entropy_per_sample_is_impossibly_high );
/* Calculate test cutoff values */
repetition_count_cutoff =
entropy_repetition_count_cutoff ( min_entropy_per_sample );
adaptive_proportion_cutoff =
entropy_adaptive_proportion_cutoff ( min_entropy_per_sample );
startup_test_count =
entropy_startup_test_count ( repetition_count_cutoff,
adaptive_proportion_cutoff );
/* Record min-entropy per sample and test cutoff values */
source->min_entropy_per_sample = min_entropy_per_sample;
source->repetition_count_test.cutoff = repetition_count_cutoff;
source->adaptive_proportion_test.cutoff = adaptive_proportion_cutoff;
source->startup_test.count = startup_test_count;
}
extern int entropy_enable ( struct entropy_source *source );
extern void entropy_disable ( struct entropy_source *source );
extern int get_noise ( struct entropy_source *source, noise_sample_t *noise );
#endif /* _IPXE_ENTROPY_H */

View File

@ -78,6 +78,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_dma ( ERRFILE_CORE | 0x00260000 )
#define ERRFILE_cachedhcp ( ERRFILE_CORE | 0x00270000 )
#define ERRFILE_acpimac ( ERRFILE_CORE | 0x00280000 )
#define ERRFILE_efi_strings ( ERRFILE_CORE | 0x00290000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
@ -403,6 +404,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_pci_cmd ( ERRFILE_OTHER | 0x00590000 )
#define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 )
#define ERRFILE_efi_cmdline ( ERRFILE_OTHER | 0x005b0000 )
#define ERRFILE_efi_rng ( ERRFILE_OTHER | 0x005c0000 )
#define ERRFILE_efi_shim ( ERRFILE_OTHER | 0x005d0000 )
/** @} */

View File

@ -61,16 +61,16 @@ struct image {
};
/** Image is registered */
#define IMAGE_REGISTERED 0x00001
/** Image is selected for execution */
#define IMAGE_SELECTED 0x0002
#define IMAGE_REGISTERED 0x0001
/** Image is trusted */
#define IMAGE_TRUSTED 0x0004
#define IMAGE_TRUSTED 0x0002
/** Image will be automatically unregistered after execution */
#define IMAGE_AUTO_UNREGISTER 0x0008
#define IMAGE_AUTO_UNREGISTER 0x0004
/** Image will be hidden from enumeration */
#define IMAGE_HIDDEN 0x0008
/** An executable image type */
struct image_type {
@ -150,8 +150,23 @@ struct image_type {
/** An executable image type */
#define __image_type( probe_order ) __table_entry ( IMAGE_TYPES, probe_order )
/** An image tag */
struct image_tag {
/** Name */
const char *name;
/** Image (weak reference, nullified when image is freed) */
struct image *image;
};
/** Image tag table */
#define IMAGE_TAGS __table ( struct image_tag, "image_tags" )
/** An image tag */
#define __image_tag __table_entry ( IMAGE_TAGS, 01 )
extern struct list_head images;
extern struct image *current_image;
extern struct image_tag current_image;
extern struct image_tag selected_image;
/** Iterate over all registered images */
#define for_each_image( image ) \
@ -161,15 +176,6 @@ extern struct image *current_image;
#define for_each_image_safe( image, tmp ) \
list_for_each_entry_safe ( (image), (tmp), &images, list )
/**
* Test for existence of images
*
* @ret existence Some images exist
*/
static inline int have_images ( void ) {
return ( ! list_empty ( &images ) );
}
/**
* Retrieve first image
*
@ -187,14 +193,15 @@ extern int image_set_len ( struct image *image, size_t len );
extern int image_set_data ( struct image *image, userptr_t data, size_t len );
extern int register_image ( struct image *image );
extern void unregister_image ( struct image *image );
struct image * find_image ( const char *name );
extern struct image * find_image ( const char *name );
extern struct image * find_image_tag ( struct image_tag *tag );
extern int image_exec ( struct image *image );
extern int image_replace ( struct image *replacement );
extern int image_select ( struct image *image );
extern struct image * image_find_selected ( void );
extern int image_set_trust ( int require_trusted, int permanent );
extern struct image * image_memory ( const char *name, userptr_t data,
size_t len );
extern const char * image_argument ( struct image *image, const char *key );
extern int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf );
extern int image_asn1 ( struct image *image, size_t offset,
struct asn1_cursor **cursor );
@ -249,4 +256,28 @@ static inline void image_untrust ( struct image *image ) {
image->flags &= ~IMAGE_TRUSTED;
}
/**
* Mark image as hidden
*
* @v image Image
*/
static inline void image_hide ( struct image *image ) {
image->flags |= IMAGE_HIDDEN;
}
/**
* Tag image
*
* @v image Image
* @v tag Image tag
* @ret prev Previous tagged image (if any)
*/
static inline struct image * image_tag ( struct image *image,
struct image_tag *tag ) {
struct image *prev = tag->image;
tag->image = image;
return prev;
}
#endif /* _IPXE_IMAGE_H */

View File

@ -22,6 +22,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Default iSCSI port */
#define ISCSI_PORT 3260
/** Default iSCSI first burst length */
#define ISCSI_FIRST_BURST_LEN 65536
/** Default iSCSI maximum burst length */
#define ISCSI_MAX_BURST_LEN 262144
/** Default iSCSI maximum receive data segment length */
#define ISCSI_MAX_RECV_DATA_SEG_LEN 8192
/**
* iSCSI segment lengths
*
@ -577,6 +586,9 @@ struct iscsi_session {
/** CHAP response (used for both initiator and target auth) */
struct chap_response chap;
/** Maximum burst length */
size_t max_burst_len;
/** Initiator session ID (IANA format) qualifier
*
* This is part of the ISID. It is generated randomly

View File

@ -1,34 +0,0 @@
#ifndef _IPXE_LINUX_ENTROPY_H
#define _IPXE_LINUX_ENTROPY_H
/** @file
*
* /dev/random-based entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef ENTROPY_LINUX
#define ENTROPY_PREFIX_linux
#else
#define ENTROPY_PREFIX_linux __linux_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) {
/* linux_get_noise() reads a single byte from /dev/random,
* which is supposed to block until a sufficient amount of
* entropy is available. We therefore assume that each sample
* contains exactly 8 bits of entropy.
*/
return MIN_ENTROPY ( 8.0 );
}
#endif /* _IPXE_LINUX_ENTROPY_H */

View File

@ -1,52 +0,0 @@
#ifndef _IPXE_NULL_ENTROPY_H
#define _IPXE_NULL_ENTROPY_H
/** @file
*
* Nonexistent entropy source
*
* This source provides no entropy and must NOT be used in a
* security-sensitive environment.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#ifdef ENTROPY_NULL
#define ENTROPY_PREFIX_null
#else
#define ENTROPY_PREFIX_null __null_
#endif
static inline __always_inline int
ENTROPY_INLINE ( null, entropy_enable ) ( void ) {
/* Do nothing */
return 0;
}
static inline __always_inline void
ENTROPY_INLINE ( null, entropy_disable ) ( void ) {
/* Do nothing */
}
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( null, min_entropy_per_sample ) ( void ) {
/* Actual amount of min-entropy is zero. To avoid
* division-by-zero errors and to allow compilation of
* entropy-consuming code, pretend to have 1 bit of entropy in
* each sample.
*/
return MIN_ENTROPY ( 1.0 );
}
static inline __always_inline int
ENTROPY_INLINE ( null, get_noise ) ( noise_sample_t *noise ) {
/* All sample values are constant */
*noise = 0x01;
return 0;
}
#endif /* _IPXE_NULL_ENTROPY_H */

View File

@ -3,7 +3,7 @@
/** @file
*
* Form parameters
* Request parameters
*
*/
@ -12,7 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/list.h>
#include <ipxe/refcnt.h>
/** A form parameter list */
/** A request parameter list */
struct parameters {
/** Reference count */
struct refcnt refcnt;
@ -24,18 +24,26 @@ struct parameters {
struct list_head entries;
};
/** A form parameter */
/** A request parameter */
struct parameter {
/** List of form parameters */
/** List of request parameters */
struct list_head list;
/** Key */
const char *key;
/** Value */
const char *value;
/** Flags */
unsigned int flags;
};
/** Request parameter is a form parameter */
#define PARAMETER_FORM 0x0001
/** Request parameter is a header parameter */
#define PARAMETER_HEADER 0x0002
/**
* Increment form parameter list reference count
* Increment request parameter list reference count
*
* @v params Parameter list, or NULL
* @ret params Parameter list as passed in
@ -47,7 +55,7 @@ params_get ( struct parameters *params ) {
}
/**
* Decrement form parameter list reference count
* Decrement request parameter list reference count
*
* @v params Parameter list, or NULL
*/
@ -57,7 +65,7 @@ params_put ( struct parameters *params ) {
}
/**
* Claim ownership of form parameter list
* Claim ownership of request parameter list
*
* @v params Parameter list
* @ret params Parameter list
@ -71,13 +79,14 @@ claim_parameters ( struct parameters *params ) {
return params;
}
/** Iterate over all form parameters in a list */
/** Iterate over all request parameters in a list */
#define for_each_param( param, params ) \
list_for_each_entry ( (param), &(params)->entries, list )
extern struct parameters * find_parameters ( const char *name );
extern struct parameters * create_parameters ( const char *name );
extern struct parameter * add_parameter ( struct parameters *params,
const char *key, const char *value );
const char *key, const char *value,
unsigned int flags );
#endif /* _IPXE_PARAMS_H */

View File

@ -383,9 +383,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
#define for_each_table_entry( pointer, table ) \
for ( pointer = table_start ( table ) ; \
pointer < table_end ( table ) ; \
pointer++ )
for ( (pointer) = table_start ( table ) ; \
(pointer) < table_end ( table ) ; \
(pointer)++ )
/**
* Iterate through all remaining entries within a linker table
@ -412,9 +412,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
#define for_each_table_entry_continue( pointer, table ) \
for ( pointer++ ; \
pointer < table_end ( table ) ; \
pointer++ )
for ( (pointer)++ ; \
(pointer) < table_end ( table ) ; \
(pointer)++ )
/**
* Iterate through all entries within a linker table in reverse order
@ -438,9 +438,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
#define for_each_table_entry_reverse( pointer, table ) \
for ( pointer = ( table_end ( table ) - 1 ) ; \
pointer >= table_start ( table ) ; \
pointer-- )
for ( (pointer) = ( table_end ( table ) - 1 ) ; \
(pointer) >= table_start ( table ) ; \
(pointer)-- )
/**
* Iterate through all remaining entries within a linker table in reverse order
@ -467,8 +467,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
#define for_each_table_entry_continue_reverse( pointer, table ) \
for ( pointer-- ; \
pointer >= table_start ( table ) ; \
pointer-- )
for ( (pointer)-- ; \
(pointer) >= table_start ( table ) ; \
(pointer)-- )
#endif /* _IPXE_TABLES_H */

View File

@ -52,6 +52,9 @@ struct tls_header {
/** Change cipher content type */
#define TLS_TYPE_CHANGE_CIPHER 20
/** Change cipher spec magic byte */
#define TLS_CHANGE_CIPHER_SPEC 1
/** Alert content type */
#define TLS_TYPE_ALERT 21
@ -395,6 +398,8 @@ struct tls_connection {
struct io_buffer rx_header_iobuf;
/** List of received data buffers */
struct list_head rx_data;
/** Received handshake fragment */
struct io_buffer *rx_handshake;
};
/** RX I/O buffer size

View File

@ -84,7 +84,7 @@ struct uri {
const char *equery;
/** Fragment (with original URI encoding) */
const char *efragment;
/** Form parameters */
/** Request parameters */
struct parameters *params;
} __attribute__ (( packed ));

View File

@ -0,0 +1,17 @@
#ifndef _USR_SHIMMGMT_H
#define _USR_SHIMMGMT_H
/** @file
*
* EFI shim management
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/image.h>
extern int shim ( struct image *image, int require_loader, int allow_pxe,
int allow_sbat );
#endif /* _USR_SHIMMGMT_H */

View File

@ -143,6 +143,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = {
"PxeBaseCode" },
{ &efi_serial_io_protocol_guid,
"SerialIo" },
{ &efi_shim_lock_protocol_guid,
"ShimLock" },
{ &efi_simple_file_system_protocol_guid,
"SimpleFileSystem" },
{ &efi_simple_network_protocol_guid,

View File

@ -25,10 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/entropy.h>
#include <ipxe/crc32.h>
#include <ipxe/profile.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/Rng.h>
/** @file
*
@ -36,22 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
/** Random number generator protocol */
static EFI_RNG_PROTOCOL *efirng;
EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );
/** Minimum number of bytes to request from RNG
*
* The UEFI spec states (for no apparently good reason) that "When a
* Deterministic Random Bit Generator (DRBG) is used on the output of
* a (raw) entropy source, its security level must be at least 256
* bits." The EDK2 codebase (mis)interprets this to mean that the
* call to GetRNG() should fail if given a buffer less than 32 bytes.
*
* Incidentally, nothing in the EFI RNG protocol provides any way to
* report the actual amount of entropy returned by GetRNG().
*/
#define EFI_ENTROPY_RNG_LEN 32
struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK );
/** Time (in 100ns units) to delay waiting for timer tick
*
@ -76,9 +59,6 @@ static int efi_entropy_enable ( void ) {
EFI_STATUS efirc;
int rc;
DBGC ( &tick, "ENTROPY %s RNG protocol\n",
( efirng ? "has" : "has no" ) );
/* Drop to external TPL to allow timer tick event to take place */
bs->RestoreTPL ( efi_external_tpl );
@ -91,6 +71,12 @@ static int efi_entropy_enable ( void ) {
return rc;
}
/* We use essentially the same mechanism as for the BIOS
* RTC-based entropy source, and so assume the same
* min-entropy per sample.
*/
entropy_init ( &efitick_entropy, MIN_ENTROPY ( 1.3 ) );
return 0;
}
@ -147,7 +133,7 @@ static int efi_entropy_tick ( void ) {
* @ret noise Noise sample
* @ret rc Return status code
*/
static int efi_get_noise_ticks ( noise_sample_t *noise ) {
static int efi_get_noise ( noise_sample_t *noise ) {
int before;
int after;
int rc;
@ -172,70 +158,10 @@ static int efi_get_noise_ticks ( noise_sample_t *noise ) {
return 0;
}
/**
* Get noise sample from RNG protocol
*
* @ret noise Noise sample
* @ret rc Return status code
*/
static int efi_get_noise_rng ( noise_sample_t *noise ) {
static uint8_t prev[EFI_ENTROPY_RNG_LEN];
uint8_t buf[EFI_ENTROPY_RNG_LEN];
EFI_STATUS efirc;
int rc;
/* Fail if we have no EFI RNG protocol */
if ( ! efirng )
return -ENOTSUP;
/* Get the minimum allowed number of random bytes */
if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN,
buf ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( &tick, "ENTROPY could not read from RNG: %s\n",
strerror ( rc ) );
return rc;
}
/* Fail (and permanently disable the EFI RNG) if we get
* consecutive identical results.
*/
if ( memcmp ( buf, prev, sizeof ( buf ) ) == 0 ) {
DBGC ( &tick, "ENTROPY detected broken EFI RNG:\n" );
DBGC_HDA ( &tick, 0, buf, sizeof ( buf ) );
efirng = NULL;
return -EIO;
}
memcpy ( prev, buf, sizeof ( prev ) );
/* Reduce random bytes to a single noise sample. This seems
* like overkill, but we have no way of knowing how much
* entropy is actually present in the bytes returned by the
* RNG protocol.
*/
*noise = crc32_le ( 0, buf, sizeof ( buf ) );
return 0;
}
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*/
static int efi_get_noise ( noise_sample_t *noise ) {
int rc;
/* Try RNG first, falling back to timer ticks */
if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) &&
( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) )
return rc;
return 0;
}
PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample );
PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable );
PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable );
PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise );
/** EFI entropy source */
struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK ) = {
.name = "efitick",
.enable = efi_entropy_enable,
.disable = efi_entropy_disable,
.get_noise = efi_get_noise,
};

View File

@ -43,14 +43,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
#include <ipxe/efi/Protocol/BlockIo.h>
#include <ipxe/efi/Protocol/DiskIo.h>
#include <ipxe/efi/Protocol/LoadFile2.h>
#include <ipxe/efi/Guid/FileInfo.h>
#include <ipxe/efi/Guid/FileSystemInfo.h>
#include <ipxe/efi/efi_strings.h>
#include <ipxe/efi/efi_path.h>
#include <ipxe/efi/efi_file.h>
/** EFI media ID */
#define EFI_MEDIA_ID_MAGIC 0x69505845
/** Linux initrd fixed device path vendor GUID */
#define LINUX_INITRD_VENDOR_GUID \
{ 0x5568e427, 0x68fc, 0x4f3d, \
{ 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } }
/** An EFI virtual file reader */
struct efi_file_reader {
/** EFI file */
@ -69,6 +76,8 @@ struct efi_file {
struct refcnt refcnt;
/** EFI file protocol */
EFI_FILE_PROTOCOL file;
/** EFI load file protocol */
EFI_LOAD_FILE2_PROTOCOL load;
/** Image (if any) */
struct image *image;
/** Filename */
@ -84,8 +93,18 @@ struct efi_file {
size_t ( * read ) ( struct efi_file_reader *reader );
};
/** An EFI fixed device path file */
struct efi_file_path {
/** EFI file */
struct efi_file file;
/** Device path */
EFI_DEVICE_PATH_PROTOCOL *path;
/** EFI handle */
EFI_HANDLE handle;
};
static struct efi_file efi_file_root;
static struct efi_file efi_file_initrd;
static struct efi_file_path efi_file_initrd;
/**
* Free EFI file
@ -121,7 +140,7 @@ static struct image * efi_file_find ( const char *name ) {
struct image *image;
/* Find image */
list_for_each_entry ( image, &images, list ) {
for_each_image ( image ) {
if ( strcasecmp ( image->name, name ) == 0 )
return image;
}
@ -231,8 +250,8 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) {
len = 0;
for_each_image ( image ) {
/* Ignore currently executing image */
if ( image == current_image )
/* Skip hidden images */
if ( image->flags & IMAGE_HIDDEN )
continue;
/* Pad to alignment boundary */
@ -276,10 +295,12 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) {
* Open fixed file
*
* @v file EFI file
* @v wname Filename
* @v new New EFI file
* @ret efirc EFI status code
*/
static EFI_STATUS efi_file_open_fixed ( struct efi_file *file,
const wchar_t *wname,
EFI_FILE_PROTOCOL **new ) {
/* Increment reference count */
@ -288,7 +309,8 @@ static EFI_STATUS efi_file_open_fixed ( struct efi_file *file,
/* Return opened file */
*new = &file->file;
DBGC ( file, "EFIFILE %s opened\n", efi_file_name ( file ) );
DBGC ( file, "EFIFILE %s opened via %ls\n",
efi_file_name ( file ), wname );
return 0;
}
@ -305,6 +327,36 @@ static void efi_file_image ( struct efi_file *file, struct image *image ) {
file->read = efi_file_read_image;
}
/**
* Open image-backed file
*
* @v image Image
* @v wname Filename
* @v new New EFI file
* @ret efirc EFI status code
*/
static EFI_STATUS efi_file_open_image ( struct image *image,
const wchar_t *wname,
EFI_FILE_PROTOCOL **new ) {
struct efi_file *file;
/* Allocate and initialise file */
file = zalloc ( sizeof ( *file ) );
if ( ! file )
return EFI_OUT_OF_RESOURCES;
ref_init ( &file->refcnt, efi_file_free );
memcpy ( &file->file, &efi_file_root.file, sizeof ( file->file ) );
memcpy ( &file->load, &efi_file_root.load, sizeof ( file->load ) );
efi_file_image ( file, image_get ( image ) );
/* Return opened file */
*new = &file->file;
DBGC ( file, "EFIFILE %s opened via %ls\n",
efi_file_name ( file ), wname );
return 0;
}
/**
* Open file
*
@ -320,9 +372,9 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
CHAR16 *wname, UINT64 mode, UINT64 attributes __unused ) {
struct efi_file *file = container_of ( this, struct efi_file, file );
char buf[ wcslen ( wname ) + 1 /* NUL */ ];
struct efi_file *new_file;
struct image *image;
char *name;
char *sep;
/* Convert name to ASCII */
snprintf ( buf, sizeof ( buf ), "%ls", wname );
@ -336,7 +388,7 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
/* Allow root directory itself to be opened */
if ( ( name[0] == '\0' ) || ( name[0] == '.' ) )
return efi_file_open_fixed ( &efi_file_root, new );
return efi_file_open_fixed ( &efi_file_root, wname, new );
/* Fail unless opening from the root */
if ( file != &efi_file_root ) {
@ -352,29 +404,28 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
return EFI_WRITE_PROTECTED;
}
/* Allow magic initrd to be opened */
if ( strcasecmp ( name, efi_file_initrd.name ) == 0 )
return efi_file_open_fixed ( &efi_file_initrd, new );
/* Allow registered images to be opened */
if ( ( image = efi_file_find ( name ) ) != NULL )
return efi_file_open_image ( image, wname, new );
/* Identify image */
image = efi_file_find ( name );
if ( ! image ) {
DBGC ( file, "EFIFILE %s does not exist\n", name );
return EFI_NOT_FOUND;
/* Allow magic initrd to be opened */
if ( strcasecmp ( name, efi_file_initrd.file.name ) == 0 ) {
return efi_file_open_fixed ( &efi_file_initrd.file, wname,
new );
}
/* Allocate and initialise file */
new_file = zalloc ( sizeof ( *new_file ) );
if ( ! new_file )
return EFI_OUT_OF_RESOURCES;
ref_init ( &file->refcnt, efi_file_free );
memcpy ( &new_file->file, &efi_file_root.file,
sizeof ( new_file->file ) );
efi_file_image ( new_file, image_get ( image ) );
*new = &new_file->file;
DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) );
/* Allow currently selected image to be opened as "grub*.efi",
* to work around buggy versions of the UEFI shim.
*/
if ( ( strncasecmp ( name, "grub", 4 ) == 0 ) &&
( ( sep = strrchr ( name, '.' ) ) != NULL ) &&
( strcasecmp ( sep, ".efi" ) == 0 ) &&
( ( image = find_image_tag ( &selected_image ) ) != NULL ) ) {
return efi_file_open_image ( image, wname, new );
}
return 0;
DBGC ( file, "EFIFILE %ls does not exist\n", wname );
return EFI_NOT_FOUND;
}
/**
@ -488,13 +539,21 @@ static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len,
/* Construct directory entries for image-backed files */
index = file->pos;
for_each_image ( image ) {
if ( index-- == 0 ) {
efi_file_image ( &entry, image );
efirc = efi_file_info ( &entry, len, data );
if ( efirc == 0 )
file->pos++;
return efirc;
}
/* Skip hidden images */
if ( image->flags & IMAGE_HIDDEN )
continue;
/* Skip preceding images */
if ( index-- )
continue;
/* Construct directory entry */
efi_file_image ( &entry, image );
efirc = efi_file_info ( &entry, len, data );
if ( efirc == 0 )
file->pos++;
return efirc;
}
/* No more entries */
@ -528,7 +587,7 @@ static EFI_STATUS EFIAPI efi_file_read ( EFI_FILE_PROTOCOL *this,
/* Read from the file */
DBGC ( file, "EFIFILE %s read [%#08zx,%#08zx)\n",
efi_file_name ( file ), pos, file->pos );
efi_file_name ( file ), pos, ( ( size_t ) ( pos + *len ) ) );
*len = file->read ( &reader );
assert ( ( pos + *len ) == file->pos );
@ -684,6 +743,44 @@ static EFI_STATUS EFIAPI efi_file_flush ( EFI_FILE_PROTOCOL *this ) {
return 0;
}
/**
* Load file
*
* @v this EFI file loader
* @v path File path
* @v boot Boot policy
* @v len Buffer size
* @v data Buffer, or NULL
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI
efi_file_load ( EFI_LOAD_FILE2_PROTOCOL *this,
EFI_DEVICE_PATH_PROTOCOL *path __unused,
BOOLEAN boot __unused, UINTN *len, VOID *data ) {
struct efi_file *file = container_of ( this, struct efi_file, load );
size_t max_len;
size_t file_len;
EFI_STATUS efirc;
/* Calculate maximum length */
max_len = ( data ? *len : 0 );
DBGC ( file, "EFIFILE %s load at %p+%#zx\n",
efi_file_name ( file ), data, max_len );
/* Check buffer size */
file_len = efi_file_len ( file );
if ( file_len > max_len ) {
*len = file_len;
return EFI_BUFFER_TOO_SMALL;
}
/* Read from file */
if ( ( efirc = efi_file_read ( &file->file, len, data ) ) != 0 )
return efirc;
return 0;
}
/** Root directory */
static struct efi_file efi_file_root = {
.refcnt = REF_INIT ( ref_no_free ),
@ -700,29 +797,58 @@ static struct efi_file efi_file_root = {
.SetInfo = efi_file_set_info,
.Flush = efi_file_flush,
},
.load = {
.LoadFile = efi_file_load,
},
.image = NULL,
.name = "",
};
/** Magic initrd file */
static struct efi_file efi_file_initrd = {
.refcnt = REF_INIT ( ref_no_free ),
.file = {
.Revision = EFI_FILE_PROTOCOL_REVISION,
.Open = efi_file_open,
.Close = efi_file_close,
.Delete = efi_file_delete,
.Read = efi_file_read,
.Write = efi_file_write,
.GetPosition = efi_file_get_position,
.SetPosition = efi_file_set_position,
.GetInfo = efi_file_get_info,
.SetInfo = efi_file_set_info,
.Flush = efi_file_flush,
/** Linux initrd fixed device path */
static struct {
VENDOR_DEVICE_PATH vendor;
EFI_DEVICE_PATH_PROTOCOL end;
} __attribute__ (( packed )) efi_file_initrd_path = {
.vendor = {
.Header = {
.Type = MEDIA_DEVICE_PATH,
.SubType = MEDIA_VENDOR_DP,
.Length[0] = sizeof ( efi_file_initrd_path.vendor ),
},
.Guid = LINUX_INITRD_VENDOR_GUID,
},
.image = NULL,
.name = "initrd.magic",
.read = efi_file_read_initrd,
.end = {
.Type = END_DEVICE_PATH_TYPE,
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
.Length[0] = sizeof ( efi_file_initrd_path.end ),
},
};
/** Magic initrd file */
static struct efi_file_path efi_file_initrd = {
.file = {
.refcnt = REF_INIT ( ref_no_free ),
.file = {
.Revision = EFI_FILE_PROTOCOL_REVISION,
.Open = efi_file_open,
.Close = efi_file_close,
.Delete = efi_file_delete,
.Read = efi_file_read,
.Write = efi_file_write,
.GetPosition = efi_file_get_position,
.SetPosition = efi_file_set_position,
.GetInfo = efi_file_get_info,
.SetInfo = efi_file_set_info,
.Flush = efi_file_flush,
},
.load = {
.LoadFile = efi_file_load,
},
.image = NULL,
.name = "initrd.magic",
.read = efi_file_read_initrd,
},
.path = &efi_file_initrd_path.vendor.Header,
};
/**
@ -737,7 +863,7 @@ efi_file_open_volume ( EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *filesystem __unused,
EFI_FILE_PROTOCOL **file ) {
DBGC ( &efi_file_root, "EFIFILE open volume\n" );
return efi_file_open_fixed ( &efi_file_root, file );
return efi_file_open_fixed ( &efi_file_root, L"<volume>", file );
}
/** EFI simple file system protocol */
@ -833,6 +959,151 @@ static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = {
.WriteDisk = efi_disk_io_write_disk,
};
/**
* Claim use of fixed device path
*
* @v file Fixed device path file
* @ret rc Return status code
*
* The design choice in Linux of using a single fixed device path is
* unfortunately messy to support, since device paths must be unique
* within a system. When multiple bootloaders are used (e.g. GRUB
* loading iPXE loading Linux) then only one bootloader can ever
* install the device path onto a handle. Bootloaders must therefore
* be prepared to locate an existing handle and uninstall its device
* path protocol instance before installing a new handle with the
* required device path.
*/
static int efi_file_path_claim ( struct efi_file_path *file ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_DEVICE_PATH_PROTOCOL *end;
EFI_HANDLE handle;
VOID *old;
EFI_STATUS efirc;
int rc;
/* Sanity check */
assert ( file->handle == NULL );
/* Locate handle with this device path, if any */
end = file->path;
if ( ( ( efirc = bs->LocateDevicePath ( &efi_device_path_protocol_guid,
&end, &handle ) ) != 0 ) ||
( end->Type != END_DEVICE_PATH_TYPE ) ) {
return 0;
}
/* Locate device path protocol on this handle */
if ( ( ( efirc = bs->HandleProtocol ( handle,
&efi_device_path_protocol_guid,
&old ) ) != 0 ) ) {
rc = -EEFI ( efirc );
DBGC ( file, "EFIFILE %s could not locate %s: %s\n",
efi_file_name ( &file->file ),
efi_devpath_text ( file->path ), strerror ( rc ) );
return rc;
}
/* Uninstall device path protocol, leaving other protocols untouched */
if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
handle,
&efi_device_path_protocol_guid, old,
NULL ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( file, "EFIFILE %s could not claim %s: %s\n",
efi_file_name ( &file->file ),
efi_devpath_text ( file->path ), strerror ( rc ) );
return rc;
}
DBGC ( file, "EFIFILE %s claimed %s",
efi_file_name ( &file->file ), efi_devpath_text ( file->path ) );
DBGC ( file, " from %s\n", efi_handle_name ( handle ) );
return 0;
}
/**
* Install fixed device path file
*
* @v file Fixed device path file
* @ret rc Return status code
*
* Linux 5.7 added the ability to autodetect an initrd by searching
* for a handle via a fixed vendor-specific "Linux initrd device path"
* and then locating and using the EFI_LOAD_FILE2_PROTOCOL instance on
* that handle.
*/
static int efi_file_path_install ( struct efi_file_path *file ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_STATUS efirc;
int rc;
/* Sanity check */
assert ( file->handle == NULL );
/* Create a new handle with this device path */
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
&file->handle,
&efi_device_path_protocol_guid, file->path,
&efi_load_file2_protocol_guid, &file->file.load,
NULL ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( file, "EFIFILE %s could not install %s: %s\n",
efi_file_name ( &file->file ),
efi_devpath_text ( file->path ), strerror ( rc ) );
return rc;
}
DBGC ( file, "EFIFILE %s installed as %s\n",
efi_file_name ( &file->file ), efi_devpath_text ( file->path ) );
return 0;
}
/**
* Uninstall fixed device path file
*
* @v file Fixed device path file
* @ret rc Return status code
*/
static void efi_file_path_uninstall ( struct efi_file_path *file ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_STATUS efirc;
int rc;
/* Do nothing if file is already uninstalled */
if ( ! file->handle )
return;
/* Uninstall protocols. Do this via two separate calls, in
* case another executable has already uninstalled the device
* path protocol from our handle.
*/
if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
file->handle,
&efi_device_path_protocol_guid, file->path,
NULL ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( file, "EFIFILE %s could not uninstall %s: %s\n",
efi_file_name ( &file->file ),
efi_devpath_text ( file->path ), strerror ( rc ) );
/* Continue uninstalling */
}
if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
file->handle,
&efi_load_file2_protocol_guid, &file->file.load,
NULL ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( file, "EFIFILE %s could not uninstall %s: %s\n",
efi_file_name ( &file->file ),
efi_guid_ntoa ( &efi_load_file2_protocol_guid ),
strerror ( rc ) );
/* Continue uninstalling */
}
/* Mark handle as uninstalled */
file->handle = NULL;
}
/**
* Install EFI simple file system protocol
*
@ -845,6 +1116,7 @@ int efi_file_install ( EFI_HANDLE handle ) {
EFI_DISK_IO_PROTOCOL *diskio;
void *interface;
} diskio;
struct image *image;
EFI_STATUS efirc;
int rc;
@ -903,8 +1175,24 @@ int efi_file_install ( EFI_HANDLE handle ) {
}
assert ( diskio.diskio == &efi_disk_io_protocol );
/* Claim Linux initrd fixed device path */
if ( ( rc = efi_file_path_claim ( &efi_file_initrd ) ) != 0 )
goto err_initrd_claim;
/* Install Linux initrd fixed device path file if non-empty */
for_each_image ( image ) {
if ( image->flags & IMAGE_HIDDEN )
continue;
if ( ( rc = efi_file_path_install ( &efi_file_initrd ) ) != 0 )
goto err_initrd_install;
break;
}
return 0;
efi_file_path_uninstall ( &efi_file_initrd );
err_initrd_install:
err_initrd_claim:
bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
efi_image_handle, handle );
err_open:
@ -930,6 +1218,9 @@ void efi_file_uninstall ( EFI_HANDLE handle ) {
EFI_STATUS efirc;
int rc;
/* Uninstall Linux initrd fixed device path file */
efi_file_path_uninstall ( &efi_file_initrd );
/* Close our own disk I/O protocol */
bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
efi_image_handle, handle );

View File

@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/Protocol/PciRootBridgeIo.h>
#include <ipxe/efi/Protocol/PxeBaseCode.h>
#include <ipxe/efi/Protocol/SerialIo.h>
#include <ipxe/efi/Protocol/ShimLock.h>
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#include <ipxe/efi/Protocol/SimplePointer.h>
@ -227,6 +228,10 @@ EFI_GUID efi_pxe_base_code_protocol_guid
EFI_GUID efi_serial_io_protocol_guid
= EFI_SERIAL_IO_PROTOCOL_GUID;
/** Shim lock protocol GUID */
EFI_GUID efi_shim_lock_protocol_guid
= EFI_SHIM_LOCK_PROTOCOL_GUID;
/** Simple file system protocol GUID */
EFI_GUID efi_simple_file_system_protocol_guid
= EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;

118
src/interface/efi/efi_rng.c Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/entropy.h>
#include <ipxe/crc32.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/Rng.h>
/** @file
*
* EFI random number generator protocol entropy source
*
*/
struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL );
/** Random number generator protocol */
static EFI_RNG_PROTOCOL *efirng;
EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );
/** Minimum number of bytes to request from RNG
*
* The UEFI spec states (for no apparently good reason) that "When a
* Deterministic Random Bit Generator (DRBG) is used on the output of
* a (raw) entropy source, its security level must be at least 256
* bits." The EDK2 codebase (mis)interprets this to mean that the
* call to GetRNG() should fail if given a buffer less than 32 bytes.
*
* Incidentally, nothing in the EFI RNG protocol provides any way to
* report the actual amount of entropy returned by GetRNG().
*/
#define EFIRNG_LEN 32
/**
* Enable entropy gathering
*
* @ret rc Return status code
*/
static int efirng_enable ( void ) {
/* Check for RNG protocol support */
if ( ! efirng ) {
DBGC ( &efirng, "EFIRNG has no RNG protocol\n" );
return -ENOTSUP;
}
/* Nothing in the EFI specification provides any clue as to
* how much entropy will be returned by GetRNG(). Make a
* totally uninformed (and conservative guess) that each
* sample will contain at least one bit of entropy.
*/
entropy_init ( &efirng_entropy, MIN_ENTROPY ( 1.0 ) );
return 0;
}
/**
* Get noise sample from RNG protocol
*
* @ret noise Noise sample
* @ret rc Return status code
*/
static int efirng_get_noise ( noise_sample_t *noise ) {
uint8_t buf[EFIRNG_LEN];
EFI_STATUS efirc;
int rc;
/* Sanity check */
assert ( efirng != NULL );
/* Get the minimum allowed number of random bytes */
if ( ( efirc = efirng->GetRNG ( efirng, NULL, sizeof ( buf ),
buf ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( &efirng, "ENTROPY could not read from RNG: %s\n",
strerror ( rc ) );
return rc;
}
/* Reduce random bytes to a single noise sample. This seems
* like overkill, but we have no way of knowing how much
* entropy is actually present in the bytes returned by the
* RNG protocol.
*/
*noise = crc32_le ( 0, buf, sizeof ( buf ) );
return 0;
}
/** EFI random number generator protocol entropy source */
struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL ) = {
.name = "efirng",
.enable = efirng_enable,
.get_noise = efirng_get_noise,
};

View File

@ -0,0 +1,402 @@
/*
* Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ipxe/image.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_strings.h>
#include <ipxe/efi/efi_shim.h>
#include <ipxe/efi/Protocol/PxeBaseCode.h>
#include <ipxe/efi/Protocol/ShimLock.h>
/** @file
*
* UEFI shim special handling
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Require use of a third party loader binary
*
* The UEFI shim is gradually becoming less capable of directly
* executing a Linux kernel image, due to an ever increasing list of
* assumptions that it will only ever be used in conjunction with a
* second stage loader binary such as GRUB.
*
* For example: shim will erroneously complain if the image that it
* loads and executes does not in turn call in to the "shim lock
* protocol" to verify a separate newly loaded binary before calling
* ExitBootServices(), even if no such separate binary is used or
* required.
*
* Experience shows that there is unfortunately no point in trying to
* get a fix for this upstreamed into shim. We therefore default to
* reducing the Secure Boot attack surface by removing, where
* possible, this spurious requirement for the use of an additional
* second stage loader.
*
* This option may be used to require the use of an additional second
* stage loader binary, in case this behaviour is ever desirable.
*/
int efi_shim_require_loader = 0;
/**
* Allow use of PXE base code protocol
*
* We provide shim with access to all of the relevant downloaded files
* via our EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interface. However, shim
* will instead try to redownload the files via TFTP since it prefers
* to use the EFI_PXE_BASE_CODE_PROTOCOL installed on the same handle.
*
* Experience shows that there is unfortunately no point in trying to
* get a fix for this upstreamed into shim. We therefore default to
* working around this undesirable behaviour by stopping the PXE base
* code protocol before invoking shim.
*
* This option may be used to allow shim to use the PXE base code
* protocol, in case this behaviour is ever desirable.
*/
int efi_shim_allow_pxe = 0;
/**
* Allow SBAT variable access
*
* The UEFI shim implements a fairly nicely designed revocation
* mechanism designed around the concept of security generations.
* Unfortunately nobody in the shim community has thus far added the
* relevant metadata to the Linux kernel, with the result that current
* versions of shim are incapable of booting current versions of the
* Linux kernel.
*
* Experience shows that there is unfortunately no point in trying to
* get a fix for this upstreamed into shim. We therefore default to
* working around this undesirable behaviour by patching data read
* from the "SbatLevel" variable used to hold SBAT configuration.
*
* This option may be used to allow shim unpatched access to the
* "SbatLevel" variable, in case this behaviour is ever desirable.
*/
int efi_shim_allow_sbat = 0;
/** UEFI shim image */
struct image_tag efi_shim __image_tag = {
.name = "SHIM",
};
/** Original GetMemoryMap() function */
static EFI_GET_MEMORY_MAP efi_shim_orig_get_memory_map;
/** Original ExitBootServices() function */
static EFI_EXIT_BOOT_SERVICES efi_shim_orig_exit_boot_services;
/** Original SetVariable() function */
static EFI_SET_VARIABLE efi_shim_orig_set_variable;
/** Original GetVariable() function */
static EFI_GET_VARIABLE efi_shim_orig_get_variable;
/** Verify read from SbatLevel variable */
static int efi_shim_sbatlevel_verify;
/**
* Check if variable is SbatLevel
*
* @v name Variable name
* @v guid Variable namespace GUID
* @ret is_sbatlevel Variable is SbatLevel
*/
static int efi_shim_is_sbatlevel ( const CHAR16 *name, const EFI_GUID *guid ) {
static CHAR16 sbatlevel[] = L"SbatLevel";
EFI_GUID *shimlock = &efi_shim_lock_protocol_guid;
return ( ( memcmp ( name, sbatlevel, sizeof ( sbatlevel ) ) == 0 ) &&
( memcmp ( guid, shimlock, sizeof ( *shimlock ) ) == 0 ) );
}
/**
* Unlock UEFI shim
*
*/
static void efi_shim_unlock ( void ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
uint8_t empty[0];
union {
EFI_SHIM_LOCK_PROTOCOL *lock;
void *interface;
} u;
EFI_STATUS efirc;
/* Locate shim lock protocol */
if ( ( efirc = bs->LocateProtocol ( &efi_shim_lock_protocol_guid,
NULL, &u.interface ) ) == 0 ) {
u.lock->Verify ( empty, sizeof ( empty ) );
DBGC ( &efi_shim, "SHIM unlocked via %p\n", u.lock );
}
}
/**
* Wrap GetMemoryMap()
*
* @v len Memory map size
* @v map Memory map
* @v key Memory map key
* @v desclen Descriptor size
* @v descver Descriptor version
* @ret efirc EFI status code
*/
static EFIAPI EFI_STATUS efi_shim_get_memory_map ( UINTN *len,
EFI_MEMORY_DESCRIPTOR *map,
UINTN *key, UINTN *desclen,
UINT32 *descver ) {
/* Unlock shim */
if ( ! efi_shim_require_loader )
efi_shim_unlock();
/* Hand off to original GetMemoryMap() */
return efi_shim_orig_get_memory_map ( len, map, key, desclen,
descver );
}
/**
* Wrap ExitBootServices()
*
* @v handle Image handle
* @v key Memory map key
* @ret efirc EFI status code
*/
static EFIAPI EFI_STATUS efi_shim_exit_boot_services ( EFI_HANDLE handle,
UINTN key ) {
EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
/* Restore original runtime services functions */
rs->GetVariable = efi_shim_orig_get_variable;
rs->SetVariable = efi_shim_orig_set_variable;
/* Hand off to original ExitBootServices() */
return efi_shim_orig_exit_boot_services ( handle, key );
}
/**
* Wrap SetVariable()
*
* @v name Variable name
* @v guid Variable namespace GUID
* @v attrs Attributes
* @v len Buffer size
* @v data Data buffer
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI
efi_shim_set_variable ( CHAR16 *name, EFI_GUID *guid, UINT32 attrs,
UINTN len, VOID *data ) {
EFI_STATUS efirc;
/* Call original SetVariable() */
efirc = efi_shim_orig_set_variable ( name, guid, attrs, len, data );
/* Allow verification of SbatLevel variable content */
if ( efi_shim_is_sbatlevel ( name, guid ) && ( efirc == 0 ) ) {
DBGC ( &efi_shim, "SHIM detected write to %ls:\n", name );
DBGC_HDA ( &efi_shim, 0, data, len );
efi_shim_sbatlevel_verify = 1;
}
return efirc;
}
/**
* Wrap GetVariable()
*
* @v name Variable name
* @v guid Variable namespace GUID
* @v attrs Attributes to fill in
* @v len Buffer size
* @v data Data buffer
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI
efi_shim_get_variable ( CHAR16 *name, EFI_GUID *guid, UINT32 *attrs,
UINTN *len, VOID *data ) {
char *value = data;
EFI_STATUS efirc;
/* Call original GetVariable() */
efirc = efi_shim_orig_get_variable ( name, guid, attrs, len, data );
/* Patch SbatLevel variable if applicable */
if ( efi_shim_is_sbatlevel ( name, guid ) && data && ( efirc == 0 ) ) {
if ( efi_shim_allow_sbat ) {
DBGC ( &efi_shim, "SHIM allowing read from %ls:\n",
name );
} else if ( efi_shim_sbatlevel_verify ) {
DBGC ( &efi_shim, "SHIM allowing one read from %ls:\n",
name );
efi_shim_sbatlevel_verify = 0;
} else {
DBGC ( &efi_shim, "SHIM patching read from %ls:\n",
name );
value[0] = '\0';
}
DBGC_HDA ( &efi_shim, 0, data, *len );
}
return efirc;
}
/**
* Inhibit use of PXE base code
*
* @v handle EFI handle
* @ret rc Return status code
*/
static int efi_shim_inhibit_pxe ( EFI_HANDLE handle ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
union {
EFI_PXE_BASE_CODE_PROTOCOL *pxe;
void *interface;
} u;
EFI_STATUS efirc;
int rc;
/* Locate PXE base code */
if ( ( efirc = bs->OpenProtocol ( handle,
&efi_pxe_base_code_protocol_guid,
&u.interface, efi_image_handle, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
rc = -EEFI ( efirc );
DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n",
strerror ( rc ) );
goto err_no_base;
}
/* Stop PXE base code */
if ( ( efirc = u.pxe->Stop ( u.pxe ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n",
strerror ( rc ) );
goto err_stop;
}
/* Success */
rc = 0;
DBGC ( &efi_shim, "SHIM stopped PXE base code\n" );
err_stop:
bs->CloseProtocol ( handle, &efi_pxe_base_code_protocol_guid,
efi_image_handle, NULL );
err_no_base:
return rc;
}
/**
* Update command line
*
* @v shim Shim image
* @v cmdline Command line to update
* @ret rc Return status code
*/
static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) {
wchar_t *shimcmdline;
int len;
int rc;
/* Construct new command line */
len = ( shim->cmdline ?
efi_asprintf ( &shimcmdline, "%s %s", shim->name,
shim->cmdline ) :
efi_asprintf ( &shimcmdline, "%s %ls", shim->name,
*cmdline ) );
if ( len < 0 ) {
rc = len;
DBGC ( &efi_shim, "SHIM could not construct command line: "
"%s\n", strerror ( rc ) );
return rc;
}
/* Replace command line */
free ( *cmdline );
*cmdline = shimcmdline;
return 0;
}
/**
* Install UEFI shim special handling
*
* @v shim Shim image
* @v handle EFI device handle
* @v cmdline Command line to update
* @ret rc Return status code
*/
int efi_shim_install ( struct image *shim, EFI_HANDLE handle,
wchar_t **cmdline ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
int rc;
/* Stop PXE base code */
if ( ( ! efi_shim_allow_pxe ) &&
( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) {
return rc;
}
/* Update command line */
if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 )
return rc;
/* Record original boot and runtime services functions */
efi_shim_orig_get_memory_map = bs->GetMemoryMap;
efi_shim_orig_exit_boot_services = bs->ExitBootServices;
efi_shim_orig_set_variable = rs->SetVariable;
efi_shim_orig_get_variable = rs->GetVariable;
/* Wrap relevant boot and runtime services functions */
bs->GetMemoryMap = efi_shim_get_memory_map;
bs->ExitBootServices = efi_shim_exit_boot_services;
rs->SetVariable = efi_shim_set_variable;
rs->GetVariable = efi_shim_get_variable;
return 0;
}
/**
* Uninstall UEFI shim special handling
*
*/
void efi_shim_uninstall ( void ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
/* Restore original boot and runtime services functions */
bs->GetMemoryMap = efi_shim_orig_get_memory_map;
bs->ExitBootServices = efi_shim_orig_exit_boot_services;
rs->SetVariable = efi_shim_orig_set_variable;
rs->GetVariable = efi_shim_orig_get_variable;
}

View File

@ -25,6 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <ipxe/vsprintf.h>
#include <ipxe/efi/efi_strings.h>
@ -150,3 +152,45 @@ int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, ... ) {
va_end ( args );
return len;
}
/**
* Write a formatted string to newly allocated memory
*
* @v wstrp Pointer to hold allocated string
* @v fmt Format string
* @v args Arguments corresponding to the format string
* @ret len Length of formatted string (in wide characters)
*/
int efi_vasprintf ( wchar_t **wstrp, const char *fmt, va_list args ) {
size_t len;
va_list args_tmp;
/* Calculate length needed for string */
va_copy ( args_tmp, args );
len = ( efi_vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 );
va_end ( args_tmp );
/* Allocate and fill string */
*wstrp = malloc ( len * sizeof ( **wstrp ) );
if ( ! *wstrp )
return -ENOMEM;
return efi_vsnprintf ( *wstrp, len, fmt, args );
}
/**
* Write a formatted string to newly allocated memory
*
* @v wstrp Pointer to hold allocated string
* @v fmt Format string
* @v ... Arguments corresponding to the format string
* @ret len Length of formatted string (in wide characters)
*/
int efi_asprintf ( wchar_t **wstrp, const char *fmt, ... ) {
va_list args;
int len;
va_start ( args, fmt );
len = efi_vasprintf ( wstrp, fmt, args );
va_end ( args );
return len;
}

View File

@ -34,6 +34,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/linux_api.h>
#include <ipxe/entropy.h>
struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL );
/** Entropy source filename */
static const char entropy_filename[] = "/dev/random";
@ -55,6 +57,13 @@ static int linux_entropy_enable ( void ) {
return entropy_fd;
}
/* linux_get_noise() reads a single byte from /dev/random,
* which is supposed to block until a sufficient amount of
* entropy is available. We therefore assume that each sample
* contains exactly 8 bits of entropy.
*/
entropy_init ( &linux_entropy, MIN_ENTROPY ( 8.0 ) );
return 0;
}
@ -95,7 +104,10 @@ static int linux_get_noise ( noise_sample_t *noise ) {
return 0;
}
PROVIDE_ENTROPY_INLINE ( linux, min_entropy_per_sample );
PROVIDE_ENTROPY ( linux, entropy_enable, linux_entropy_enable );
PROVIDE_ENTROPY ( linux, entropy_disable, linux_entropy_disable );
PROVIDE_ENTROPY ( linux, get_noise, linux_get_noise );
/** Linux entropy source */
struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL ) = {
.name = "linux",
.enable = linux_entropy_enable,
.disable = linux_entropy_disable,
.get_noise = linux_get_noise,
};

View File

@ -830,7 +830,9 @@ static int http_transfer_complete ( struct http_transaction *http ) {
*/
static int http_format_headers ( struct http_transaction *http, char *buf,
size_t len ) {
struct parameters *params = http->uri->params;
struct http_request_header *header;
struct parameter *param;
size_t used;
size_t remaining;
char *line;
@ -844,7 +846,7 @@ static int http_format_headers ( struct http_transaction *http, char *buf,
DBGC2 ( http, "HTTP %p TX %s\n", http, buf );
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
/* Construct all headers */
/* Construct all fixed headers */
for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) {
/* Determine header value length */
@ -869,6 +871,23 @@ static int http_format_headers ( struct http_transaction *http, char *buf,
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
}
/* Construct parameter headers, if any */
if ( params ) {
/* Construct all parameter headers */
for_each_param ( param, params ) {
/* Skip non-header parameters */
if ( ! ( param->flags & PARAMETER_HEADER ) )
continue;
/* Add parameter */
used += ssnprintf ( ( buf + used ), ( len - used ),
"%s: %s\r\n", param->key,
param->value );
}
}
/* Construct terminating newline */
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
@ -1851,14 +1870,15 @@ static struct http_state http_trailers = {
*/
/**
* Construct HTTP parameter list
* Construct HTTP form parameter list
*
* @v params Parameter list
* @v buf Buffer to contain HTTP POST parameters
* @v len Length of buffer
* @ret len Length of parameter list (excluding terminating NUL)
*/
static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
static size_t http_form_params ( struct parameters *params, char *buf,
size_t len ) {
struct parameter *param;
ssize_t remaining = len;
size_t frag_len;
@ -1867,6 +1887,10 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
len = 0;
for_each_param ( param, params ) {
/* Skip non-form parameters */
if ( ! ( param->flags & PARAMETER_FORM ) )
continue;
/* Add the "&", if applicable */
if ( len ) {
if ( remaining > 0 )
@ -1904,53 +1928,59 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
}
/**
* Open HTTP transaction for simple GET URI
* Open HTTP transaction for simple URI
*
* @v xfer Data transfer interface
* @v uri Request URI
* @ret rc Return status code
*/
static int http_open_get_uri ( struct interface *xfer, struct uri *uri ) {
return http_open ( xfer, &http_get, uri, NULL, NULL );
}
/**
* Open HTTP transaction for simple POST URI
*
* @v xfer Data transfer interface
* @v uri Request URI
* @ret rc Return status code
*/
static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) {
int http_open_uri ( struct interface *xfer, struct uri *uri ) {
struct parameters *params = uri->params;
struct http_request_content content;
struct http_method *method;
const char *type;
void *data;
size_t len;
size_t check_len;
int rc;
/* Calculate length of parameter list */
len = http_params ( params, NULL, 0 );
/* Calculate length of form parameter list, if any */
len = ( params ? http_form_params ( params, NULL, 0 ) : 0 );
/* Allocate temporary parameter list */
data = zalloc ( len + 1 /* NUL */ );
if ( ! data ) {
rc = -ENOMEM;
goto err_alloc;
/* Use POST if and only if there are form parameters */
if ( len ) {
/* Use POST */
method = &http_post;
type = "application/x-www-form-urlencoded";
/* Allocate temporary form parameter list */
data = zalloc ( len + 1 /* NUL */ );
if ( ! data ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Construct temporary form parameter list */
check_len = http_form_params ( params, data,
( len + 1 /* NUL */ ) );
assert ( check_len == len );
} else {
/* Use GET */
method = &http_get;
type = NULL;
data = NULL;
}
/* Construct temporary parameter list */
check_len = http_params ( params, data, ( len + 1 /* NUL */ ) );
assert ( check_len == len );
/* Construct request content */
content.type = "application/x-www-form-urlencoded";
content.type = type;
content.data = data;
content.len = len;
/* Open HTTP transaction */
if ( ( rc = http_open ( xfer, &http_post, uri, NULL, &content ) ) != 0 )
if ( ( rc = http_open ( xfer, method, uri, NULL, &content ) ) != 0 )
goto err_open;
err_open:
@ -1959,23 +1989,6 @@ static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) {
return rc;
}
/**
* Open HTTP transaction for simple URI
*
* @v xfer Data transfer interface
* @v uri Request URI
* @ret rc Return status code
*/
int http_open_uri ( struct interface *xfer, struct uri *uri ) {
/* Open GET/POST URI as applicable */
if ( uri->params ) {
return http_open_post_uri ( xfer, uri );
} else {
return http_open_get_uri ( xfer, uri );
}
}
/* Drag in HTTP extensions */
REQUIRING_SYMBOL ( http_open );
REQUIRE_OBJECT ( config_http );

View File

@ -46,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/base16.h>
#include <ipxe/base64.h>
#include <ipxe/ibft.h>
#include <ipxe/blockdev.h>
#include <ipxe/efi/efi_path.h>
#include <ipxe/iscsi.h>
@ -86,6 +87,10 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 );
__einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN )
#define EINFO_EINVAL_NO_INITIATOR_IQN \
__einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" )
#define EINVAL_MAXBURSTLENGTH \
__einfo_error ( EINFO_EINVAL_MAXBURSTLENGTH )
#define EINFO_EINVAL_MAXBURSTLENGTH \
__einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid MaxBurstLength" )
#define EIO_TARGET_UNAVAILABLE \
__einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE )
#define EINFO_EIO_TARGET_UNAVAILABLE \
@ -281,6 +286,9 @@ static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
/* Assign fresh initiator task tag */
iscsi_new_itt ( iscsi );
/* Set default operational parameters */
iscsi->max_burst_len = ISCSI_MAX_BURST_LEN;
/* Initiate login */
iscsi_start_login ( iscsi );
@ -736,16 +744,20 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
"MaxConnections=1%c"
"InitialR2T=Yes%c"
"ImmediateData=No%c"
"MaxRecvDataSegmentLength=8192%c"
"MaxBurstLength=262144%c"
"FirstBurstLength=65536%c"
"MaxRecvDataSegmentLength=%d%c"
"MaxBurstLength=%d%c"
"FirstBurstLength=%d%c"
"DefaultTime2Wait=0%c"
"DefaultTime2Retain=0%c"
"MaxOutstandingR2T=1%c"
"DataPDUInOrder=Yes%c"
"DataSequenceInOrder=Yes%c"
"ErrorRecoveryLevel=0%c",
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
0, 0, 0, 0, 0,
ISCSI_MAX_RECV_DATA_SEG_LEN, 0,
ISCSI_MAX_BURST_LEN, 0,
ISCSI_FIRST_BURST_LEN, 0,
0, 0, 0, 0, 0, 0 );
}
return used;
@ -908,6 +920,31 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
return 0;
}
/**
* Handle iSCSI MaxBurstLength text value
*
* @v iscsi iSCSI session
* @v value MaxBurstLength value
* @ret rc Return status code
*/
static int iscsi_handle_maxburstlength_value ( struct iscsi_session *iscsi,
const char *value ) {
unsigned long max_burst_len;
char *end;
/* Update maximum burst length */
max_burst_len = strtoul ( value, &end, 0 );
if ( *end ) {
DBGC ( iscsi, "iSCSI %p invalid MaxBurstLength \"%s\"\n",
iscsi, value );
return -EINVAL_MAXBURSTLENGTH;
}
if ( max_burst_len < iscsi->max_burst_len )
iscsi->max_burst_len = max_burst_len;
return 0;
}
/**
* Handle iSCSI CHAP_A text value
*
@ -1148,6 +1185,7 @@ struct iscsi_string_type {
/** iSCSI text strings that we want to handle */
static struct iscsi_string_type iscsi_string_types[] = {
{ "TargetAddress", iscsi_handle_targetaddress_value },
{ "MaxBurstLength", iscsi_handle_maxburstlength_value },
{ "AuthMethod", iscsi_handle_authmethod_value },
{ "CHAP_A", iscsi_handle_chap_a_value },
{ "CHAP_I", iscsi_handle_chap_i_value },
@ -1847,6 +1885,24 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi,
return iscsi->itt;
}
/**
* Update SCSI block device capacity
*
* @v iscsi iSCSI session
* @v capacity Block device capacity
*/
static void iscsi_scsi_capacity ( struct iscsi_session *iscsi,
struct block_device_capacity *capacity ) {
unsigned int max_count;
/* Limit maximum number of blocks per transfer to fit MaxBurstLength */
if ( capacity->blksize ) {
max_count = ( iscsi->max_burst_len / capacity->blksize );
if ( max_count < capacity->max_count )
capacity->max_count = max_count;
}
}
/**
* Get iSCSI ACPI descriptor
*
@ -1862,6 +1918,7 @@ static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) {
static struct interface_operation iscsi_control_op[] = {
INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ),
INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ),
INTF_OP ( block_capacity, struct iscsi_session *, iscsi_scsi_capacity ),
INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ),
INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ),
EFI_INTF_OP ( efi_describe, struct iscsi_session *, efi_iscsi_path ),

View File

@ -388,6 +388,7 @@ static void free_tls ( struct refcnt *refcnt ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
}
free_iob ( tls->rx_handshake );
x509_chain_put ( tls->certs );
x509_chain_put ( tls->chain );
x509_root_put ( tls->root );
@ -1682,9 +1683,14 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) {
* @ret rc Return status code
*/
static int tls_send_change_cipher ( struct tls_connection *tls ) {
static const uint8_t change_cipher[1] = { 1 };
static const struct {
uint8_t spec;
} __attribute__ (( packed )) change_cipher = {
.spec = TLS_CHANGE_CIPHER_SPEC,
};
return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER,
change_cipher, sizeof ( change_cipher ) );
&change_cipher, sizeof ( change_cipher ) );
}
/**
@ -1731,20 +1737,27 @@ static int tls_send_finished ( struct tls_connection *tls ) {
* Receive new Change Cipher record
*
* @v tls TLS connection
* @v data Plaintext record
* @v len Length of plaintext record
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int tls_new_change_cipher ( struct tls_connection *tls,
const void *data, size_t len ) {
struct io_buffer *iobuf ) {
const struct {
uint8_t spec;
} __attribute__ (( packed )) *change_cipher = iobuf->data;
size_t len = iob_len ( iobuf );
int rc;
if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) {
/* Sanity check */
if ( ( sizeof ( *change_cipher ) != len ) ||
( change_cipher->spec != TLS_CHANGE_CIPHER_SPEC ) ) {
DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls );
DBGC_HD ( tls, data, len );
DBGC_HD ( tls, change_cipher, len );
return -EINVAL_CHANGE_CIPHER;
}
iob_pull ( iobuf, sizeof ( *change_cipher ) );
/* Change receive cipher spec */
if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending,
&tls->rx_cipherspec ) ) != 0 ) {
DBGC ( tls, "TLS %p could not activate RX cipher: %s\n",
@ -1760,25 +1773,27 @@ static int tls_new_change_cipher ( struct tls_connection *tls,
* Receive new Alert record
*
* @v tls TLS connection
* @v data Plaintext record
* @v len Length of plaintext record
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int tls_new_alert ( struct tls_connection *tls, const void *data,
size_t len ) {
static int tls_new_alert ( struct tls_connection *tls,
struct io_buffer *iobuf ) {
const struct {
uint8_t level;
uint8_t description;
char next[0];
} __attribute__ (( packed )) *alert = data;
} __attribute__ (( packed )) *alert = iobuf->data;
size_t len = iob_len ( iobuf );
/* Sanity check */
if ( sizeof ( *alert ) != len ) {
DBGC ( tls, "TLS %p received overlength Alert\n", tls );
DBGC_HD ( tls, data, len );
DBGC_HD ( tls, alert, len );
return -EINVAL_ALERT;
}
iob_pull ( iobuf, sizeof ( *alert ) );
/* Handle alert */
switch ( alert->level ) {
case TLS_ALERT_WARNING:
DBGC ( tls, "TLS %p received warning alert %d\n",
@ -2392,38 +2407,33 @@ static int tls_new_finished ( struct tls_connection *tls,
* Receive new Handshake record
*
* @v tls TLS connection
* @v data Plaintext record
* @v len Length of plaintext record
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int tls_new_handshake ( struct tls_connection *tls,
const void *data, size_t len ) {
size_t remaining = len;
struct io_buffer *iobuf ) {
size_t remaining;
int rc;
while ( remaining ) {
while ( ( remaining = iob_len ( iobuf ) ) ) {
const struct {
uint8_t type;
tls24_t length;
uint8_t payload[0];
} __attribute__ (( packed )) *handshake = data;
} __attribute__ (( packed )) *handshake = iobuf->data;
const void *payload;
size_t payload_len;
size_t record_len;
/* Parse header */
if ( sizeof ( *handshake ) > remaining ) {
DBGC ( tls, "TLS %p received underlength Handshake\n",
tls );
DBGC_HD ( tls, data, remaining );
return -EINVAL_HANDSHAKE;
/* Leave remaining fragment unconsumed */
break;
}
payload_len = tls_uint24 ( &handshake->length );
if ( payload_len > ( remaining - sizeof ( *handshake ) ) ) {
DBGC ( tls, "TLS %p received overlength Handshake\n",
tls );
DBGC_HD ( tls, data, len );
return -EINVAL_HANDSHAKE;
/* Leave remaining fragment unconsumed */
break;
}
payload = &handshake->payload;
record_len = ( sizeof ( *handshake ) + payload_len );
@ -2470,15 +2480,60 @@ static int tls_new_handshake ( struct tls_connection *tls,
* which are explicitly excluded).
*/
if ( handshake->type != TLS_HELLO_REQUEST )
tls_add_handshake ( tls, data, record_len );
tls_add_handshake ( tls, handshake, record_len );
/* Abort on failure */
if ( rc != 0 )
return rc;
/* Move to next handshake record */
data += record_len;
remaining -= record_len;
iob_pull ( iobuf, record_len );
}
return 0;
}
/**
* Receive new unknown record
*
* @v tls TLS connection
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int tls_new_unknown ( struct tls_connection *tls __unused,
struct io_buffer *iobuf ) {
/* RFC4346 says that we should just ignore unknown record types */
iob_pull ( iobuf, iob_len ( iobuf ) );
return 0;
}
/**
* Receive new data record
*
* @v tls TLS connection
* @v rx_data List of received data buffers
* @ret rc Return status code
*/
static int tls_new_data ( struct tls_connection *tls,
struct list_head *rx_data ) {
struct io_buffer *iobuf;
int rc;
/* Fail unless we are ready to receive data */
if ( ! tls_ready ( tls ) )
return -ENOTCONN;
/* Deliver each I/O buffer in turn */
while ( ( iobuf = list_first_entry ( rx_data, struct io_buffer,
list ) ) ) {
list_del ( &iobuf->list );
if ( ( rc = xfer_deliver_iob ( &tls->plainstream,
iobuf ) ) != 0 ) {
DBGC ( tls, "TLS %p could not deliver data: "
"%s\n", tls, strerror ( rc ) );
return rc;
}
}
return 0;
@ -2494,41 +2549,18 @@ static int tls_new_handshake ( struct tls_connection *tls,
*/
static int tls_new_record ( struct tls_connection *tls, unsigned int type,
struct list_head *rx_data ) {
struct io_buffer *iobuf;
int ( * handler ) ( struct tls_connection *tls, const void *data,
size_t len );
int ( * handler ) ( struct tls_connection *tls,
struct io_buffer *iobuf );
struct io_buffer *tmp = NULL;
struct io_buffer **iobuf;
int rc;
/* Deliver data records to the plainstream interface */
if ( type == TLS_TYPE_DATA ) {
/* Deliver data records as-is to the plainstream interface */
if ( type == TLS_TYPE_DATA )
return tls_new_data ( tls, rx_data );
/* Fail unless we are ready to receive data */
if ( ! tls_ready ( tls ) )
return -ENOTCONN;
/* Deliver each I/O buffer in turn */
while ( ( iobuf = list_first_entry ( rx_data, struct io_buffer,
list ) ) ) {
list_del ( &iobuf->list );
if ( ( rc = xfer_deliver_iob ( &tls->plainstream,
iobuf ) ) != 0 ) {
DBGC ( tls, "TLS %p could not deliver data: "
"%s\n", tls, strerror ( rc ) );
return rc;
}
}
return 0;
}
/* For all other records, merge into a single I/O buffer */
iobuf = iob_concatenate ( rx_data );
if ( ! iobuf ) {
DBGC ( tls, "TLS %p could not concatenate non-data record "
"type %d\n", tls, type );
return -ENOMEM_RX_CONCAT;
}
/* Determine handler */
/* Determine handler and fragment buffer */
iobuf = &tmp;
switch ( type ) {
case TLS_TYPE_CHANGE_CIPHER:
handler = tls_new_change_cipher;
@ -2538,19 +2570,44 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type,
break;
case TLS_TYPE_HANDSHAKE:
handler = tls_new_handshake;
iobuf = &tls->rx_handshake;
break;
default:
/* RFC4346 says that we should just ignore unknown
* record types.
*/
handler = NULL;
DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type );
DBGC ( tls, "TLS %p unknown record type %d\n", tls, type );
handler = tls_new_unknown;
break;
}
/* Handle record and free I/O buffer */
rc = ( handler ? handler ( tls, iobuf->data, iob_len ( iobuf ) ) : 0 );
free_iob ( iobuf );
/* Merge into a single I/O buffer */
if ( *iobuf )
list_add ( &(*iobuf)->list, rx_data );
*iobuf = iob_concatenate ( rx_data );
if ( ! *iobuf ) {
DBGC ( tls, "TLS %p could not concatenate non-data record "
"type %d\n", tls, type );
rc = -ENOMEM_RX_CONCAT;
goto err_concatenate;
}
/* Handle record */
if ( ( rc = handler ( tls, *iobuf ) ) != 0 )
goto err_handle;
/* Discard I/O buffer if empty */
if ( ! iob_len ( *iobuf ) ) {
free_iob ( *iobuf );
*iobuf = NULL;
}
/* Sanity check */
assert ( tmp == NULL );
return 0;
err_handle:
free_iob ( *iobuf );
*iobuf = NULL;
err_concatenate:
return rc;
}

View File

@ -601,6 +601,12 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
return;
}
/* Unregister any existing ProxyDHCP or PXEBS settings */
if ( ( settings = find_settings ( PROXYDHCP_SETTINGS_NAME ) ) != NULL )
unregister_settings ( settings );
if ( ( settings = find_settings ( PXEBS_SETTINGS_NAME ) ) != NULL )
unregister_settings ( settings );
/* Perform ProxyDHCP if applicable */
if ( dhcp->proxy_offer /* Have ProxyDHCP offer */ &&
( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ ) {

View File

@ -42,8 +42,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Generate entropy samples for external testing
*
* @v source Entropy source
*/
static void entropy_sample_test_exec ( void ) {
static void entropy_sample ( struct entropy_source *source ) {
static noise_sample_t samples[SAMPLE_BLOCKSIZE];
unsigned int i;
unsigned int j;
@ -53,22 +54,35 @@ static void entropy_sample_test_exec ( void ) {
for ( i = 0 ; i < ( SAMPLE_COUNT / SAMPLE_BLOCKSIZE ) ; i++ ) {
/* Collect one block of samples */
rc = entropy_enable();
rc = entropy_enable ( source );
ok ( rc == 0 );
for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) {
rc = get_noise ( &samples[j] );
rc = get_noise ( source, &samples[j] );
ok ( rc == 0 );
}
entropy_disable();
entropy_disable ( source );
/* Print out sample values */
for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) {
printf ( "SAMPLE %d %d\n", ( i * SAMPLE_BLOCKSIZE + j ),
samples[j] );
printf ( "SAMPLE %s %d %d\n", source->name,
( i * SAMPLE_BLOCKSIZE + j ), samples[j] );
}
}
}
/**
* Generate entropy samples for external testing
*
*/
static void entropy_sample_test_exec ( void ) {
struct entropy_source *source;
/* Test each entropy source */
for_each_table_entry ( source, ENTROPY_SOURCES ) {
entropy_sample ( source );
}
}
/** Entropy sampling self-test */
struct self_test entropy_sample_test __self_test = {
.name = "entropy_sample",

View File

@ -92,15 +92,17 @@ struct uri_churi_test {
const char *expected;
};
/** A form parameter URI test list */
/** A request parameter URI test list */
struct uri_params_test_list {
/** Key */
const char *key;
/** Value */
const char *value;
/** Flags */
unsigned int flags;
};
/** A form parameter URI test */
/** A request parameter URI test */
struct uri_params_test {
/** URI string */
const char *string;
@ -403,9 +405,9 @@ static void uri_churi_okx ( struct uri_churi_test *test, const char *file,
#define uri_churi_ok( test ) uri_churi_okx ( test, __FILE__, __LINE__ )
/**
* Report form parameter URI test list result
* Report request parameter URI test list result
*
* @v test Form parameter URI test
* @v test Request parameter URI test
* @v uri URI
* @v file Test code file
* @v line Test code line
@ -428,6 +430,7 @@ static void uri_params_list_okx ( struct uri_params_test *test,
file, line );
okx ( strcmp ( param->value, list->value ) == 0,
file, line );
okx ( param->flags == list->flags, file, line );
list++;
}
okx ( list->key == NULL, file, line );
@ -437,9 +440,9 @@ static void uri_params_list_okx ( struct uri_params_test *test,
uri_params_list_okx ( test, __FILE__, __LINE__ )
/**
* Report form parameter URI test result
* Report request parameter URI test result
*
* @v test Form parameter URI test
* @v test Request parameter URI test
* @v file Test code file
* @v line Test code line
*/
@ -456,7 +459,8 @@ static void uri_params_okx ( struct uri_params_test *test, const char *file,
okx ( params != NULL, file, line );
if ( params ) {
for ( list = test->list ; list->key ; list++ ) {
param = add_parameter ( params, list->key, list->value);
param = add_parameter ( params, list->key, list->value,
list->flags );
okx ( param != NULL, file, line );
}
}
@ -879,27 +883,31 @@ static struct uri_churi_test uri_churi[] = {
}
};
/** Form parameter URI test list */
/** Request parameter URI test list */
static struct uri_params_test_list uri_params_list[] = {
{
"vendor",
"10ec",
PARAMETER_FORM,
},
{
"device",
"8139",
PARAMETER_FORM,
},
{
"uuid",
"f59fac00-758f-498f-9fe5-87d790045d94",
PARAMETER_HEADER,
},
{
NULL,
NULL,
0,
}
};
/** Form parameter URI test */
/** Request parameter URI test */
static struct uri_params_test uri_params = {
"http://boot.ipxe.org/demo/boot.php##params",
{
@ -912,23 +920,26 @@ static struct uri_params_test uri_params = {
uri_params_list,
};
/** Named form parameter URI test list */
/** Named request parameter URI test list */
static struct uri_params_test_list uri_named_params_list[] = {
{
"mac",
"00:1e:65:80:d3:b6",
PARAMETER_FORM,
},
{
"serial",
"LXTQ20Z1139322762F2000",
PARAMETER_FORM,
},
{
NULL,
NULL,
0,
}
};
/** Named form parameter URI test */
/** Named request parameter URI test */
static struct uri_params_test uri_named_params = {
"http://192.168.100.4:3001/register##params=foo",
{
@ -996,7 +1007,7 @@ static void uri_test_exec ( void ) {
/* Current working URI tests */
uri_churi_ok ( uri_churi );
/* Form parameter URI tests */
/* Request parameter URI tests */
uri_params_ok ( &uri_params );
uri_params_ok ( &uri_named_params );
}

View File

@ -156,15 +156,21 @@ int imgacquire ( const char *name_uri, unsigned long timeout,
* @v image Executable/loadable image
*/
void imgstat ( struct image *image ) {
struct image_tag *tag;
printf ( "%s : %zd bytes", image->name, image->len );
if ( image->type )
printf ( " [%s]", image->type->name );
for_each_table_entry ( tag, IMAGE_TAGS ) {
if ( tag->image == image )
printf ( " [%s]", tag->name );
}
if ( image->flags & IMAGE_TRUSTED )
printf ( " [TRUSTED]" );
if ( image->flags & IMAGE_SELECTED )
printf ( " [SELECTED]" );
if ( image->flags & IMAGE_AUTO_UNREGISTER )
printf ( " [AUTOFREE]" );
if ( image->flags & IMAGE_HIDDEN )
printf ( " [HIDDEN]" );
if ( image->cmdline )
printf ( " \"%s\"", image->cmdline );
printf ( "\n" );

61
src/usr/shimmgmt.c Normal file
View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_shim.h>
#include <usr/shimmgmt.h>
/** @file
*
* EFI shim management
*
*/
/**
* Set shim image
*
* @v image Shim image, or NULL to clear shim
* @v require_loader Require use of a third party loader
* @v allow_pxe Allow use of PXE base code
* @v allow_sbat Allow SBAT variable access
* @ret rc Return status code
*/
int shim ( struct image *image, int require_loader, int allow_pxe,
int allow_sbat ) {
/* Record (or clear) shim image */
image_tag ( image, &efi_shim );
/* Avoid including image in constructed initrd */
if ( image )
image_hide ( image );
/* Record configuration */
efi_shim_require_loader = require_loader;
efi_shim_allow_pxe = allow_pxe;
efi_shim_allow_sbat = allow_sbat;
return 0;
}

View File

@ -168,6 +168,9 @@
*/
#define EFI_IMAGE_ALIGN 0x1000
/** Number of data directory entries */
#define NUMBER_OF_DIRECTORY_ENTRIES 8
struct elf_file {
void *data;
size_t len;
@ -178,6 +181,7 @@ struct pe_section {
struct pe_section *next;
EFI_IMAGE_SECTION_HEADER hdr;
void ( * fixup ) ( struct pe_section *section );
int hidden;
uint8_t contents[0];
};
@ -191,7 +195,6 @@ struct pe_relocs {
struct pe_header {
EFI_IMAGE_DOS_HEADER dos;
uint8_t padding[128];
EFI_IMAGE_NT_HEADERS nt;
};
@ -205,7 +208,11 @@ static struct pe_header efi_pe_header = {
.FileHeader = {
.TimeDateStamp = 0x10d1a884,
.SizeOfOptionalHeader =
sizeof ( efi_pe_header.nt.OptionalHeader ),
( sizeof ( efi_pe_header.nt.OptionalHeader ) -
( ( EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES -
NUMBER_OF_DIRECTORY_ENTRIES ) *
sizeof ( efi_pe_header.nt.OptionalHeader.
DataDirectory[0] ) ) ),
.Characteristics = ( EFI_IMAGE_FILE_DLL |
EFI_IMAGE_FILE_MACHINE |
EFI_IMAGE_FILE_EXECUTABLE_IMAGE ),
@ -218,15 +225,17 @@ static struct pe_header efi_pe_header = {
.FileAlignment = EFI_FILE_ALIGN,
.SizeOfImage = EFI_IMAGE_ALIGN,
.SizeOfHeaders = sizeof ( efi_pe_header ),
.NumberOfRvaAndSizes =
EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES,
.NumberOfRvaAndSizes = NUMBER_OF_DIRECTORY_ENTRIES,
},
},
};
/** Command-line options */
struct options {
/** PE32+ subsystem type */
unsigned int subsystem;
/** Create hybrid BIOS/UEFI binary */
int hybrid;
};
/**
@ -633,10 +642,12 @@ static struct pe_section * process_section ( struct elf_file *elf,
/* Update RVA limits */
start = new->hdr.VirtualAddress;
end = ( start + new->hdr.Misc.VirtualSize );
if ( ( ! *applicable_start ) || ( *applicable_start >= start ) )
*applicable_start = start;
if ( *applicable_end < end )
*applicable_end = end;
if ( ! new->hidden ) {
if ( ( ! *applicable_start ) || ( *applicable_start >= start ) )
*applicable_start = start;
if ( *applicable_end < end )
*applicable_end = end;
}
if ( data_start < code_end )
data_start = code_end;
if ( data_mid < data_start )
@ -656,8 +667,11 @@ static struct pe_section * process_section ( struct elf_file *elf,
( data_end - data_mid );
/* Update remaining file header fields */
pe_header->nt.FileHeader.NumberOfSections++;
pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr );
if ( ! new->hidden ) {
pe_header->nt.FileHeader.NumberOfSections++;
pe_header->nt.OptionalHeader.SizeOfHeaders +=
sizeof ( new->hdr );
}
pe_header->nt.OptionalHeader.SizeOfImage =
efi_image_align ( data_end );
@ -673,10 +687,12 @@ static struct pe_section * process_section ( struct elf_file *elf,
* @v nsyms Number of symbol table entries
* @v rel Relocation record
* @v pe_reltab PE relocation table to fill in
* @v opts Options
*/
static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
const Elf_Sym *syms, unsigned int nsyms,
const Elf_Rel *rel, struct pe_relocs **pe_reltab ) {
const Elf_Rel *rel, struct pe_relocs **pe_reltab,
struct options *opts ) {
unsigned int type = ELF_R_TYPE ( rel->r_info );
unsigned int sym = ELF_R_SYM ( rel->r_info );
unsigned int mrel = ELF_MREL ( elf->ehdr->e_machine, type );
@ -739,6 +755,15 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
* loaded.
*/
break;
case ELF_MREL ( EM_X86_64, R_X86_64_32 ) :
/* Ignore 32-bit relocations in a hybrid
* 32-bit BIOS and 64-bit UEFI binary,
* otherwise fall through to treat as an
* unknown type.
*/
if ( opts->hybrid )
break;
/* fallthrough */
default:
eprintf ( "Unrecognised relocation type %d\n", type );
exit ( 1 );
@ -753,9 +778,11 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
* @v shdr ELF section header
* @v stride Relocation record size
* @v pe_reltab PE relocation table to fill in
* @v opts Options
*/
static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr,
size_t stride, struct pe_relocs **pe_reltab ) {
size_t stride, struct pe_relocs **pe_reltab,
struct options *opts ) {
const Elf_Shdr *symtab;
const Elf_Sym *syms;
const Elf_Rel *rel;
@ -773,7 +800,7 @@ static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr,
rel = ( elf->data + shdr->sh_offset );
nrels = ( shdr->sh_size / stride );
for ( i = 0 ; i < nrels ; i++ ) {
process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab );
process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab, opts );
rel = ( ( ( const void * ) rel ) + stride );
}
}
@ -914,6 +941,7 @@ static void write_pe_file ( struct pe_header *pe_header,
FILE *pe ) {
struct pe_section *section;
unsigned long fpos = 0;
unsigned int count = 0;
/* Align length of headers */
fpos = pe_header->nt.OptionalHeader.SizeOfHeaders =
@ -931,19 +959,26 @@ static void write_pe_file ( struct pe_header *pe_header,
}
/* Write file header */
if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 1 ) {
if ( fwrite ( pe_header,
( offsetof ( typeof ( *pe_header ), nt.OptionalHeader ) +
pe_header->nt.FileHeader.SizeOfOptionalHeader ),
1, pe ) != 1 ) {
perror ( "Could not write PE header" );
exit ( 1 );
}
/* Write section headers */
for ( section = pe_sections ; section ; section = section->next ) {
if ( section->hidden )
continue;
if ( fwrite ( &section->hdr, sizeof ( section->hdr ),
1, pe ) != 1 ) {
perror ( "Could not write section header" );
exit ( 1 );
}
count++;
}
assert ( count == pe_header->nt.FileHeader.NumberOfSections );
/* Write sections */
for ( section = pe_sections ; section ; section = section->next ) {
@ -969,6 +1004,7 @@ static void write_pe_file ( struct pe_header *pe_header,
*
* @v elf_name ELF file name
* @v pe_name PE file name
* @v opts Options
*/
static void elf2pe ( const char *elf_name, const char *pe_name,
struct options *opts ) {
@ -1012,13 +1048,13 @@ static void elf2pe ( const char *elf_name, const char *pe_name,
/* Process .rel relocations */
process_relocs ( &elf, shdr, sizeof ( Elf_Rel ),
&pe_reltab );
&pe_reltab, opts );
} else if ( shdr->sh_type == SHT_RELA ) {
/* Process .rela relocations */
process_relocs ( &elf, shdr, sizeof ( Elf_Rela ),
&pe_reltab );
&pe_reltab, opts );
}
}
@ -1071,11 +1107,12 @@ static int parse_options ( const int argc, char **argv,
int option_index = 0;
static struct option long_options[] = {
{ "subsystem", required_argument, NULL, 's' },
{ "hybrid", no_argument, NULL, 'H' },
{ "help", 0, NULL, 'h' },
{ 0, 0, 0, 0 }
};
if ( ( c = getopt_long ( argc, argv, "s:h",
if ( ( c = getopt_long ( argc, argv, "s:Hh",
long_options,
&option_index ) ) == -1 ) {
break;
@ -1090,6 +1127,9 @@ static int parse_options ( const int argc, char **argv,
exit ( 2 );
}
break;
case 'H':
opts->hybrid = 1;
break;
case 'h':
print_help ( argv[0] );
exit ( 0 );