Compare commits

...

79 Commits

Author SHA1 Message Date
37850e0e85 [build] Fix build failures with random versions of gcc
For unknown reasons, miscellaneous versions of gcc seem to struggle
with the static assertions used to ensure the correct layout of the
GCM structures.

Adjust the assertions to use offsetof() rather than direct pointer
comparison, on the basis that offsetof() must be a compile-time
constant value.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-27 14:28:47 +00:00
9bbe77669c [efi] Extract basic network settings from loaded image device path
The UEFI HTTP boot mechanism is extraordinarily badly designed, even
by the standards of the UEFI specification in general.  It has the
symptoms of a feature that has been designed entirely in terms of user
stories, without any consideration at all being given to the
underlying technical architecture.  It does work, provided that you
are doing precisely and only what was envisioned by the product owner.
If you want to try anything outside the bounds of the product owner's
extremely limited imagination, then you are almost certainly about to
enter a world of pain.

As one very minor example of this: the cached DHCP packet is not
available when using HTTP boot.  The UEFI HTTP boot code does perform
DHCP, but it pointlessly and unhelpfully throws away the DHCP packet
and trashes the network interface configuration before handing over to
the downloaded executable.

Work around this imbecility by parsing and applying the few network
configuration settings that are persisted into the loaded image's
device path.  This is limited to very basic information such as the IP
address, gateway address, and DNS server address, but it does at least
provide enough for a functional routing table.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-26 15:57:58 +00:00
170bbfd487 [efi] Add efi_path_mac() to parse a MAC address from an EFI device path
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-26 15:16:33 +00:00
dcad73ca5a [efi] Add support for driving EFI_MANAGED_NETWORK_PROTOCOL devices
We want exclusive access to the network device, both for performance
reasons and because we perform operations such as EAPoL that affect
the entire link.  We currently drive the network card via either a
native hardware driver or via the SNP or NII/UNDI interfaces, both of
which grant us this exclusive access.

Add an alternative driver that drives the network card non-exclusively
via the EFI_MANAGED_NETWORK_PROTOCOL interface.  This can function as
a fallback for situations where neither SNP nor NII/UNDI interfaces
are functional, and also opens up the possibility of non-destructively
installing a temporary network device over which to download the
autoexec.ipxe script.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-25 17:58:33 +00:00
da5188f3ea [efi] Allow for drivers to be located via child handles
When using a service binding protocol, CreateChild() will create a new
protocol instance (and optionally a new handle).  The caller will then
typically open this new protocol instance with BY_DRIVER attributes,
since the service binding mechanism has no equivalent of the driver
binding protocol's Stop() method, and there is therefore no other way
for the caller to be informed if the protocol instance is about to
become invalid (e.g. because the service driver wants to remove the
child).

The caller cannot ask CreateChild() to install the new protocol
instance on the original handle (i.e. the service binding handle),
since the whole point of the service binding protocol is to allow for
the existence of multiple children, and UEFI does not permit multiple
instances of the same protocol to be installed on a handle.

Our current drivers all open the original handle (as passed to our
driver binding's Start() method) with BY_DRIVER attributes, and so the
same handle will be passed to our Stop() method.  This changes when
our driver must use a separate handle, as described above.

Add an optional "child handle" field to struct efi_device (on the
assumption that we will not have any drivers that need to create
multiple children), and generalise efidev_find() to match on either
the original handle or the child handle.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-25 17:58:33 +00:00
ca483a196c [efi] Add helper functions for service binding protocols
The EFI service binding abstraction is used to add and remove child
handles for multiple different protocols.  Provide a common interface
for doing so.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-25 17:58:33 +00:00
a15ce00182 [efi] Match chainloaded device by uppermost matching handle
Commit 4c5b794 ("[efi] Use the SNP protocol instance to match the SNP
chainloading device") switched the chainloaded device matching logic
to use a target protocol instance rather than the loaded image's
device handle, on the basis that we want to bind to the parent SNP
device rather than to a duplicate SNP protocol instance installed onto
an IPv4 or IPv6 child device handle.

It is possible that our calls to DisconnectController() and
ConnectController() will cause the target protocol instance to be
uninstalled and reinstalled, which may change the value of the
protocol instance pointer.  Allow for this by identifying and matching
against the uppermost handle that initially has this target protocol
instance installed.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-25 17:58:33 +00:00
390bce9516 [efi] Set current working URI from our own device path URI, if present
When booted via HTTP, our loaded image's device path will include the
URI from which we were downloaded.  Set this as the current working
URI, so that an embedded script may perform subsequent downloads
relative to the iPXE binary, or construct explicit relative paths via
the ${cwduri} setting.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-19 15:13:59 +00:00
1a84facf12 [efi] Add efi_path_uri() to parse a URI from an EFI device path
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-19 15:01:25 +00:00
88c2a01e1a [settings] Expose current working URI and directory URI via settings
iPXE maintains a concept of a current working URI, which is used when
resolving relative URIs and allows scripts to download files using
URIs relative to the script itself.

There are situations in which it is valuable for a script to be able
to access the URI explicitly as a string, not just implicitly as a
base URI for subsequent downloads.  For example, when booting a Fedora
installer, the "inst.repo" command-line parameter may be used to pass
the URI of the repository to the installer.

Expose the current working URI as ${cwuri}.  Since relative URIs may
be constructed as strings only from a directory URI (not from a full
URI), also expose the current working directory URI as ${cwduri}.

This feature may be used as e.g.

  #!ipxe
  echo Booting from ${cwuri}
  prompt -k 0x197e -t 2000 Press F12 to install Fedora... || exit
  kernel images/pxeboot/vmlinux inst.repo=${cwduri}
  initrd images/pxeboot/initrd.img
  boot

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-19 13:33:21 +00:00
926816c58f [efi] Pad transmit buffer length to work around vendor driver bugs
The Mellanox/Nvidia UEFI driver is built from the same codebase as the
iPXE driver, and appears to contain the bug that was fixed in commit
c11734e ("[golan] Use ETH_HLEN for inline header size").  This results
in identical failures when using the SNP or NII interface (via
e.g. snponly.efi) to drive a Mellanox card while EAPoL is enabled.

Work around the underlying UEFI driver bug by padding transmit I/O
buffers to the minimum Ethernet frame length before passing them to
the underlying driver's transmit function.

This padding is not technically necessary, since almost all modern
hardware will insert transmit padding as necessary (and where the
hardware does not support doing so, the underlying UEFI driver is
responsible for adding any necessary padding).  However, it is
guaranteed to be harmless (other than a miniscule performance impact):
the Ethernet specification requires zero padding up to the minimum
frame length for packets that are transmitted onto the wire, and so
the receiver will see the same packet whether or not we manually
insert this padding in software.

The additional padding causes the underlying Mellanox driver to avoid
its faulty code path, since it will never be asked to transmit a very
short packet.

Tested-by: Eric Hagberg <ehagberg@janestreet.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-18 22:52:05 +00:00
c11734eee0 [golan] Use ETH_HLEN for inline header size
The driver does not correctly handle very short transmitted packets
such as EAPoL-Start where the entire DMA content lies within the
current send work queue entry inline header length of 18 bytes.

Fix by reducing the inline header length to the Ethernet frame header
length of 14 bytes.

Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-17 22:55:32 +00:00
fa4bda617d [build] Fix building on older versions of gcc
Older versions of gcc (observed with gcc 4.8.5 on CentOS 7) complain
about having the label "err_ioremap" at the end of a compound
statement in bios_mp_start_all().  The label is correctly placed,
since it immediately follows the iounmap() that would be required to
undo a successful ioremap() in the non-error case.

Fix by adding an explicit "return" immediately after the label.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-17 17:49:05 +00:00
bac967d51a [snp] Allocate additional padding for receive buffers
Some SNP implementations (observed with a wifi adapter in a Dell
Latitude 3440 laptop) seem to require additional space in the
allocated receive buffers, otherwise full-length packets will be
silently dropped.

The EDK2 MnpDxe driver happens to allocate an additional 8 bytes of
padding (4 for a VLAN tag, 4 for the Ethernet frame checksum).  Match
this behaviour since drivers are very likely to have been tested
against MnpDxe.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-16 23:28:34 +00:00
17882e76af [ucode] Add support for updating x86 microcode
Intel and AMD distribute microcode updates, which are typically
applied by the BIOS and/or the booted operating system.

BIOS updates can be difficult to obtain and cumbersome to apply, and
are often neglected.  Operating system updates may be subject to
strict change control processes, particularly for production
workloads.  There is therefore value in being able to update the
microcode at boot time using a freshly downloaded microcode update
file, particularly in scenarios where the physical hardware and the
installed operating system are controlled by different parties (such
as in a public cloud infrastructure).

Add support for parsing Intel and AMD microcode update images, and for
applying the updates to all CPUs in the system.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-15 17:43:49 +00:00
1344e13a03 [bios] Provide a multiprocessor API for BIOS
Provide an implementation of the iPXE multiprocessor API for BIOS,
based on sending broadcast INIT and SIPI interprocessor interrupts to
start up all application processors.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-15 17:30:21 +00:00
a67f913d66 [librm] Add support for installing a startup IPI handler
Application processors are started via INIT and SIPI interprocessor
interrupts: the INIT places the processor into a "wait for SIPI"
state, and the SIPI then starts the processor in real mode at a
page-aligned address derived from the SIPI vector number.

Add support for installing a real-mode SIPI handler that will switch
the CPU into protected mode with flat physical addressing, load
initial register contents, and then jump to the address of a
protected-mode SIPI handler.  No stack pointer is set up, to avoid the
need to allocate stack space for each available processor.

We use 32-bit physical addressing in order to minimise the changes
required for a 64-bit build.  The existing long mode transition code
relies on the existence of the stack, so we cannot easily switch the
application processor into long mode.  We could use 32-bit virtual
addressing, but this runtime environment does not currently exist
outside of librm.S itself in a 64-bit build, and using it would
complicate the implementation of the protected-mode SIPI handler.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-15 13:27:06 +00:00
89bb926a04 [efi] Provide a multiprocessor API for EFI
Provide an implementation of the iPXE multiprocessor API for EFI,
based on using EFI_MP_SERVICES to start up a wrapper function on all
application processors.

Note that the processor numbers used by EFI_MP_SERVICES are opaque
integers that bear no relation to the underlying CPU identity
(e.g. the APIC ID), and so we must rely on our own (architecture-
specific) implementation to determine the relevant CPU identifiers.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-15 13:26:53 +00:00
1ab4d3079d [mp] Define an API for multiprocessor functions
Define an API for executing very limited functions on application
processors in a multiprocessor system, along with an x86-only
implementation.

The normal iPXE runtime environment is effectively non-existent on
application processors.  There is no ability to make firmware calls
(e.g. to write to a console), and there may be no stack space
available.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-15 13:26:53 +00:00
df2f23e333 [efi] Update to current EDK2 headers
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-13 14:48:44 +00:00
226531ed36 [ci] Update action versions to silence GitHub warnings
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-07 23:25:35 +00:00
06e229590c [efi] Do not report return status from efi_block_local()
The return status from efi_block_local() indicates whether or not the
handle is eligible to be assigned a local virtual drive number.  There
will always be several enumerated EFI_BLOCK_IO_PROTOCOL handles that
are not eligible for a local virtual drive number (e.g. the handles
corresponding to partitions, rather than to complete disks), and this
is not an interesting error to report.

Do not report errors from efi_block_local() as the overall error
status for a SAN boot, since doing so would be likely to mask a much
more relevant error from having previously attempted to scan for a
matching filesystem within an eligible block device handle.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-07 14:11:46 +00:00
24a855f1fc [block] Allow SAN boot device to be identified by filesystem label
Add a "--label" option that can be used to specify a filesystem label,
to be matched against the FAT volume label.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-07 14:11:46 +00:00
62b6d36335 [block] Allow SAN boot device to be identified by an extra filename
Add an "--extra" option that can be used to specify an extra
(non-boot) filename that must exist within the booted filesystem.

Note that only files within the FAT-formatted bootable partition will
be visible to this filter.  Files within the operating system's root
disk (e.g. "/etc/redhat-release") are not generally accessible to the
firmware and so cannot be used as the existence check filter filename.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-07 13:31:29 +00:00
cea22d76e4 [block] Allow SAN boot device to be identified by UUID
Add a "--uuid" option which may be used to specify a boot device UUID,
to be matched against the GPT partition GUID.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-06 16:11:29 +00:00
c4471e3408 [efi] Add efi_path_guid() utility function
EFI provides no API for determining the partition GUID (if any) for a
specified device handle.  The partition GUID appears to be exposed
only as part of the device path.

Add efi_path_guid() to extract the partition GUID (if any) from a
device path.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-06 16:11:29 +00:00
636ccb4ca5 [block] Allow for additional SAN boot parameters alongside filename
The drive specification alone does not necessarily contain enough
information to perform a SAN boot (or local disk boot) under UEFI.  If
the next-stage bootloader is installed in the EFI system partition
under a non-standard name (e.g. "\EFI\debian\grubx64.efi") then this
explicit boot filename must also be specified.

Generalise this concept to use a "SAN boot configuration parameters"
structure (currently containing only the optional explicit boot
filename), to allow for easy expansion to provide other parameters
such as the partition UUID or volume label.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-06 12:19:22 +00:00
b1c13cc43e [efi] Allow booting from local disks via the "sanboot" command
Extend the EFI SAN boot code to allow for booting from a local disk,
as is already possible with the BIOS SAN boot code.

There is unfortunately no direct UEFI equivalent of the BIOS drive
number.  The UEFI shell does provide numbered mappings fs0:, blk0:,
etc, but these numberings exist only while the UEFI shell is running
and are not necessarily stable between shell invocations or across
reboots.

A substantial amount of existing third-party documentation for iPXE
will suggest using "sanboot --drive 0x80" to boot from a local disk
(when no SAN drives are present), since this suggestion has been
present in the official documentation for the "sanboot" command for
almost thirteen years.  We therefore aim to ensure that this
instruction will also work for UEFI, i.e. that in a situation where
there are local disks but no SAN disks, then the first local disk will
be treated as being drive 0x80.

We therefore assign local disks the virtual drive numbers 0x80, 0x81,
etc, matching the numbering typically used in a BIOS environment.
Where a SAN disk is already occupying one of these drive numbers, the
local disks' virtual drive numbers will be incremented as necessary.
This provides a rough approximation of the equivalent functionality
under BIOS, where existing local disks' drive numbers are remapped to
make way for SAN disks.

We do not make any attempt to sort the list of local disks: the order
used for allocating virtual drive numbers will be whatever order is
returned by LocateHandle().  This will typically match the creation
order of the EFI handles, which will typically match the hardware
enumeration order of the devices, which will typically match user
expectations as to which local disk is first, second, etc.

We explicitly do not attempt to match the numbering used by the UEFI
shell (which initially sorts in increasing order of device path, but
does not renumber when new devices are added or removed).  We can
never guarantee matching this partly transient UEFI shell numbering,
so it is best not to set any expectation that it will be matched.
(Using local drive numbers starting at 0x80 helps to avoid setting up
this impossible expectation, since the UEFI shell uses local drive
numbers starting at zero.)

Since floppy disks are essentially non-existent in any plausible UEFI
system, overload "--drive 0" to mean "boot from any drive containing
the specified (or default) boot filename".

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-05 13:25:35 +00:00
8da22a59ee [block] Allow for iteration over SAN device list in drive number order
Maintain the SAN device list in order of drive number, and provide
sandev_next() to locate the first SAN device at or above a given drive
number.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-05 13:17:35 +00:00
37edfea72b [efi] Generalise block device boot to support arbitrary EFI handles
SAN devices created by iPXE are visible to the firmware, and may be
accessed using the firmware's standard block I/O device interface
(e.g. INT 13 for BIOS, or EFI_BLOCK_IO_PROTOCOL for UEFI).  The iPXE
code to perform a SAN boot acts as a client of this standard block I/O
device interface, even when the underlying block I/O is being
performed by iPXE itself.

We rely on this separation to allow the "sanboot" command to be used
to boot from a local disk: since the code to perform a SAN boot does
not need direct access to an underlying iPXE SAN device, it may be
used to boot from any device providing the firmware's standard block
I/O device interface.

Clean up the EFI SAN boot code to require only a drive number and an
EFI_BLOCK_IO_PROTOCOL handle, in preparation for adding support for
booting from a local disk under UEFI.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-04 15:25:25 +00:00
eb720d2224 [efi] Use file system protocol to check for SAN boot filename existence
The "sanboot" command allows a custom boot filename to be specified
via the "--filename" option.  We currently rely on LoadImage() to
perform both the existence check and to load the image ready for
execution.  This may give a false negative result if Secure Boot is
enabled and the boot file is not correctly signed.

Carry out the existence check using EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
separately from loading the image via LoadImage().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-04 14:57:25 +00:00
75c7904482 [block] Use drive number as debug message stream ID
We currently use the SAN device pointer as the debug message stream
identifier.  This pointer is not always available: for example, when
booting from a local disk there is no underlying SAN device.

Switch to using the drive number as the debug message colour stream
identifier, so that all block device debug messages may be colourised
consistently.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-04 12:50:25 +00:00
1b23d4de25 [efi] Use long forms of device paths in debug messages
We currently call ConvertDevicePathToText() with DisplayOnly=TRUE when
constructing a device path to appear within a debug message.  For
ATAPI device paths, this will unfortunately omit some key information:
the textual representation will not indicate which ATA bus or drive is
represented.  This can lead to misleading debug messages that appear
to refer to identical devices.

Fix by setting DisplayOnly=FALSE to select the long form of device
path textual representations.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-04 12:15:20 +00:00
7cd73884e5 [parseopt] Add parse_uuid() for parsing UUID command-line arguments
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-29 15:20:44 +00:00
0eb8fbd0bf [settings] Add parsing for UUID and GUID settings types
The ":uuid" and ":guid" settings types are currently format-only: it
is possible to format a setting as a UUID (via e.g. "show foo:uuid")
but it is not currently possible to parse a string into a UUID setting
(via e.g. "set foo:uuid 406343fe-998b-44be-8a28-44ca38cb202b").

Use uuid_aton() to implement parsing of these settings types, and add
appropriate test cases for both.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-29 15:20:44 +00:00
da7b266289 [uuid] Add uuid_aton() to parse a UUID from a string
Add uuid_aton() to parse a UUID value from a string (analogous to
inet_aton(), inet6_aton(), sock_aton(), etc), treating it as a
32-digit hex string with optional hyphen separators.  The placement of
the separators is not checked: each byte within the hex string may be
separated by a hyphen, or not separated at all.

Add dedicated self-tests for UUID parsing and formatting (already
partially covered by the ":uuid" and ":guid" settings self-tests).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-29 14:43:55 +00:00
182ee90931 [efi] Work around broken boot services table manipulation by UEFI shim
The UEFI shim installs wrappers around several boot services functions
before invoking its next stage bootloader, in an attempt to enforce
its desired behaviour upon the aforementioned bootloader.  For
example, shim checks that the bootloader has either invoked
StartImage() or has called into the "shim lock protocol" before
allowing an ExitBootServices() call to proceed.

When invoking a shim, iPXE will also install boot services function
wrappers in order to work around assorted bugs in the UEFI shim code
that would otherwise prevent it from being used to boot a kernel.  For
details on these workarounds, see commits 28184b7 ("[efi] Add support
for executing images via a shim") and 5b43181 ("[efi] Support versions
of shim that perform SBAT verification").

Using boot services function wrappers in this way is not intrinsically
problematic, provided that wrappers are installed before starting the
wrapped program, and uninstalled only after the wrapped program exits.
This strict ordering requirement ensures that all layers of wrappers
are called in the expected order, and that no calls are issued through
a no-longer-valid function pointer.

Unfortunately, the UEFI shim does not respect this strict ordering
requirement, and will instead uninstall (and reinstall) its wrappers
midway through the execution of the wrapped program.  This leaves the
wrapped program with an inconsistent view of the boot services table,
leading to incorrect behaviour.

This results in a boot failure when a first shim is used to boot iPXE,
which then uses a second shim to boot a Linux kernel:

  - First shim installs StartImage() and ExitBootServices() wrappers

  - First shim invokes iPXE via its own PE loader

  - iPXE installs ExitBootServices() wrapper

  - iPXE invokes second shim via StartImage()

At this point, the first shim's StartImage() wrapper will illegally
uninstall its ExitBootServices() wrapper, without first checking that
nothing else has modified the ExitBootServices function pointer.  This
effectively bypasses iPXE's own ExitBootServices() wrapper, which
causes a boot failure since the code within that wrapper does not get
called.

A proper fix would be for shim to install its wrappers before starting
the image and uninstall its wrappers only after the started image has
exited.  Instead of repeatedly uninstalling and reinstalling its
wrappers while the wrapped program is running, shim should simply use
a flag to keep track of whether or not it needs to modify the
behaviour of the wrapped calls.

Experience shows that there is unfortunately no point in trying to get
a fix for this upstreamed into shim.  We therefore work around the
shim bug by removing our ExitBootServices() wrapper and moving the
relevant code into our GetMemoryMap() wrapper.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-27 14:38:41 +00:00
43e385091a [eap] Add support for the MS-CHAPv2 authentication method
Add support for EAP-MSCHAPv2 (note that this is not the same as
PEAP-MSCHAPv2), controllable via the build configuration option
EAP_METHOD_MSCHAPV2 in config/general.h.

Our model for EAP does not encompass mutual authentication: we will
starting sending plaintext packets (e.g. DHCP requests) over the link
even before EAP completes, and our only use for an EAP success is to
mark the link as unblocked.

We therefore ignore the content of the EAP-MSCHAPv2 success request
(containing the MS-CHAPv2 authenticator response) and just send back
an EAP-MSCHAPv2 success response, so that the EAP authenticator will
complete the process and send through the real EAP success packet
(which will, in turn, cause us to unblock the link).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-23 16:37:07 +00:00
25ffcd79bf [eap] Allow MD5-Challenge authentication method to be disabled
RFC 3748 states that implementations must support the MD5-Challenge
method.  However, some network environments may wish to disable it as
a matter of policy.

Allow support for MD5-Challenge to be controllable via the build
configuration option EAP_METHOD_MD5 in config/general.h.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-23 16:24:44 +00:00
834f319f87 [eap] Add progress debug messages
Add debug messages for each EAP Request and Response, and to show the
list of methods offered when sending a Nak.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-23 16:17:07 +00:00
ee6185dcf5 [efi] Ignore new LoongArch PC-relative relocations and relaxations
Several new relocations types have been added in LoongArch ABI version
2.10.  In particular:

- R_LARCH_B16 (18-bit PC-relative jump)
- R_LARCH_B21 (23-bit PC-relative jump)
- R_LARCH_PCREL20_S2 (22-bit PC-relative offset)

Also relocation relaxations have been introduced.  Recent GCC (13.2)
and binutils 2.41+ use these types of relocations, which confuses
elf2efi tool.  As a result, iPXE EFI images for LoongArch fail to
build with the following error:

  Unrecognised relocation type 103

Fix by ignoring R_LARCH_B{16,21} and R_LARCH_PCREL20_S2 (as with other
PC-relative relocations), and by ignoring relaxations (R_LARCH_RELAX).
Relocation relaxations are basically optimizations: ignoring them
results in a correct binary (although it might be suboptimal).

Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-22 14:40:53 +00:00
e5f3ba0ca7 [drivers] Sort PCI_ROM() entries numerically
Done with the help of this Perl script:

$MARKER = 'PCI_ROM';  # a regex
$AB = 1;  # At Begin
@HEAD = ();
@ITEMS = ();
@TAIL = ();

foreach $fn (@ARGV) {
    open(IN, $fn) or die "Can't open file '$fn': $!\n";
    while (<IN>) {
        if (/$MARKER/) {
            push @ITEMS, $_;
            $AB = 0;  # not anymore at begin
        }
        else {
            if ($AB) {
                push @HEAD, $_;
            }
            else {
                push @TAIL, $_;
            }
        }
    }
} continue {
    close IN;
    open(OUT, ">$fn") or die "Can't open file '$fn' for output: $!\n";
    print OUT @HEAD;
    print OUT sort @ITEMS;
    print OUT @TAIL;
    close OUT;
    # For a next file
    $AB = 1;
    @HEAD = ();
    @ITEMS = ();
    @TAIL = ();
}

Executed that script while src/drivers/ as current working directory,
provided '$(grep -rl PCI_ROM)' as argument.

Signed-off-by: Geert Stappers <stappers@stappers.it>
2024-02-22 14:19:04 +00:00
582132fe3f [crypto] Force inlining of trivial wrapper functions
Inspection of the generated assembly shows that gcc will often emit
standalone implementations of frequently invoked functions such as
digest_update(), which contain no logic and exist only as syntactic
sugar.

Force inlining of these functions to reduce the overall binary size.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-22 12:55:59 +00:00
075292cc2d [crypto] Add implementation of MS-CHAPv2 authentication
Add an implementation of the authentication portions of the MS-CHAPv2
algorithm as defined in RFC 2759, along with the single test vector
provided therein.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-22 00:08:27 +00:00
929f06a76d [crypto] Allow for multiple cross-signed certificate download attempts
Certificates issued by Let's Encrypt have two options for their chain
of trust: the chain can either terminate in the self-signed ISRG Root
X1 root certificate, or in an intermediate ISRG Root X1 certificate
that is signed in turn by the self-signed DST Root CA X3 root
certificate.  This is a historical artifact: when Let's Encrypt first
launched as a project, the chain ending in DST Root CA X3 was used
since existing clients would not have recognised the ISRG Root X1
certificate as a trusted root certificate.

The DST Root CA X3 certificate expired in September 2021, and so is no
longer trusted by clients (such as iPXE) that validate the expiry
times of all certificates in the certificate chain.

In order to maintain usability of certificates on older Android
devices, the default certificate chain provided by Let's Encrypt still
terminates in DST Root CA X3, even though that certificate has now
expired.  On newer devices which include ISRG Root X1 as a trusted
root certificate, the intermediate version of ISRG Root X1 in the
certificate chain is ignored and validation is performed as though the
chain had terminated in the self-signed ISRG Root X1 root certificate.
On older Android devices which do not include ISRG Root X1 as a
trusted root certificate, the validation succeeds since Android
chooses to ignore expiry times for root certificates and so continues
to trust the DST Root CA X3 root certificate.

This backwards compatibility hack unfortunately breaks the cross-
signing mechanism used by iPXE, which assumes that the certificate
chain will always terminate in a non-expired root certificate.

Generalise the validator's cross-signed certificate download mechanism
to walk up the certificate chain in the event of a failure, attempting
to find a replacement cross-signed certificate chain starting from the
next level up.  This allows the validator to step over the expired
(and hence invalidatable) DST Root CA X3 certificate, and instead
download the cross-signed version of the ISRG Root X1 certificate.

This generalisation also gives us the ability to handle servers that
provide a full certificate chain including their root certificate:
iPXE will step over the untrusted public root certificate and attempt
to find a cross-signed version of it instead.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-15 13:13:08 +00:00
943d75b557 [crypto] Add x509_is_self_signed() helper function
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-15 12:45:58 +00:00
3e721e0c08 [crypto] Add x509_truncate() to truncate a certificate chain
Downloading a cross-signed certificate chain to partially replace
(rather than simply extend) an existing chain will require the ability
to discard all certificates after a specified link in the chain.

Extract the relevant logic from x509_free_chain() and expose it
separately as x509_truncate().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-14 16:40:05 +00:00
e10dfe5dc7 [list] Add list_for_each_entry_safe_continue()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-14 16:40:05 +00:00
88b291d647 [list] Add list_is_head_entry()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-14 16:25:21 +00:00
94b39fbe92 [build] Fix build failures with older versions of gcc
Some versions of gcc (observed with gcc 4.8.5 in CentOS 7) will report
spurious build_assert() failures for some assertions about structure
layouts.  There is no clear pattern as to what causes these spurious
failures, and the build assertion does succeed in that no unresolvable
symbol reference is generated in the compiled code.

Adjust the assertions to work around these apparent compiler issues.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-10 14:48:56 +00:00
0f5abd8b11 [libc] Allow build_assert() failures to be ignored via NO_WERROR=1
We build with -Werror by default so that any warning is treated as an
error and aborts the build.  The build system allows NO_WERROR=1 to be
used to override this behaviour, in order to allow builds to succeed
when spurious warnings occur (e.g. when using a newer compiler that
includes checks for which the codebase is not yet prepared).

Some versions of gcc (observed with gcc 4.8.5 in CentOS 7) will report
spurious build_assert() failures: the compilation will fail due to an
allegedly unelided call to the build assertion's external function
declared with __attribute__((error)) even though the compiler does
manage to successfully elide the call (as verified by the fact that
there are no unresolvable symbol references in the compiler output).

Change build_assert() to declare __attribute__((warning)) instead of
__attribute__((error)) on its extern function.  This will still abort
a normal build if the assertion fails, but may be overridden using
NO_WERROR=1 if necessary to work around a spurious assertion failure.

Note that if the build assertion has genuinely failed (i.e. if the
compiler has genuinely not been able to elide the call) then the
object will still contain an unresolvable symbol reference that will
cause the link to fail (which matches the behaviour of the old
linker_assert() mechanism).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-10 14:47:40 +00:00
a846c4ccfc [bnxt] Add support for BCM957608
Add support for BCM957608 device.  Add support for additional link
speeds supported by BCM957608.

Signed-off-by: Joseph Wong <joseph.wong@broadcom.com>
2024-02-08 15:10:12 +00:00
e7ae51b0d7 [crypto] Add implementation of the DES cipher
The DES block cipher dates back to the 1970s.  It is no longer
relevant for use in TLS cipher suites, but it is still used by the
MS-CHAPv2 authentication protocol which remains unfortunately common
for 802.1x port authentication.

Add an implementation of the DES block cipher, complete with the
extremely comprehensive test vectors published by NBS (the precursor
to NIST) in the form of an utterly adorable typewritten and hand-drawn
paper document.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-07 23:32:10 +00:00
af4583b214 [test] Remove dummy initialisation vector for ECB-mode AES tests
A block cipher in ECB mode has no concept of an initialisation vector,
and any data provided to cipher_setiv() for an ECB cipher will be
ignored.  There is no requirement within our cipher algorithm
abstraction for a dummy initialisation vector to be provided.

Remove the entirely spurious dummy 16-byte initialisation vector from
the ECB test cases.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-07 21:16:47 +00:00
36a27b22b1 [crypto] Fix stray references to AES
The CBC_CIPHER() macro contains some accidentally hardcoded references
to an underlying AES cipher, instead of using the cipher specified in
the macro parameters.

Fix by using the macro parameter as required.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-02-02 17:09:06 +00:00
0cc0f47443 [tls] Tidy up error handling flow in tls_send_plaintext()
Coverity reported that tls_send_plaintext() failed to check the return
status from tls_generate_random(), which could potentially result in
uninitialised random data being used as the block initialisation
vector (instead of intentionally random data).

Add the missing return status check, and separate out the error
handling code paths (since on the successful exit code path there will
be no need to free either the plaintext or the ciphertext anyway).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-31 13:49:35 +00:00
65d69d33da [efi] Fix hang during ExitBootServices()
When ExitBootServices() invokes efi_shutdown_hook(), there may be
nothing to generate an interrupt since the timer is disabled in the
first step of ExitBootServices().  Additionally, for VMs OVMF masks
everything from the PIC (except the timer) by default.  This means
that calling cpu_nap() may hang indefinitely.  This was seen in
practice in netfront_reset() when running in a VM on XenServer.

Fix this by skipping the halt if an EFI shutdown is in progress.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-31 13:23:56 +00:00
963ec1c4f3 [tls] Add ECDHE cipher suites
Add ECDHE variants of the existing cipher suites, and lower the
priority of the non-ECDHE variants.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-31 12:34:20 +00:00
8f6a9399b3 [tls] Make key exchange algorithms selectable via build configuration
Allow the choice of key exchange algorithms to be controlled via build
configuration options in config/crypto.h, as is already done for the
choices of public-key algorithms, cipher algorithms, and digest
algorithms.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 18:01:44 +00:00
a881a26061 [crypto] Add X25519 OID-identified algorithm and TLS named curve
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 18:01:44 +00:00
b234226dbc [tls] Add support for Ephemeral Elliptic Curve Diffie-Hellman key exchange
Add support for the Ephemeral Elliptic Curve Diffie-Hellman (ECDHE)
key exchange algorithm.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 16:16:31 +00:00
8e2469c861 [tls] Split out Diffie-Hellman parameter signature verification
DHE and ECDHE use essentially the same mechanism for verifying the
signature over the Diffie-Hellman parameters, though the format of the
parameters is different between the two methods.

Split out the verification of the parameter signature so that it may
be shared between the DHE and ECDHE key exchange algorithms.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 16:06:45 +00:00
989dbe0bc4 [tls] Generate key material after sending ClientKeyExchange
The construction of the key material for the pending cipher suites
from the TLS master secret must happen regardless of which key
exchange algorithm is in use, and the key material is not required to
send the ClientKeyExchange handshake (which is sent before changing
cipher suites).

Centralise the call to tls_generate_keys() after performing key
exchange via the selected algorithm.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 15:25:38 +00:00
6f70e8be83 [tls] Restructure construction of ClientHello message
Define an individual local structure for each extension and a single
structure for the list of extensions.  This makes it viable to add
extensions such as the Supported Elliptic Curves extension, which must
not be present if the list of curves is empty.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 13:38:15 +00:00
17135c83fb [crypto] Add an abstraction of an elliptic curve
Define an abstraction of an elliptic curve with a fixed generator and
one supported operation (scalar multiplication of a curve point).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 13:26:36 +00:00
27398f1360 [crypto] Check for all-zeros result from X25519 key exchange
RFC7748 states that it is entirely optional for X25519 Diffie-Hellman
implementations to check whether or not the result is the all-zero
value (indicating that an attacker sent a malicious public key with a
small order).  RFC8422 states that implementations in TLS must abort
the handshake if the all-zero value is obtained.

Return an error if the all-zero value is obtained, so that the TLS
code will not require knowledge specific to the X25519 curve.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-30 13:21:01 +00:00
de8a0821c7 [bnxt] Add support for additional chip IDs
Add additional chip IDs that can be recognized as part of the thor
family.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-19 22:08:48 +00:00
2eea04c02c [crypto] Add X25519 key exchange algorithm
Add an implementation of the X25519 key exchange algorithm as defined
in RFC7748.

This implementation is inspired by and partially based upon the paper
"Implementing Curve25519/X25519: A Tutorial on Elliptic Curve
Cryptography" by Martin Kleppmann, available for download from
https://www.cl.cam.ac.uk/teaching/2122/Crypto/curve25519.pdf

The underlying modular addition, subtraction, and multiplication
operations are completely redesigned for substantially improved
efficiency compared to the TweetNaCl implementation studied in that
paper (approximately 5x-10x faster and with 70% less memory usage).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-19 16:44:30 +00:00
908174ec7e [loong64] Replace broken big integer arithmetic implementations
The slightly incomprehensible LoongArch64 implementation for
bigint_subtract() is observed to produce incorrect results for some
input values.

Replace the suspicious LoongArch64 implementations of bigint_add(),
bigint_subtract(), bigint_rol() and bigint_ror(), and add a test case
for a subtraction that was producing an incorrect result with the
previous implementation.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-19 16:40:11 +00:00
bac13ba1f6 [crypto] Add bigint_swap() to conditionally swap big integers
Add a helper function bigint_swap() that can be used to conditionally
swap a pair of big integers in constant time.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-19 12:34:02 +00:00
13e390d54e [crypto] Add bigint_copy() as a convenient wrapper macro
Big integers may be efficiently copied using bigint_shrink() (which
will always copy only the size of the destination integer), but this
is potentially confusing to a reader of the code.

Provide bigint_copy() as an alias for bigint_shrink() so that the
intention of the calling code may be more obvious.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-19 12:29:29 +00:00
26d3ef062b [crypto] Allow multiplicand and multiplier to differ in size
Big integer multiplication is currently used only as part of modular
exponentiation, where both multiplicand and multiplier will be the
same size.

Relax this requirement to allow for the use of big integer
multiplication in other contexts.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-16 16:09:16 +00:00
4b7d9a6af0 [libc] Replace linker_assert() with build_assert()
We currently implement build-time assertions via a mechanism that
generates a call to an undefined external function that will cause the
link to fail unless the compiler can prove that the asserted condition
is true (and thereby eliminate the undefined function call).

This assertion mechanism can be used for conditions that are not
amenable to the use of static_assert(), since static_assert() will not
allow for proofs via dead code elimination.

Add __attribute__((error(...))) to the undefined external function, so
that the error is raised at compile time rather than at link time.
This allows us to provide a more meaningful error message (which will
include the file name and line number, as with any other compile-time
error), and avoids the need for the caller to specify a unique symbol
name for the external function.

Change the name from linker_assert() to build_assert(), since the
assertion now takes place at compile time rather than at link time.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-16 13:35:08 +00:00
6d29415c89 [libc] Make static_assert() available via assert.h
Expose static_assert() via assert.h and migrate link-time assertions
to build-time assertions where possible.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-16 13:35:08 +00:00
6ca597eee9 [build] Fix building with newer binutils
Newer versions of the GNU assembler (observed with binutils 2.41) will
complain about the ".arch i386" in files assembled with "as --64",
with the message "Error: 64bit mode not supported on 'i386'".

In files such as stack.S that contain no instructions to be assembled,
the ".arch i386" is redundant and may be removed entirely.

In the remaining files, fix by moving ".arch i386" below the relevant
".code16" or ".code32" directive, so that the assembler is no longer
expecting 64-bit instructions to be used by the time that the ".arch
i386" directive is encountered.

Reported-by: Ali Mustakim <alim@forwardcomputers.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-14 12:16:02 +00:00
e66552eeed [build] Remove unnecessary ".text" directives
The .text directive is entirely redundant when followed by a .section
directive giving an explicit section name and attributes.

Remove these unnecessary directives to simplify the code.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-14 11:51:16 +00:00
08fcb0e8fb [eap] Add support for the MD5-Challenge authentication type
RFC 3748 states that support for MD5-Challenge is mandatory for EAP
implementations.  The MD5 and CHAP code is already included in the
default build since it is required by iSCSI, and so this does not
substantially increase the binary size.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-10 16:22:32 +00:00
c6226f104e [eap] Add support for sending an EAP identity
Allow the ${netX/username} setting to be used to specify an EAP
identity to be returned in response to a Request-Identity, and provide
a mechanism for responding with a NAK to indicate which authentication
types we support.

If no identity is specified then fall back to the current behaviour of
not sending any Request-Identity response, so that switches will time
out and switch to MAC Authentication Bypass (MAB) if applicable.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-10 16:03:10 +00:00
0abb3e85e5 [eap] Ignore any received EAP responses
EAP responses (including our own) may be broadcast by switches but are
not of interest to us and can be safely ignored if received.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-10 16:02:32 +00:00
203 changed files with 13265 additions and 1448 deletions

View File

@ -12,7 +12,7 @@ jobs:
run: |
sudo chown $(id -un) /var/cache/apt/archives
- name: Cache packages
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache-${{ github.run_id }}-${{ github.run_attempt }}
@ -32,14 +32,14 @@ jobs:
needs: cache
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache permissions
run: |
sudo chown $(id -un) /var/cache/apt/archives
- name: Cache packages
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache-${{ github.run_id }}-${{ github.run_attempt }}
@ -68,14 +68,14 @@ jobs:
needs: cache
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache permissions
run: |
sudo chown $(id -un) /var/cache/apt/archives
- name: Cache packages
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache-${{ github.run_id }}-${{ github.run_attempt }}
@ -97,14 +97,14 @@ jobs:
needs: cache
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache permissions
run: |
sudo chown $(id -un) /var/cache/apt/archives
- name: Cache packages
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache-${{ github.run_id }}-${{ github.run_attempt }}

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Download Coverity Scan
run: |
curl --form token=${{ secrets.COVERITY_SCAN_TOKEN }} \

View File

@ -23,9 +23,9 @@ NON_AUTO_MEDIA += efidrv
NON_AUTO_MEDIA += drv.efi
NON_AUTO_MEDIA += efirom
# Include SNP driver in the all-drivers build
# Include SNP and MNP drivers in the all-drivers build
#
DRIVERS_net += snp
DRIVERS_net += snp mnp
# Rules for building EFI files
#

View File

@ -0,0 +1,12 @@
#ifndef _BITS_MP_H
#define _BITS_MP_H
/** @file
*
* ARM-specific multiprocessor API implementation
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_MP_H */

View File

@ -46,8 +46,12 @@ static void efiarm_cpu_nap ( void ) {
* The EFI shell doesn't seem to bother sleeping the CPU; it
* just sits there idly burning power.
*
* If a shutdown is in progess, there may be nothing to
* generate an interrupt since the timer is disabled in the
* first step of ExitBootServices().
*/
__asm__ __volatile__ ( "wfi" );
if ( ! efi_shutdown_in_progress )
__asm__ __volatile__ ( "wfi" );
}
PROVIDE_NAP ( efiarm, cpu_nap, efiarm_cpu_nap );

View File

@ -36,19 +36,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Multiply big integers
*
* @v multiplicand0 Element 0 of big integer to be multiplied
* @v multiplicand_size Number of elements in multiplicand
* @v multiplier0 Element 0 of big integer to be multiplied
* @v multiplier_size Number of elements in multiplier
* @v result0 Element 0 of big integer to hold result
* @v size Number of elements
*/
void bigint_multiply_raw ( const uint32_t *multiplicand0,
unsigned int multiplicand_size,
const uint32_t *multiplier0,
uint32_t *result0, unsigned int size ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
( ( const void * ) multiplicand0 );
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
( ( const void * ) multiplier0 );
bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result =
( ( void * ) result0 );
unsigned int multiplier_size,
uint32_t *result0 ) {
unsigned int result_size = ( multiplicand_size + multiplier_size );
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
*multiplicand = ( ( const void * ) multiplicand0 );
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
*multiplier = ( ( const void * ) multiplier0 );
bigint_t ( result_size ) __attribute__ (( may_alias ))
*result = ( ( void * ) result0 );
unsigned int i;
unsigned int j;
uint32_t multiplicand_element;
@ -62,9 +66,9 @@ void bigint_multiply_raw ( const uint32_t *multiplicand0,
memset ( result, 0, sizeof ( *result ) );
/* Multiply integers one element at a time */
for ( i = 0 ; i < size ; i++ ) {
for ( i = 0 ; i < multiplicand_size ; i++ ) {
multiplicand_element = multiplicand->element[i];
for ( j = 0 ; j < size ; j++ ) {
for ( j = 0 ; j < multiplier_size ; j++ ) {
multiplier_element = multiplier->element[j];
result_elements = &result->element[ i + j ];
/* Perform a single multiply, and add the
@ -73,7 +77,7 @@ void bigint_multiply_raw ( const uint32_t *multiplicand0,
* never overflow beyond the end of the
* result, since:
*
* a < 2^{n}, b < 2^{n} => ab < 2^{2n}
* a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
*/
__asm__ __volatile__ ( "umull %1, %2, %5, %6\n\t"
"ldr %3, [%0]\n\t"

View File

@ -310,7 +310,9 @@ bigint_done_raw ( const uint32_t *value0, unsigned int size __unused,
}
extern void bigint_multiply_raw ( const uint32_t *multiplicand0,
unsigned int multiplicand_size,
const uint32_t *multiplier0,
uint32_t *value0, unsigned int size );
unsigned int multiplier_size,
uint32_t *value0 );
#endif /* _BITS_BIGINT_H */

View File

@ -1,7 +1,6 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", %progbits
.text
.thumb
/**

View File

@ -1,7 +1,6 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", %progbits
.text
.arm
/**

View File

@ -36,19 +36,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Multiply big integers
*
* @v multiplicand0 Element 0 of big integer to be multiplied
* @v multiplicand_size Number of elements in multiplicand
* @v multiplier0 Element 0 of big integer to be multiplied
* @v multiplier_size Number of elements in multiplier
* @v result0 Element 0 of big integer to hold result
* @v size Number of elements
*/
void bigint_multiply_raw ( const uint64_t *multiplicand0,
unsigned int multiplicand_size,
const uint64_t *multiplier0,
uint64_t *result0, unsigned int size ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
( ( const void * ) multiplicand0 );
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
( ( const void * ) multiplier0 );
bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result =
( ( void * ) result0 );
unsigned int multiplier_size,
uint64_t *result0 ) {
unsigned int result_size = ( multiplicand_size + multiplier_size );
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
*multiplicand = ( ( const void * ) multiplicand0 );
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
*multiplier = ( ( const void * ) multiplier0 );
bigint_t ( result_size ) __attribute__ (( may_alias ))
*result = ( ( void * ) result0 );
unsigned int i;
unsigned int j;
uint64_t multiplicand_element;
@ -63,9 +67,9 @@ void bigint_multiply_raw ( const uint64_t *multiplicand0,
memset ( result, 0, sizeof ( *result ) );
/* Multiply integers one element at a time */
for ( i = 0 ; i < size ; i++ ) {
for ( i = 0 ; i < multiplicand_size ; i++ ) {
multiplicand_element = multiplicand->element[i];
for ( j = 0 ; j < size ; j++ ) {
for ( j = 0 ; j < multiplier_size ; j++ ) {
multiplier_element = multiplier->element[j];
result_elements = &result->element[ i + j ];
/* Perform a single multiply, and add the
@ -74,7 +78,7 @@ void bigint_multiply_raw ( const uint64_t *multiplicand0,
* never overflow beyond the end of the
* result, since:
*
* a < 2^{n}, b < 2^{n} => ab < 2^{2n}
* a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
*/
__asm__ __volatile__ ( "mul %1, %6, %7\n\t"
"umulh %2, %6, %7\n\t"

View File

@ -311,7 +311,9 @@ bigint_done_raw ( const uint64_t *value0, unsigned int size __unused,
}
extern void bigint_multiply_raw ( const uint64_t *multiplicand0,
unsigned int multiplicand_size,
const uint64_t *multiplier0,
uint64_t *value0, unsigned int size );
unsigned int multiplier_size,
uint64_t *value0 );
#endif /* _BITS_BIGINT_H */

View File

@ -2,8 +2,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.text
.arch i386
.code32
.arch i386
/* Must match jmp_buf structure layout */
.struct 0

View File

@ -1,4 +1,5 @@
.section ".note.GNU-stack", "", @progbits
.code32
.arch i386
.section ".data", "aw", @progbits

View File

@ -37,19 +37,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Multiply big integers
*
* @v multiplicand0 Element 0 of big integer to be multiplied
* @v multiplicand_size Number of elements in multiplicand
* @v multiplier0 Element 0 of big integer to be multiplied
* @v multiplier_size Number of elements in multiplier
* @v result0 Element 0 of big integer to hold result
* @v size Number of elements
*/
void bigint_multiply_raw ( const uint64_t *multiplicand0,
unsigned int multiplicand_size,
const uint64_t *multiplier0,
uint64_t *result0, unsigned int size ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
( ( const void * ) multiplicand0 );
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
( ( const void * ) multiplier0 );
bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result =
( ( void * ) result0 );
unsigned int multiplier_size,
uint64_t *result0 ) {
unsigned int result_size = ( multiplicand_size + multiplier_size );
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
*multiplicand = ( ( const void * ) multiplicand0 );
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
*multiplier = ( ( const void * ) multiplier0 );
bigint_t ( result_size ) __attribute__ (( may_alias ))
*result = ( ( void * ) result0 );
unsigned int i;
unsigned int j;
uint64_t multiplicand_element;
@ -64,9 +68,9 @@ void bigint_multiply_raw ( const uint64_t *multiplicand0,
memset ( result, 0, sizeof ( *result ) );
/* Multiply integers one element at a time */
for ( i = 0 ; i < size ; i++ ) {
for ( i = 0 ; i < multiplicand_size ; i++ ) {
multiplicand_element = multiplicand->element[i];
for ( j = 0 ; j < size ; j++ ) {
for ( j = 0 ; j < multiplier_size ; j++ ) {
multiplier_element = multiplier->element[j];
result_elements = &result->element[ i + j ];
/* Perform a single multiply, and add the
@ -75,7 +79,7 @@ void bigint_multiply_raw ( const uint64_t *multiplicand0,
* never overflow beyond the end of the
* result, since:
*
* a < 2^{n}, b < 2^{n} => ab < 2^{2n}
* a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
*/
__asm__ __volatile__ ( "mul.d %1, %6, %7\n\t"
"mulh.du %2, %6, %7\n\t"

View File

@ -53,34 +53,37 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
uint64_t *discard_value;
uint64_t discard_addend_i;
uint64_t discard_value_i;
uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
__asm__ __volatile__ ( "move $t0, $zero\n"
"1:\n\t"
"ld.d %3, %0, 0\n\t"
__asm__ __volatile__ ( "\n1:\n\t"
/* Load addend[i] and value[i] */
"ld.d %3, %0, 0\n\t"
"ld.d %4, %1, 0\n\t"
/* Add carry flag and addend */
"add.d %4, %4, %5\n\t"
"sltu %6, %4, %5\n\t"
"add.d %4, %4, %3\n\t"
"sltu %5, %4, %3\n\t"
"or %5, %5, %6\n\t"
/* Store value[i] */
"st.d %4, %1, 0\n\t"
/* Loop */
"addi.d %0, %0, 8\n\t"
"ld.d %4, %1, 0\n\t"
"add.d %4, %4, $t0\n\t"
"sltu $t0, %4, $t0\n\t"
"add.d %4, %4, %3\n\t"
"sltu $t1, %4, %3\n\t"
"or $t0, $t0, $t1\n\t"
"st.d %4, %1, 0\n\t"
"addi.d %1, %1, 8\n\t"
"addi.w %2, %2, -1\n\t"
"bnez %2, 1b"
"bnez %2, 1b\n\t"
: "=r" ( discard_addend ),
"=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_addend_i ),
"=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value )
: "0" ( addend0 ),
"1" ( value0 ),
"2" ( size )
: "t0", "t1" );
: "0" ( addend0 ), "1" ( value0 ),
"2" ( size ), "5" ( 0 ) );
}
/**
@ -93,35 +96,43 @@ bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
static inline __attribute__ (( always_inline )) void
bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_subtrahend;
uint64_t *discard_value;
uint64_t discard_subtrahend_i;
uint64_t discard_value_i;
uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
unsigned int flag = 0;
discard_subtrahend = (uint64_t*) subtrahend0;
discard_value = value0;
discard_size = size;
do {
discard_subtrahend_i = *discard_subtrahend;
discard_subtrahend++;
discard_value_i = *discard_value;
discard_value_i = discard_value_i - discard_subtrahend_i - flag;
if ( *discard_value < (discard_subtrahend_i + flag)) {
flag = 1;
} else {
flag = 0;
}
*discard_value = discard_value_i;
discard_value++;
discard_size -= 1;
} while (discard_size != 0);
__asm__ __volatile__ ( "\n1:\n\t"
/* Load subtrahend[i] and value[i] */
"ld.d %3, %0, 0\n\t"
"ld.d %4, %1, 0\n\t"
/* Subtract carry flag and subtrahend */
"sltu %6, %4, %5\n\t"
"sub.d %4, %4, %5\n\t"
"sltu %5, %4, %3\n\t"
"sub.d %4, %4, %3\n\t"
"or %5, %5, %6\n\t"
/* Store value[i] */
"st.d %4, %1, 0\n\t"
/* Loop */
"addi.d %0, %0, 8\n\t"
"addi.d %1, %1, 8\n\t"
"addi.w %2, %2, -1\n\t"
"bnez %2, 1b\n\t"
: "=r" ( discard_subtrahend ),
"=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_subtrahend_i ),
"=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value )
: "0" ( subtrahend0 ), "1" ( value0 ),
"2" ( size ), "5" ( 0 ) );
}
/**
@ -132,30 +143,37 @@ bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
*/
static inline __attribute__ (( always_inline )) void
bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
uint64_t discard_value_i;
uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
uint64_t current_value_i;
unsigned int flag = 0;
discard_value = value0;
discard_size = size;
do {
discard_value_i = *discard_value;
current_value_i = discard_value_i;
discard_value_i += discard_value_i + flag;
if (discard_value_i < current_value_i) {
flag = 1;
} else {
flag = 0;
}
*discard_value = discard_value_i;
discard_value++;
discard_size -= 1;
} while ( discard_size != 0 );
__asm__ __volatile__ ( "\n1:\n\t"
/* Load value[i] */
"ld.d %2, %0, 0\n\t"
/* Shift left */
"rotri.d %2, %2, 63\n\t"
"andi %4, %2, 1\n\t"
"xor %2, %2, %4\n\t"
"or %2, %2, %3\n\t"
"move %3, %4\n\t"
/* Store value[i] */
"st.d %2, %0, 0\n\t"
/* Loop */
"addi.d %0, %0, 8\n\t"
"addi.w %1, %1, -1\n\t"
"bnez %1, 1b\n\t"
: "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value )
: "0" ( value0 ), "1" ( size ), "3" ( 0 )
: "cc" );
}
/**
@ -166,27 +184,37 @@ bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
*/
static inline __attribute__ (( always_inline )) void
bigint_ror_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
uint64_t discard_value_i;
uint64_t discard_value_j;
uint64_t discard_carry;
uint64_t discard_temp;
unsigned int discard_size;
discard_value = value0;
discard_size = size;
discard_value_j = 0;
do {
discard_size -= 1;
discard_value_i = *(discard_value + discard_size);
discard_value_j = (discard_value_j << 63) | (discard_value_i >> 1);
*(discard_value + discard_size) = discard_value_j;
discard_value_j = discard_value_i;
} while ( discard_size > 0 );
__asm__ __volatile__ ( "\n1:\n\t"
/* Load value[i] */
"ld.d %2, %0, -8\n\t"
/* Shift right */
"andi %4, %2, 1\n\t"
"xor %2, %2, %4\n\t"
"or %2, %2, %3\n\t"
"move %3, %4\n\t"
"rotri.d %2, %2, 1\n\t"
/* Store value[i] */
"st.d %2, %0, -8\n\t"
/* Loop */
"addi.d %0, %0, -8\n\t"
"addi.w %1, %1, -1\n\t"
"bnez %1, 1b\n\t"
: "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
"=r" ( discard_carry ),
"=r" ( discard_temp ),
"+m" ( *value )
: "0" ( value0 + size ), "1" ( size ), "3" ( 0 )
: "cc" );
}
/**
@ -330,7 +358,9 @@ bigint_done_raw ( const uint64_t *value0, unsigned int size __unused,
}
extern void bigint_multiply_raw ( const uint64_t *multiplicand0,
unsigned int multiplicand_size,
const uint64_t *multiplier0,
uint64_t *value0, unsigned int size );
unsigned int multiplier_size,
uint64_t *value0 );
#endif /* _BITS_BIGINT_H */

View File

@ -0,0 +1,12 @@
#ifndef _BITS_MP_H
#define _BITS_MP_H
/** @file
*
* LoongArch64-specific multiprocessor API implementation
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_MP_H */

View File

@ -46,8 +46,12 @@ static void efiloong64_cpu_nap ( void ) {
* The EFI shell doesn't seem to bother sleeping the CPU; it
* just sits there idly burning power.
*
* If a shutdown is in progess, there may be nothing to
* generate an interrupt since the timer is disabled in the
* first step of ExitBootServices().
*/
__asm__ __volatile__ ( "idle 0" );
if ( ! efi_shutdown_in_progress )
__asm__ __volatile__ ( "idle 0" );
}
PROVIDE_NAP ( efiloong64, cpu_nap, efiloong64_cpu_nap );

197
src/arch/x86/core/mpcall.S Normal file
View File

@ -0,0 +1,197 @@
/*
* Copyright (C) 2024 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
*
* Multiprocessor functions
*
*/
.section ".note.GNU-stack", "", @progbits
.text
/* Selectively assemble code for 32-bit/64-bit builds */
#if defined ( __x86_64__ ) && ! defined ( PLATFORM_pcbios )
#define codemp code64
#define DI rdi
#define SP rsp
#define if32 if 0
#define if64 if 1
#else
#define codemp code32
#define DI edi
#define SP esp
#define if32 if 1
#define if64 if 0
#endif
/* Standard features CPUID leaf */
#define CPUID_FEATURES 0x00000001
/* x2APIC is supported */
#define CPUID_FEATURES_ECX_X2APIC 0x00200000
/* Extended topology enumeration CPUID leaf */
#define CPUID_XT_ENUM 0x0000000b
/*
* Call multiprocessor function from C code
*
* Parameters:
* 4(%esp)/%rdi Multiprocessor function
* 8(%esp)/%rsi Opaque data pointer
*/
.section ".text.mp_call", "ax", @progbits
.codemp
.globl mp_call
mp_call:
.if64 /* Preserve registers, load incoming parameters into registers */
pushq %rax
pushq %rcx
pushq %rdx
pushq %rbx
pushq %rsp
pushq %rbp
pushq %rsi
pushq %rdi
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %r12
pushq %r13
pushq %r14
pushq %r15
.else
pushal
movl 36(%esp), %eax
movl 40(%esp), %edx
.endif
/* Call multiprocessor function */
call mp_jump
.if64 /* Restore registers and return */
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rdi
popq %rsi
popq %rbp
leaq 8(%rsp), %rsp /* discard */
popq %rbx
popq %rdx
popq %rcx
popq %rax
.else
popal
.endif
ret
.size mp_call, . - mp_call
/*
* Jump to multiprocessor function
*
* Parameters:
* %eax/%rdi Multiprocessor function
* %edx/%rsi Opaque data pointer
* %esp/%rsp Stack, or NULL to halt AP upon completion
*
* Obtain the CPU identifier (i.e. the APIC ID) and perform a tail
* call into the specified multiprocessor function.
*
* This code may run with no stack on an application processor.
*/
.section ".text.mp_jump", "ax", @progbits
.codemp
.globl mp_jump
mp_jump:
.if32 /* Move function parameters to available registers */
movl %eax, %edi
movl %edx, %esi
.endif
/* Get 8-bit APIC ID and x2APIC feature bit */
movl $CPUID_FEATURES, %eax
cpuid
shrl $24, %ebx
movl %ebx, %edx
/* Get 32-bit x2APIC ID if applicable */
testl $CPUID_FEATURES_ECX_X2APIC, %ecx
jz 1f
movl $CPUID_XT_ENUM, %eax
xorl %ecx, %ecx
cpuid
1:
.if64 /* Tail call to function */
movq %rdi, %rax
movq %rsi, %rdi
movl %edx, %esi
jmp *%rax
.else
movl %esi, %eax
jmp *%edi
.endif
.size mp_jump, . - mp_jump
/*
* Update maximum CPU identifier
*
* Parameters:
* %eax/%rdi Pointer to shared maximum APIC ID
* %edx/%rsi CPU identifier (APIC ID)
* %esp/%rsp Stack, or NULL to halt AP upon completion
*
* This code may run with no stack on an application processor.
*/
.section ".text.mp_update_max_cpuid", "ax", @progbits
.codemp
.globl mp_update_max_cpuid
mp_update_max_cpuid:
.if32 /* Move function parameters to available registers */
movl %eax, %edi
movl %edx, %esi
.endif
/* Update maximum APIC ID (atomically) */
movl (%DI), %eax
1: cmpl %esi, %eax
jae 2f
lock cmpxchgl %esi, (%DI)
jnz 1b
2:
/* Return to caller (if stack exists), or halt application processor */
test %SP, %SP
jz 3f
ret
3: cli
hlt
jmp 3b
.size mp_update_max_cpuid, . - mp_update_max_cpuid

View File

@ -23,9 +23,8 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.text
.arch i386
.code16
.arch i386
/****************************************************************************
* Set/clear CF on the stack as appropriate, assumes stack is as it should

View File

@ -1,7 +1,6 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.arch i386
#ifdef __x86_64__
#define STACK_SIZE 8192

View File

@ -1,7 +1,6 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.arch i386
/****************************************************************************
* Internal stack

View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 2024 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
*
* Microcode updates
*
*/
.section ".note.GNU-stack", "", @progbits
.text
/* Selectively assemble code for 32-bit/64-bit builds */
#if defined ( __x86_64__ ) && ! defined ( PLATFORM_pcbios )
#define codemp code64
#define AX rax
#define BX rbx
#define CX rcx
#define DX rdx
#define SI rsi
#define DI rdi
#define BP rbp
#define SP rsp
#define if32 if 0
#define if64 if 1
#else
#define codemp code32
#define AX eax
#define BX ebx
#define CX ecx
#define DX edx
#define SI esi
#define DI edi
#define BP ebp
#define SP esp
#define if32 if 1
#define if64 if 0
#endif
/* Standard features CPUID leaf */
#define CPUID_FEATURES 0x00000001
/* BIOS update signature MSR */
#define MSR_BIOS_SIGN_ID 0x0000008b
/** Microcode update control layout
*
* This must match the layout of struct ucode_control.
*/
.struct 0
CONTROL_DESC:
.space 8
CONTROL_STATUS:
.space 8
CONTROL_TRIGGER_MSR:
.space 4
CONTROL_APIC_MAX:
.space 4
CONTROL_APIC_UNEXPECTED:
.space 4
CONTROL_APIC_MASK:
.space 4
CONTROL_APIC_TEST:
.space 4
CONTROL_VER_CLEAR:
.space 1
CONTROL_VER_HIGH:
.space 1
CONTROL_LEN:
/* We use register %ebp/%rbp to hold the address of the update control */
#define CONTROL BP
/* Microcode update descriptor layout
*
* This must match the layout of struct ucode_descriptor.
*/
.struct 0
DESC_SIGNATURE:
.space 4
DESC_VERSION:
.space 4
DESC_ADDRESS:
.space 8
DESC_LEN:
/* We use register %esi/%rsi to hold the address of the descriptor */
#define DESC SI
/** Microcode update status report layout
*
* This must match the layout of struct ucode_status.
*/
.struct 0
STATUS_SIGNATURE:
.space 4
STATUS_ID:
.space 4
STATUS_BEFORE:
.space 4
STATUS_AFTER:
.space 4
STATUS_LEN:
.equ LOG2_STATUS_LEN, 4
.if ( 1 << LOG2_STATUS_LEN ) - STATUS_LEN
.error "LOG2_STATUS_LEN value is incorrect"
.endif
/* We use register %edi/%rdi to hold the address of the status report */
#define STATUS DI
/*
* Update microcode
*
* Parameters:
* %eax/%rdi Microcode update structure
* %edx/%rsi CPU identifier (APIC ID)
* %esp/%rsp Stack, or NULL to halt AP upon completion
*
* This code may run with no stack on an application processor (AP).
* All values must be held in registers, and no subroutine calls are
* possible. No firmware routines may be called.
*
* Since cpuid/rdmsr/wrmsr require the use of %eax, %ebx, %ecx, and
* %edx, we have essentially only three registers available for
* long-term state.
*/
.text
.globl ucode_update
.codemp
.section ".text.ucode_update", "ax", @progbits
ucode_update:
.if64 /* Get input parameters */
movq %rdi, %CONTROL
movl %esi, %edx
.else
movl %eax, %CONTROL
.endif
/* Check against maximum expected APIC ID */
cmpl CONTROL_APIC_MAX(%CONTROL), %edx
jbe 1f
movl %edx, CONTROL_APIC_UNEXPECTED(%CONTROL)
jmp done
1:
/* Calculate per-CPU status report buffer address */
mov %DX, %STATUS
shl $LOG2_STATUS_LEN, %STATUS
add CONTROL_STATUS(%CONTROL), %STATUS
/* Report APIC ID */
movl %edx, STATUS_ID(%STATUS)
/* Get and report CPU signature */
movl $CPUID_FEATURES, %eax
cpuid
movl %eax, STATUS_SIGNATURE(%STATUS)
/* Check APIC ID mask */
movl STATUS_ID(%STATUS), %eax
andl CONTROL_APIC_MASK(%CONTROL), %eax
cmpl CONTROL_APIC_TEST(%CONTROL), %eax
jne done
/* Clear BIOS_SIGN_ID MSR if applicable */
movl $MSR_BIOS_SIGN_ID, %ecx
xorl %eax, %eax
xorl %edx, %edx
testb $0xff, CONTROL_VER_CLEAR(%CONTROL)
jz 1f
wrmsr
1:
/* Get CPU signature to repopulate BIOS_SIGN_ID MSR (for Intel) */
movl $CPUID_FEATURES, %eax
cpuid
/* Get initial microcode version */
movl $MSR_BIOS_SIGN_ID, %ecx
rdmsr
testb $0xff, CONTROL_VER_HIGH(%CONTROL)
jz 1f
movl %edx, %eax
1: movl %eax, STATUS_BEFORE(%STATUS)
/* Get start of descriptor list */
mov CONTROL_DESC(%CONTROL), %DESC
sub $DESC_LEN, %DESC
1: /* Walk update descriptor list to find a matching CPU signature */
add $DESC_LEN, %DESC
movl DESC_SIGNATURE(%DESC), %eax
testl %eax, %eax
jz noload
cmpl STATUS_SIGNATURE(%STATUS), %eax
jne 1b
/* Compare (signed) microcode versions */
movl STATUS_BEFORE(%STATUS), %eax
cmpl DESC_VERSION(%DESC), %eax
jge noload
/* Load microcode update */
movl CONTROL_TRIGGER_MSR(%CONTROL), %ecx
movl (DESC_ADDRESS + 0)(%DESC), %eax
movl (DESC_ADDRESS + 4)(%DESC), %edx
wrmsr
noload: /* Clear BIOS_SIGN_ID MSR if applicable */
movl $MSR_BIOS_SIGN_ID, %ecx
xorl %eax, %eax
xorl %edx, %edx
testb $0xff, CONTROL_VER_CLEAR(%CONTROL)
jz 1f
wrmsr
1:
/* Get CPU signature to repopulate BIOS_SIGN_ID MSR (for Intel) */
movl $CPUID_FEATURES, %eax
cpuid
/* Get and report final microcode version */
movl $MSR_BIOS_SIGN_ID, %ecx
rdmsr
testb $0xff, CONTROL_VER_HIGH(%CONTROL)
jz 1f
movl %edx, %eax
1: movl %eax, STATUS_AFTER(%STATUS)
done: /* Return to caller (if stack exists), or halt application processor */
test %SP, %SP
jz 1f
ret
1: cli
hlt
jmp 1b
.size ucode_update, . - ucode_update

View File

@ -36,19 +36,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Multiply big integers
*
* @v multiplicand0 Element 0 of big integer to be multiplied
* @v multiplicand_size Number of elements in multiplicand
* @v multiplier0 Element 0 of big integer to be multiplied
* @v multiplier_size Number of elements in multiplier
* @v result0 Element 0 of big integer to hold result
* @v size Number of elements
*/
void bigint_multiply_raw ( const uint32_t *multiplicand0,
unsigned int multiplicand_size,
const uint32_t *multiplier0,
uint32_t *result0, unsigned int size ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
( ( const void * ) multiplicand0 );
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
( ( const void * ) multiplier0 );
bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result =
( ( void * ) result0 );
unsigned int multiplier_size,
uint32_t *result0 ) {
unsigned int result_size = ( multiplicand_size + multiplier_size );
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
*multiplicand = ( ( const void * ) multiplicand0 );
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
*multiplier = ( ( const void * ) multiplier0 );
bigint_t ( result_size ) __attribute__ (( may_alias ))
*result = ( ( void * ) result0 );
unsigned int i;
unsigned int j;
uint32_t multiplicand_element;
@ -62,9 +66,9 @@ void bigint_multiply_raw ( const uint32_t *multiplicand0,
memset ( result, 0, sizeof ( *result ) );
/* Multiply integers one element at a time */
for ( i = 0 ; i < size ; i++ ) {
for ( i = 0 ; i < multiplicand_size ; i++ ) {
multiplicand_element = multiplicand->element[i];
for ( j = 0 ; j < size ; j++ ) {
for ( j = 0 ; j < multiplier_size ; j++ ) {
multiplier_element = multiplier->element[j];
result_elements = &result->element[ i + j ];
/* Perform a single multiply, and add the
@ -73,7 +77,7 @@ void bigint_multiply_raw ( const uint32_t *multiplicand0,
* never overflow beyond the end of the
* result, since:
*
* a < 2^{n}, b < 2^{n} => ab < 2^{2n}
* a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
*/
__asm__ __volatile__ ( "mull %5\n\t"
"addl %%eax, (%6,%2,4)\n\t"

View File

@ -11,9 +11,8 @@ FILE_LICENCE ( GPL2_OR_LATER )
#define PIC2_ICR 0xa0
.section ".note.GNU-stack", "", @progbits
.text
.arch i386
.code16
.arch i386
.section ".text16", "ax", @progbits
.globl undiisr

798
src/arch/x86/image/ucode.c Normal file
View File

@ -0,0 +1,798 @@
/*
* Copyright (C) 2024 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
*
* Microcode updates
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <ipxe/uaccess.h>
#include <ipxe/umalloc.h>
#include <ipxe/image.h>
#include <ipxe/cpuid.h>
#include <ipxe/msr.h>
#include <ipxe/mp.h>
#include <ipxe/timer.h>
#include <ipxe/ucode.h>
/**
* Maximum number of hyperthread siblings
*
* Microcode updates must not be performed on hyperthread siblings at
* the same time, since they share microcode storage.
*
* Hyperthread siblings are always the lowest level of the CPU
* topology and correspond to the least significant bits of the APIC
* ID. We may therefore avoid collisions by performing the microcode
* updates in batches, with each batch targeting just one value for
* the least significant N bits of the APIC ID.
*
* We assume that no CPUs exist with more than this number of
* hyperthread siblings. (This must be a power of two.)
*/
#define UCODE_MAX_HT 8
/** Time to wait for a microcode update to complete */
#define UCODE_WAIT_MS 10
/** A CPU vendor string */
union ucode_vendor_id {
/** CPUID registers */
uint32_t dword[3];
/** Human-readable string */
uint8_t string[12];
};
/** A CPU vendor */
struct ucode_vendor {
/** Vendor string */
union ucode_vendor_id id;
/** Microcode load trigger MSR */
uint32_t trigger_msr;
/** Microcode version requires manual clear */
uint8_t ver_clear;
/** Microcode version is reported via high dword */
uint8_t ver_high;
};
/** A microcode update */
struct ucode_update {
/** CPU vendor, if known */
struct ucode_vendor *vendor;
/** Boot processor CPU signature */
uint32_t signature;
/** Platform ID */
uint32_t platform;
/** Number of potentially relevant signatures found */
unsigned int count;
/** Update descriptors (if being populated) */
struct ucode_descriptor *desc;
};
/** A microcode update summary */
struct ucode_summary {
/** Number of CPUs processed */
unsigned int count;
/** Lowest observed microcode version */
int32_t low;
/** Highest observed microcode version */
int32_t high;
};
/** Intel CPU vendor */
static struct ucode_vendor ucode_intel = {
.id = { .string = "GenuineIntel" },
.ver_clear = 1,
.ver_high = 1,
.trigger_msr = MSR_UCODE_TRIGGER_INTEL,
};
/** AMD CPU vendor */
static struct ucode_vendor ucode_amd = {
.id = { .string = "AuthenticAMD" },
.trigger_msr = MSR_UCODE_TRIGGER_AMD,
};
/** List of known CPU vendors */
static struct ucode_vendor *ucode_vendors[] = {
&ucode_intel,
&ucode_amd,
};
/**
* Get CPU vendor name (for debugging)
*
* @v vendor CPU vendor
* @ret name Name
*/
static const char * ucode_vendor_name ( const union ucode_vendor_id *vendor ) {
static union {
union ucode_vendor_id vendor;
char text[ sizeof ( *vendor ) + 1 /* NUL */ ];
} u;
/* Construct name */
memcpy ( &u.vendor, vendor, sizeof ( u.vendor ) );
u.text[ sizeof ( u.text ) - 1 ] = '\0';
return u.text;
}
/**
* Check status report
*
* @v update Microcode update
* @v control Microcode update control
* @v summary Microcode update summary
* @v id APIC ID
* @v optional Status report is optional
* @ret rc Return status code
*/
static int ucode_status ( struct ucode_update *update,
struct ucode_control *control,
struct ucode_summary *summary,
unsigned int id, int optional ) {
struct ucode_status status;
struct ucode_descriptor *desc;
/* Sanity check */
assert ( id <= control->apic_max );
/* Read status report */
copy_from_user ( &status, phys_to_user ( control->status ),
( id * sizeof ( status ) ), sizeof ( status ) );
/* Ignore empty optional status reports */
if ( optional && ( ! status.signature ) )
return 0;
DBGC ( update, "UCODE %#08x signature %#08x ucode %#08x->%#08x\n",
id, status.signature, status.before, status.after );
/* Check CPU signature */
if ( ! status.signature ) {
DBGC2 ( update, "UCODE %#08x has no signature\n", id );
return -ENOENT;
}
/* Check APIC ID is correct */
if ( status.id != id ) {
DBGC ( update, "UCODE %#08x wrong APIC ID %#08x\n",
id, status.id );
return -EINVAL;
}
/* Check that maximum APIC ID was not exceeded */
if ( control->apic_unexpected ) {
DBGC ( update, "UCODE %#08x saw unexpected APIC ID %#08x\n",
id, control->apic_unexpected );
return -ERANGE;
}
/* Check microcode was not downgraded */
if ( status.after < status.before ) {
DBGC ( update, "UCODE %#08x was downgraded %#08x->%#08x\n",
id, status.before, status.after );
return -ENOTTY;
}
/* Check that expected updates (if any) were applied */
for ( desc = update->desc ; desc->signature ; desc++ ) {
if ( ( desc->signature == status.signature ) &&
( status.after < desc->version ) ) {
DBGC ( update, "UCODE %#08x failed update %#08x->%#08x "
"(wanted %#08x)\n", id, status.before,
status.after, desc->version );
return -EIO;
}
}
/* Update summary */
summary->count++;
if ( status.before < summary->low )
summary->low = status.before;
if ( status.after > summary->high )
summary->high = status.after;
return 0;
}
/**
* Update microcode on all CPUs
*
* @v image Microcode image
* @v update Microcode update
* @v summary Microcode update summary to fill in
* @ret rc Return status code
*/
static int ucode_update_all ( struct image *image,
struct ucode_update *update,
struct ucode_summary *summary ) {
struct ucode_control control;
struct ucode_vendor *vendor;
userptr_t status;
unsigned int max;
unsigned int i;
size_t len;
int rc;
/* Initialise summary */
summary->count = 0;
summary->low = UCODE_VERSION_MAX;
summary->high = UCODE_VERSION_MIN;
/* Allocate status reports */
max = mp_max_cpuid();
len = ( ( max + 1 ) * sizeof ( struct ucode_status ) );
status = umalloc ( len );
if ( ! status ) {
DBGC ( image, "UCODE %s could not allocate %d status reports\n",
image->name, ( max + 1 ) );
rc = -ENOMEM;
goto err_alloc;
}
memset_user ( status, 0, 0, len );
/* Construct control structure */
memset ( &control, 0, sizeof ( control ) );
control.desc = virt_to_phys ( update->desc );
control.status = user_to_phys ( status, 0 );
vendor = update->vendor;
if ( vendor ) {
control.ver_clear = vendor->ver_clear;
control.ver_high = vendor->ver_high;
control.trigger_msr = vendor->trigger_msr;
} else {
assert ( update->count == 0 );
}
control.apic_max = max;
/* Update microcode on boot processor */
mp_exec_boot ( ucode_update, &control );
if ( ( rc = ucode_status ( update, &control, summary,
mp_boot_cpuid(), 0 ) ) != 0 ) {
DBGC ( image, "UCODE %s failed on boot processor: %s\n",
image->name, strerror ( rc ) );
goto err_boot;
}
/* Update microcode on application processors, avoiding
* simultaneous updates on hyperthread siblings.
*/
build_assert ( ( UCODE_MAX_HT & ( UCODE_MAX_HT - 1 ) ) == 0 );
control.apic_mask = ( UCODE_MAX_HT - 1 );
for ( ; control.apic_test <= control.apic_mask ; control.apic_test++ ) {
mp_start_all ( ucode_update, &control );
mdelay ( UCODE_WAIT_MS );
}
/* Check status reports */
summary->count = 0;
for ( i = 0 ; i <= max ; i++ ) {
if ( ( rc = ucode_status ( update, &control, summary,
i, 1 ) ) != 0 ) {
goto err_status;
}
}
/* Success */
rc = 0;
err_status:
err_boot:
ufree ( status );
err_alloc:
return rc;
}
/**
* Add descriptor to list (if applicable)
*
* @v image Microcode image
* @v start Starting offset within image
* @v vendor CPU vendor
* @v desc Microcode descriptor
* @v platforms Supported platforms, or 0 for all platforms
* @v update Microcode update
*/
static void ucode_describe ( struct image *image, size_t start,
const struct ucode_vendor *vendor,
const struct ucode_descriptor *desc,
uint32_t platforms, struct ucode_update *update ) {
/* Dump descriptor information */
DBGC2 ( image, "UCODE %s+%#04zx %s %#08x", image->name, start,
ucode_vendor_name ( &vendor->id ), desc->signature );
if ( platforms )
DBGC2 ( image, " (%#02x)", platforms );
DBGC2 ( image, " version %#08x\n", desc->version );
/* Check applicability */
if ( vendor != update->vendor )
return;
if ( ( desc->signature ^ update->signature ) & UCODE_SIGNATURE_MASK )
return;
if ( platforms && ( ! ( platforms & update->platform ) ) )
return;
/* Add descriptor, if applicable */
if ( update->desc ) {
memcpy ( &update->desc[update->count], desc, sizeof ( *desc ) );
DBGC ( image, "UCODE %s+%#04zx found %s %#08x version %#08x\n",
image->name, start, ucode_vendor_name ( &vendor->id ),
desc->signature, desc->version );
}
update->count++;
}
/**
* Verify checksum
*
* @v image Microcode image
* @v start Starting offset
* @v len Length
* @ret rc Return status code
*/
static int ucode_verify ( struct image *image, size_t start, size_t len ) {
uint32_t checksum = 0;
uint32_t dword;
size_t offset;
/* Check length is a multiple of dwords */
if ( ( len % sizeof ( dword ) ) != 0 ) {
DBGC ( image, "UCODE %s+%#04zx invalid length %#zx\n",
image->name, start, len );
return -EINVAL;
}
/* Calculate checksum */
for ( offset = start ; len ;
offset += sizeof ( dword ), len -= sizeof ( dword ) ) {
copy_from_user ( &dword, image->data, offset,
sizeof ( dword ) );
checksum += dword;
}
if ( checksum != 0 ) {
DBGC ( image, "UCODE %s+%#04zx bad checksum %#08x\n",
image->name, start, checksum );
return -EINVAL;
}
return 0;
}
/**
* Parse Intel microcode image
*
* @v image Microcode image
* @v start Starting offset within image
* @v update Microcode update
* @ret len Length consumed, or negative error
*/
static int ucode_parse_intel ( struct image *image, size_t start,
struct ucode_update *update ) {
struct intel_ucode_header hdr;
struct intel_ucode_ext_header exthdr;
struct intel_ucode_ext ext;
struct ucode_descriptor desc;
size_t remaining;
size_t offset;
size_t data_len;
size_t len;
unsigned int i;
int rc;
/* Read header */
remaining = ( image->len - start );
if ( remaining < sizeof ( hdr ) ) {
DBGC ( image, "UCODE %s+%#04zx too small for Intel header\n",
image->name, start );
return -ENOEXEC;
}
copy_from_user ( &hdr, image->data, start, sizeof ( hdr ) );
/* Determine lengths */
data_len = hdr.data_len;
if ( ! data_len )
data_len = INTEL_UCODE_DATA_LEN;
len = hdr.len;
if ( ! len )
len = ( sizeof ( hdr ) + data_len );
/* Verify a selection of fields */
if ( ( hdr.hver != INTEL_UCODE_HVER ) ||
( hdr.lver != INTEL_UCODE_LVER ) ||
( len < sizeof ( hdr ) ) ||
( len > remaining ) ||
( data_len > ( len - sizeof ( hdr ) ) ) ||
( ( data_len % sizeof ( uint32_t ) ) != 0 ) ||
( ( len % INTEL_UCODE_ALIGN ) != 0 ) ) {
DBGC2 ( image, "UCODE %s+%#04zx is not an Intel update\n",
image->name, start );
return -EINVAL;
}
DBGC2 ( image, "UCODE %s+%#04zx is an Intel update\n",
image->name, start );
/* Verify checksum */
if ( ( rc = ucode_verify ( image, start, len ) ) != 0 )
return rc;
/* Populate descriptor */
desc.signature = hdr.signature;
desc.version = hdr.version;
desc.address = user_to_phys ( image->data,
( start + sizeof ( hdr ) ) );
/* Add non-extended descriptor, if applicable */
ucode_describe ( image, start, &ucode_intel, &desc, hdr.platforms,
update );
/* Construct extended descriptors, if applicable */
offset = ( sizeof ( hdr ) + data_len );
if ( offset <= ( len - sizeof ( exthdr ) ) ) {
/* Read extended header */
copy_from_user ( &exthdr, image->data, ( start + offset ),
sizeof ( exthdr ) );
offset += sizeof ( exthdr );
/* Read extended signatures */
for ( i = 0 ; i < exthdr.count ; i++ ) {
/* Read extended signature */
if ( offset > ( len - sizeof ( ext ) ) ) {
DBGC ( image, "UCODE %s+%#04zx extended "
"signature overrun\n",
image->name, start );
return -EINVAL;
}
copy_from_user ( &ext, image->data, ( start + offset ),
sizeof ( ext ) );
offset += sizeof ( ext );
/* Avoid duplicating non-extended descriptor */
if ( ( ext.signature == hdr.signature ) &&
( ext.platforms == hdr.platforms ) ) {
continue;
}
/* Construct descriptor, if applicable */
desc.signature = ext.signature;
ucode_describe ( image, start, &ucode_intel, &desc,
ext.platforms, update );
}
}
return len;
}
/**
* Parse AMD microcode image
*
* @v image Microcode image
* @v start Starting offset within image
* @v update Microcode update
* @ret len Length consumed, or negative error
*/
static int ucode_parse_amd ( struct image *image, size_t start,
struct ucode_update *update ) {
struct amd_ucode_header hdr;
struct amd_ucode_equivalence equiv;
struct amd_ucode_patch_header phdr;
struct amd_ucode_patch patch;
struct ucode_descriptor desc;
size_t remaining;
size_t offset;
unsigned int count;
unsigned int used;
unsigned int i;
/* Read header */
remaining = ( image->len - start );
if ( remaining < sizeof ( hdr ) ) {
DBGC ( image, "UCODE %s+%#04zx too small for AMD header\n",
image->name, start );
return -ENOEXEC;
}
copy_from_user ( &hdr, image->data, start, sizeof ( hdr ) );
/* Check header */
if ( hdr.magic != AMD_UCODE_MAGIC ) {
DBGC2 ( image, "UCODE %s+%#04zx is not an AMD update\n",
image->name, start );
return -ENOEXEC;
}
DBGC2 ( image, "UCODE %s+%#04zx is an AMD update\n",
image->name, start );
if ( hdr.type != AMD_UCODE_EQUIV_TYPE ) {
DBGC ( image, "UCODE %s+%#04zx unsupported equivalence table "
"type %d\n", image->name, start, hdr.type );
return -ENOTSUP;
}
if ( hdr.len > ( remaining - sizeof ( hdr ) ) ) {
DBGC ( image, "UCODE %s+%#04zx truncated equivalence table\n",
image->name, start );
return -EINVAL;
}
/* Count number of equivalence table entries */
offset = sizeof ( hdr );
for ( count = 0 ; offset < ( sizeof ( hdr ) + hdr.len ) ;
count++, offset += sizeof ( equiv ) ) {
copy_from_user ( &equiv, image->data, ( start + offset ),
sizeof ( equiv ) );
if ( ! equiv.signature )
break;
}
DBGC2 ( image, "UCODE %s+%#04zx has %d equivalence table entries\n",
image->name, start, count );
/* Parse available updates */
offset = ( sizeof ( hdr ) + hdr.len );
used = 0;
while ( used < count ) {
/* Read patch header */
if ( ( offset + sizeof ( phdr ) ) > remaining ) {
DBGC ( image, "UCODE %s+%#04zx truncated patch "
"header\n", image->name, start );
return -EINVAL;
}
copy_from_user ( &phdr, image->data, ( start + offset ),
sizeof ( phdr ) );
offset += sizeof ( phdr );
/* Validate patch header */
if ( phdr.type != AMD_UCODE_PATCH_TYPE ) {
DBGC ( image, "UCODE %s+%#04zx unsupported patch type "
"%d\n", image->name, start, phdr.type );
return -ENOTSUP;
}
if ( phdr.len < sizeof ( patch ) ) {
DBGC ( image, "UCODE %s+%#04zx underlength patch\n",
image->name, start );
return -EINVAL;
}
if ( phdr.len > ( remaining - offset ) ) {
DBGC ( image, "UCODE %s+%#04zx truncated patch\n",
image->name, start );
return -EINVAL;
}
/* Read patch and construct descriptor */
copy_from_user ( &patch, image->data, ( start + offset ),
sizeof ( patch ) );
desc.version = patch.version;
desc.address = user_to_phys ( image->data, ( start + offset ) );
offset += phdr.len;
/* Parse equivalence table to find matching signatures */
for ( i = 0 ; i < count ; i++ ) {
copy_from_user ( &equiv, image->data,
( start + sizeof ( hdr ) +
( i * ( sizeof ( equiv ) ) ) ),
sizeof ( equiv ) );
if ( patch.id == equiv.id ) {
desc.signature = equiv.signature;
ucode_describe ( image, start, &ucode_amd,
&desc, 0, update );
used++;
}
}
}
return offset;
}
/**
* Parse microcode image
*
* @v image Microcode image
* @v update Microcode update
* @ret rc Return status code
*/
static int ucode_parse ( struct image *image, struct ucode_update *update ) {
size_t start;
int len;
/* Attempt to parse concatenated microcode updates */
for ( start = 0 ; start < image->len ; start += len ) {
/* Attempt to parse as Intel microcode */
len = ucode_parse_intel ( image, start, update );
if ( len > 0 )
continue;
/* Attempt to parse as AMD microcode */
len = ucode_parse_amd ( image, start, update );
if ( len > 0 )
continue;
/* Not a recognised microcode format */
DBGC ( image, "UCODE %s+%zx not recognised\n",
image->name, start );
return -ENOEXEC;
}
return 0;
}
/**
* Execute microcode update
*
* @v image Microcode image
* @ret rc Return status code
*/
static int ucode_exec ( struct image *image ) {
struct ucode_update update;
struct ucode_vendor *vendor;
struct ucode_summary summary;
union ucode_vendor_id id;
uint64_t platform_id;
uint32_t discard_a;
uint32_t discard_b;
uint32_t discard_c;
uint32_t discard_d;
unsigned int check;
unsigned int i;
size_t len;
int rc;
/* Initialise update */
memset ( &update, 0, sizeof ( update ) );
cpuid ( CPUID_VENDOR_ID, 0, &discard_a, &id.dword[0], &id.dword[2],
&id.dword[1] );
cpuid ( CPUID_FEATURES, 0, &update.signature, &discard_b,
&discard_c, &discard_d );
/* Identify CPU vendor, if recognised */
for ( i = 0 ; i < ( sizeof ( ucode_vendors ) /
sizeof ( ucode_vendors[0] ) ) ; i++ ) {
vendor = ucode_vendors[i];
if ( memcmp ( &id, &vendor->id, sizeof ( id ) ) == 0 )
update.vendor = vendor;
}
/* Identify platform, if applicable */
if ( update.vendor == &ucode_intel ) {
platform_id = rdmsr ( MSR_PLATFORM_ID );
update.platform =
( 1 << MSR_PLATFORM_ID_VALUE ( platform_id ) );
}
/* Count number of matching update descriptors */
DBGC ( image, "UCODE %s applying to %s %#08x",
image->name, ucode_vendor_name ( &id ), update.signature );
if ( update.platform )
DBGC ( image, " (%#02x)", update.platform );
DBGC ( image, "\n" );
if ( ( rc = ucode_parse ( image, &update ) ) != 0 )
goto err_count;
DBGC ( image, "UCODE %s found %d matching update(s)\n",
image->name, update.count );
/* Allocate descriptors */
len = ( ( update.count + 1 /* terminator */ ) *
sizeof ( update.desc[0] ) );
update.desc = zalloc ( len );
if ( ! update.desc ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Populate descriptors */
check = update.count;
update.count = 0;
if ( ( rc = ucode_parse ( image, &update ) ) != 0 )
goto err_parse;
assert ( check == update.count );
/* Perform update */
if ( ( rc = ucode_update_all ( image, &update, &summary ) ) != 0 )
goto err_update;
/* Print summary if directed to do so */
if ( image->cmdline && ( strstr ( image->cmdline, "-v" ) ) ) {
printf ( "Microcode: " );
if ( summary.low == summary.high ) {
printf ( "already version %#x", summary.low );
} else {
printf ( "updated version %#x->%#x",
summary.low, summary.high );
}
printf ( " (x%d)\n", summary.count );
}
err_update:
err_parse:
free ( update.desc );
err_alloc:
err_count:
return rc;
}
/**
* Probe microcode update image
*
* @v image Microcode image
* @ret rc Return status code
*/
static int ucode_probe ( struct image *image ) {
union {
struct intel_ucode_header intel;
struct amd_ucode_header amd;
} header;
/* Sanity check */
if ( image->len < sizeof ( header ) ) {
DBGC ( image, "UCODE %s too short\n", image->name );
return -ENOEXEC;
}
/* Read first microcode image header */
copy_from_user ( &header, image->data, 0, sizeof ( header ) );
/* Check for something that looks like an Intel update
*
* Intel updates unfortunately have no magic signatures or
* other easily verifiable fields. We check a small selection
* of header fields that can be easily verified.
*
* We do not attempt to fully parse the update, since we want
* errors to be reported at the point of attempting to execute
* the image, and do not want to have a microcode image
* erroneously treated as a PXE boot executable.
*/
if ( ( header.intel.hver == INTEL_UCODE_HVER ) &&
( header.intel.lver == INTEL_UCODE_LVER ) &&
( ( header.intel.date.century == 0x19 ) ||
( ( header.intel.date.century >= 0x20 ) &&
( header.intel.date.century <= 0x29 ) ) ) ) {
DBGC ( image, "UCODE %s+%#04zx looks like an Intel update\n",
image->name, ( ( size_t ) 0 ) );
return 0;
}
/* Check for AMD update signature */
if ( ( header.amd.magic == AMD_UCODE_MAGIC ) &&
( header.amd.type == AMD_UCODE_EQUIV_TYPE ) ) {
DBGC ( image, "UCODE %s+%#04zx looks like an AMD update\n",
image->name, ( ( size_t ) 0 ) );
return 0;
}
return -ENOEXEC;
}
/** Microcode update image type */
struct image_type ucode_image_type __image_type ( PROBE_NORMAL ) = {
.name = "ucode",
.probe = ucode_probe,
.exec = ucode_exec,
};

View File

@ -323,7 +323,9 @@ bigint_done_raw ( const uint32_t *value0, unsigned int size __unused,
}
extern void bigint_multiply_raw ( const uint32_t *multiplicand0,
unsigned int multiplicand_size,
const uint32_t *multiplier0,
uint32_t *value0, unsigned int size );
unsigned int multiplier_size,
uint32_t *value0 );
#endif /* _BITS_BIGINT_H */

View File

@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_sdi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000b0000 )
#define ERRFILE_initrd ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000c0000 )
#define ERRFILE_pxe_call ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000d0000 )
#define ERRFILE_ucode ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000e0000 )
#define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 )
#define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 )

View File

@ -0,0 +1,14 @@
#ifndef _BITS_MP_H
#define _BITS_MP_H
/** @file
*
* x86-specific multiprocessor API implementation
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/bios_mp.h>
#endif /* _BITS_MP_H */

View File

@ -0,0 +1,32 @@
#ifndef _IPXE_BIOS_MP_H
#define _IPXE_BIOS_MP_H
/** @file
*
* BIOS multiprocessor API implementation
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/io.h>
#ifdef MPAPI_PCBIOS
#define MPAPI_PREFIX_pcbios
#else
#define MPAPI_PREFIX_pcbios __pcbios_
#endif
/**
* Calculate address as seen by a multiprocessor function
*
* @v address Address in boot processor address space
* @ret address Address in application processor address space
*/
static inline __attribute__ (( always_inline )) mp_addr_t
MPAPI_INLINE ( pcbios, mp_address ) ( void *address ) {
return virt_to_phys ( address );
}
#endif /* _IPXE_BIOS_MP_H */

View File

@ -0,0 +1,223 @@
#ifndef _IPXE_UCODE_H
#define _IPXE_UCODE_H
/** @file
*
* Microcode updates
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/mp.h>
/** Platform ID MSR */
#define MSR_PLATFORM_ID 0x00000017UL
/** Extract platform ID from MSR value */
#define MSR_PLATFORM_ID_VALUE( value ) ( ( (value) >> 50 ) & 0x7 )
/** Intel microcode load trigger MSR */
#define MSR_UCODE_TRIGGER_INTEL 0x00000079UL
/** AMD microcode load trigger MSR */
#define MSR_UCODE_TRIGGER_AMD 0xc0010020UL
/** CPUID signature applicability mask
*
* We assume that only steppings may vary between the boot CPU and any
* application processors.
*/
#define UCODE_SIGNATURE_MASK 0xfffffff0UL
/** Minimum possible microcode version */
#define UCODE_VERSION_MIN -0x80000000L
/** Maximum possible microcode version */
#define UCODE_VERSION_MAX 0x7fffffffL
/** A microcode update control
*
* This must match the layout as used by the assembly code in
* ucode_mp.S.
*/
struct ucode_control {
/** Microcode descriptor list physical address */
uint64_t desc;
/** Microcode status array physical address */
uint64_t status;
/** Microcode load trigger MSR */
uint32_t trigger_msr;
/** Maximum expected APIC ID */
uint32_t apic_max;
/** Unexpected APIC ID
*
* Any application processor may set this to indicate that its
* APIC ID was higher than the maximum expected APIC ID.
*/
uint32_t apic_unexpected;
/** APIC ID eligibility mask bits */
uint32_t apic_mask;
/** APIC ID eligibility test bits */
uint32_t apic_test;
/** Microcode version requires manual clear */
uint8_t ver_clear;
/** Microcode version is reported via high dword */
uint8_t ver_high;
} __attribute__ (( packed ));
/** A microcode update descriptor
*
* This must match the layout as used by the assembly code in
* ucode_mp.S.
*/
struct ucode_descriptor {
/** CPUID signature (or 0 to terminate list) */
uint32_t signature;
/** Microcode version */
int32_t version;
/** Microcode physical address */
uint64_t address;
} __attribute__ (( packed ));
/** A microcode update status report
*
* This must match the layout as used by the assembly code in
* ucode_mp.S.
*/
struct ucode_status {
/** CPU signature */
uint32_t signature;
/** APIC ID (for sanity checking) */
uint32_t id;
/** Initial microcode version */
int32_t before;
/** Final microcode version */
int32_t after;
} __attribute__ (( packed ));
/** A microcode date */
struct ucode_date {
/** Year (BCD) */
uint8_t year;
/** Century (BCD) */
uint8_t century;
/** Day (BCD) */
uint8_t day;
/** Month (BCD) */
uint8_t month;
} __attribute__ (( packed ));
/** An Intel microcode update file header */
struct intel_ucode_header {
/** Header version number */
uint32_t hver;
/** Microcode version */
int32_t version;
/** Date */
struct ucode_date date;
/** CPUID signature */
uint32_t signature;
/** Checksum */
uint32_t checksum;
/** Loader version */
uint32_t lver;
/** Supported platforms */
uint32_t platforms;
/** Microcode data size (or 0 to indicate 2000 bytes) */
uint32_t data_len;
/** Total size (or 0 to indicate 2048 bytes) */
uint32_t len;
/** Reserved */
uint8_t reserved[12];
} __attribute__ (( packed ));
/** Intel microcode header version number */
#define INTEL_UCODE_HVER 0x00000001UL
/** Intel microcode loader version number */
#define INTEL_UCODE_LVER 0x00000001UL
/** Intel microcode default data length */
#define INTEL_UCODE_DATA_LEN 2000
/** Intel microcode file alignment */
#define INTEL_UCODE_ALIGN 1024
/** An Intel microcode update file extended header */
struct intel_ucode_ext_header {
/** Extended signature count */
uint32_t count;
/** Extended checksum */
uint32_t checksum;
/** Reserved */
uint8_t reserved[12];
} __attribute__ (( packed ));
/** An Intel microcode extended signature */
struct intel_ucode_ext {
/** CPUID signature */
uint32_t signature;
/** Supported platforms */
uint32_t platforms;
/** Checksum */
uint32_t checksum;
} __attribute__ (( packed ));
/** An AMD microcode update file header */
struct amd_ucode_header {
/** Magic signature */
uint32_t magic;
/** Equivalence table type */
uint32_t type;
/** Equivalence table length */
uint32_t len;
} __attribute__ (( packed ));
/** AMD microcode magic signature */
#define AMD_UCODE_MAGIC ( ( 'A' << 16 ) | ( 'M' << 8 ) | ( 'D' << 0 ) )
/** AMD microcode equivalence table type */
#define AMD_UCODE_EQUIV_TYPE 0x00000000UL
/** An AMD microcode equivalence table entry */
struct amd_ucode_equivalence {
/** CPU signature */
uint32_t signature;
/** Reserved */
uint8_t reserved_a[8];
/** Equivalence ID */
uint16_t id;
/** Reserved */
uint8_t reserved_b[2];
} __attribute__ (( packed ));
/** An AMD microcode patch header */
struct amd_ucode_patch_header {
/** Patch type */
uint32_t type;
/** Patch length */
uint32_t len;
} __attribute__ (( packed ));
/** An AMD microcode patch */
struct amd_ucode_patch {
/** Date */
struct ucode_date date;
/** Microcode version */
int32_t version;
/** Reserved */
uint8_t reserved_a[16];
/** Equivalence ID */
uint16_t id;
/** Reserved */
uint8_t reserved_b[14];
} __attribute__ (( packed ));
/** AMD patch type */
#define AMD_UCODE_PATCH_TYPE 0x00000001UL
extern mp_func_t ucode_update;
#endif /* _IPXE_UCODE_H */

View File

@ -474,6 +474,26 @@ extern struct page_table io_pages;
*/
#define IO_BASE ( ( void * ) 0x100000000ULL )
/** Startup IPI real-mode handler */
extern char __text16_array ( sipi, [] );
#define sipi __use_text16 ( sipi )
/** Length of startup IPI real-mode handler */
extern char sipi_len[];
/** Startup IPI real-mode handler copy of real-mode data segment */
extern uint16_t __text16 ( sipi_ds );
#define sipi_ds __use_text16 ( sipi_ds )
/** Startup IPI protected-mode handler (physical address) */
extern uint32_t sipi_handler;
/** Startup IPI register state */
extern struct i386_regs sipi_regs;
extern void setup_sipi ( unsigned int vector, uint32_t handler,
struct i386_regs *regs );
#endif /* ASSEMBLY */
#endif /* LIBRM_H */

View File

@ -46,8 +46,12 @@ static void efix86_cpu_nap ( void ) {
* The EFI shell doesn't seem to bother sleeping the CPU; it
* just sits there idly burning power.
*
* If a shutdown is in progess, there may be nothing to
* generate an interrupt since the timer is disabled in the
* first step of ExitBootServices().
*/
__asm__ __volatile__ ( "hlt" );
if ( ! efi_shutdown_in_progress )
__asm__ __volatile__ ( "hlt" );
}
PROVIDE_NAP ( efix86, cpu_nap, efix86_cpu_nap );

View File

@ -0,0 +1,173 @@
/*
* Copyright (C) 2024 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
*
* BIOS multiprocessor API implementation
*
*/
#include <registers.h>
#include <ipxe/uaccess.h>
#include <ipxe/timer.h>
#include <ipxe/msr.h>
#include <ipxe/mp.h>
/** Local APIC base address MSR */
#define MSR_APIC_BASE 0x0000001b
/** Local APIC is in x2APIC mode */
#define MSR_APIC_BASE_X2APIC 0x400
/** Local APIC base address mask */
#define MSR_APIC_BASE_MASK ( ~0xfffULL )
/** Interrupt command register */
#define APIC_ICR 0x0300
/** Interrupt command register (x2APIC) */
#define MSR_X2APIC_ICR 0x830
/** Interrupt command register: send to all excluding self */
#define APIC_ICR_ALL_NOT_SELF 0x000c0000
/** Interrupt command register: level mode */
#define APIC_ICR_LEVEL 0x00008000
/** Interrupt command register: level asserted */
#define APIC_ICR_LEVEL_ASSERT 0x00004000
/** Interrupt command register: INIT */
#define APIC_ICR_INIT 0x00000500
/** Interrupt command register: SIPI */
#define APIC_ICR_SIPI( vector ) ( 0x00000600 | (vector) )
/** Time to wait for an IPI to complete */
#define IPI_WAIT_MS 10
/**
* Startup IPI vector
*
* The real-mode startup IPI code must be copied to a page boundary in
* base memory. We fairly arbitrarily choose to place this at 0x8000.
*/
#define SIPI_VECTOR 0x08
/** Protected-mode startup IPI handler */
extern void __asmcall mp_jump ( mp_addr_t func, mp_addr_t opaque );
/**
* Execute a multiprocessor function on the boot processor
*
* @v func Multiprocessor function
* @v opaque Opaque data pointer
*/
static void bios_mp_exec_boot ( mp_func_t func, void *opaque ) {
/* Call multiprocessor function with physical addressing */
__asm__ __volatile__ ( PHYS_CODE ( "pushl %k2\n\t"
"pushl %k1\n\t"
"call *%k0\n\t"
"addl $8, %%esp\n\t" )
: : "r" ( mp_address ( mp_call ) ),
"r" ( mp_address ( func ) ),
"r" ( mp_address ( opaque ) ) );
}
/**
* Send an interprocessor interrupt
*
* @v apic APIC base address
* @v x2apic x2APIC mode enabled
* @v icr Interrupt control register value
*/
static void bios_mp_ipi ( void *apic, int x2apic, uint32_t icr ) {
/* Write ICR according to APIC/x2APIC mode */
DBGC ( MSR_APIC_BASE, "BIOSMP sending IPI %#08x\n", icr );
if ( x2apic ) {
wrmsr ( MSR_X2APIC_ICR, icr );
} else {
writel ( icr, ( apic + APIC_ICR ) );
}
/* Allow plenty of time for delivery to complete */
mdelay ( IPI_WAIT_MS );
}
/**
* Start a multiprocessor function on all application processors
*
* @v func Multiprocessor function
* @v opaque Opaque data pointer
*/
static void bios_mp_start_all ( mp_func_t func, void *opaque ) {
struct i386_regs regs;
uint64_t base;
uint32_t ipi;
void *apic;
int x2apic;
/* Prepare SIPI handler */
regs.eax = mp_address ( func );
regs.edx = mp_address ( opaque );
setup_sipi ( SIPI_VECTOR, virt_to_phys ( mp_jump ), &regs );
/* Get local APIC base address and mode */
base = rdmsr ( MSR_APIC_BASE );
x2apic = ( base & MSR_APIC_BASE_X2APIC );
DBGC ( MSR_APIC_BASE, "BIOSMP local %sAPIC base %#llx\n",
( x2apic ? "x2" : "" ), ( ( unsigned long long ) base ) );
/* Map local APIC */
apic = ioremap ( ( base & MSR_APIC_BASE_MASK ), PAGE_SIZE );
if ( ! apic )
goto err_ioremap;
/* Assert INIT IPI */
ipi = ( APIC_ICR_ALL_NOT_SELF | APIC_ICR_LEVEL |
APIC_ICR_LEVEL_ASSERT | APIC_ICR_INIT );
bios_mp_ipi ( apic, x2apic, ipi );
/* Clear INIT IPI */
ipi &= ~APIC_ICR_LEVEL_ASSERT;
bios_mp_ipi ( apic, x2apic, ipi );
/* Send SIPI */
ipi = ( APIC_ICR_ALL_NOT_SELF | APIC_ICR_SIPI ( SIPI_VECTOR ) );
bios_mp_ipi ( apic, x2apic, ipi );
iounmap ( apic );
err_ioremap:
/* No way to handle errors: caller must check that
* multiprocessor function executed as expected.
*/
return;
}
PROVIDE_MPAPI_INLINE ( pcbios, mp_address );
PROVIDE_MPAPI ( pcbios, mp_exec_boot, bios_mp_exec_boot );
PROVIDE_MPAPI ( pcbios, mp_start_all, bios_mp_start_all );

View File

@ -24,9 +24,8 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.text
.arch i386
.code16
.arch i386
#define SMAP 0x534d4150

View File

@ -183,8 +183,8 @@ static int int13_parse_eltorito ( struct san_device *sandev, void *scratch ) {
/* Read boot record volume descriptor */
if ( ( rc = sandev_read ( sandev, ELTORITO_LBA, 1,
virt_to_user ( boot ) ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x could not read El Torito boot "
"record volume descriptor: %s\n",
DBGC ( sandev->drive, "INT13 drive %02x could not read El "
"Torito boot record volume descriptor: %s\n",
sandev->drive, strerror ( rc ) );
return rc;
}
@ -192,10 +192,11 @@ static int int13_parse_eltorito ( struct san_device *sandev, void *scratch ) {
/* Check for an El Torito boot catalog */
if ( memcmp ( boot, &boot_check, sizeof ( boot_check ) ) == 0 ) {
int13->boot_catalog = boot->sector;
DBGC ( sandev, "INT13 drive %02x has an El Torito boot catalog "
"at LBA %08x\n", sandev->drive, int13->boot_catalog );
DBGC ( sandev->drive, "INT13 drive %02x has an El Torito boot "
"catalog at LBA %08x\n", sandev->drive,
int13->boot_catalog );
} else {
DBGC ( sandev, "INT13 drive %02x has no El Torito boot "
DBGC ( sandev->drive, "INT13 drive %02x has no El Torito boot "
"catalog\n", sandev->drive );
}
@ -228,14 +229,14 @@ static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch,
/* Read partition table */
if ( ( rc = sandev_read ( sandev, 0, 1, virt_to_user ( mbr ) ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x could not read "
DBGC ( sandev->drive, "INT13 drive %02x could not read "
"partition table to guess geometry: %s\n",
sandev->drive, strerror ( rc ) );
return rc;
}
DBGC2 ( sandev, "INT13 drive %02x has MBR:\n", sandev->drive );
DBGC2_HDA ( sandev, 0, mbr, sizeof ( *mbr ) );
DBGC ( sandev, "INT13 drive %02x has signature %08x\n",
DBGC2 ( sandev->drive, "INT13 drive %02x has MBR:\n", sandev->drive );
DBGC2_HDA ( sandev->drive, 0, mbr, sizeof ( *mbr ) );
DBGC ( sandev->drive, "INT13 drive %02x has signature %08x\n",
sandev->drive, mbr->signature );
/* Scan through partition table and modify guesses for
@ -260,8 +261,8 @@ static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch,
if ( ( start_cylinder == 0 ) && ( start_head != 0 ) ) {
*sectors = ( ( partition->start + 1 - start_sector ) /
start_head );
DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
"xx/xx/%d based on partition %d\n",
DBGC ( sandev->drive, "INT13 drive %02x guessing "
"C/H/S xx/xx/%d based on partition %d\n",
sandev->drive, *sectors, ( i + 1 ) );
}
@ -272,14 +273,14 @@ static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch,
end_sector = PART_SECTOR ( partition->chs_end );
if ( ( end_head + 1 ) > *heads ) {
*heads = ( end_head + 1 );
DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
"xx/%d/xx based on partition %d\n",
DBGC ( sandev->drive, "INT13 drive %02x guessing "
"C/H/S xx/%d/xx based on partition %d\n",
sandev->drive, *heads, ( i + 1 ) );
}
if ( end_sector > *sectors ) {
*sectors = end_sector;
DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
"xx/xx/%d based on partition %d\n",
DBGC ( sandev->drive, "INT13 drive %02x guessing "
"C/H/S xx/xx/%d based on partition %d\n",
sandev->drive, *sectors, ( i + 1 ) );
}
}
@ -343,9 +344,10 @@ static int int13_guess_geometry_fdd ( struct san_device *sandev,
*heads = INT13_FDD_HEADS ( geometry );
*sectors = INT13_FDD_SECTORS ( geometry );
if ( ( cylinders * (*heads) * (*sectors) ) == blocks ) {
DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
"%d/%d/%d based on size %dK\n", sandev->drive,
cylinders, *heads, *sectors, ( blocks / 2 ) );
DBGC ( sandev->drive, "INT13 drive %02x guessing "
"C/H/S %d/%d/%d based on size %dK\n",
sandev->drive, cylinders, *heads, *sectors,
( blocks / 2 ) );
return 0;
}
}
@ -355,8 +357,9 @@ static int int13_guess_geometry_fdd ( struct san_device *sandev,
*/
*heads = 2;
*sectors = 18;
DBGC ( sandev, "INT13 drive %02x guessing C/H/S xx/%d/%d based on size "
"%dK\n", sandev->drive, *heads, *sectors, ( blocks / 2 ) );
DBGC ( sandev->drive, "INT13 drive %02x guessing C/H/S xx/%d/%d "
"based on size %dK\n", sandev->drive, *heads, *sectors,
( blocks / 2 ) );
return 0;
}
@ -431,8 +434,8 @@ static void int13_sync_num_drives ( void ) {
required = ( ( max_drive & 0x7f ) + 1 );
if ( *counter < required ) {
*counter = required;
DBGC ( sandev, "INT13 drive %02x added to drive count: "
"%d HDDs, %d FDDs\n",
DBGC ( sandev->drive, "INT13 drive %02x added to "
"drive count: %d HDDs, %d FDDs\n",
sandev->drive, num_drives, num_fdds );
}
}
@ -472,7 +475,7 @@ static int int13_reset ( struct san_device *sandev,
struct i386_all_regs *ix86 __unused ) {
int rc;
DBGC2 ( sandev, "Reset drive\n" );
DBGC2 ( sandev->drive, "Reset drive\n" );
/* Reset SAN device */
if ( ( rc = sandev_reset ( sandev ) ) != 0 )
@ -491,7 +494,7 @@ static int int13_get_last_status ( struct san_device *sandev,
struct i386_all_regs *ix86 __unused ) {
struct int13_data *int13 = sandev->priv;
DBGC2 ( sandev, "Get status of last operation\n" );
DBGC2 ( sandev->drive, "Get status of last operation\n" );
return int13->last_status;
}
@ -524,8 +527,8 @@ static int int13_rw_sectors ( struct san_device *sandev,
/* Validate blocksize */
if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) {
DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) "
"for non-extended read/write\n",
DBGC ( sandev->drive, "\nINT 13 drive %02x invalid blocksize "
"(%zd) for non-extended read/write\n",
sandev->drive, sandev_blksize ( sandev ) );
return -INT13_STATUS_INVALID;
}
@ -537,9 +540,10 @@ static int int13_rw_sectors ( struct san_device *sandev,
if ( ( cylinder >= int13->cylinders ) ||
( head >= int13->heads ) ||
( sector < 1 ) || ( sector > int13->sectors_per_track ) ) {
DBGC ( sandev, "C/H/S %d/%d/%d out of range for geometry "
"%d/%d/%d\n", cylinder, head, sector, int13->cylinders,
int13->heads, int13->sectors_per_track );
DBGC ( sandev->drive, "C/H/S %d/%d/%d out of range for "
"geometry %d/%d/%d\n", cylinder, head, sector,
int13->cylinders, int13->heads,
int13->sectors_per_track );
return -INT13_STATUS_INVALID;
}
lba = ( ( ( ( cylinder * int13->heads ) + head )
@ -547,13 +551,13 @@ static int int13_rw_sectors ( struct san_device *sandev,
count = ix86->regs.al;
buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
DBGC2 ( sandev, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n",
cylinder, head, sector, lba, ix86->segs.es, ix86->regs.bx,
count );
DBGC2 ( sandev->drive, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x "
"(count %d)\n", cylinder, head, sector, lba, ix86->segs.es,
ix86->regs.bx, count );
/* Read from / write to block device */
if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ){
DBGC ( sandev, "INT13 drive %02x I/O failed: %s\n",
DBGC ( sandev->drive, "INT13 drive %02x I/O failed: %s\n",
sandev->drive, strerror ( rc ) );
return -INT13_STATUS_READ_ERROR;
}
@ -577,7 +581,7 @@ static int int13_rw_sectors ( struct san_device *sandev,
static int int13_read_sectors ( struct san_device *sandev,
struct i386_all_regs *ix86 ) {
DBGC2 ( sandev, "Read: " );
DBGC2 ( sandev->drive, "Read: " );
return int13_rw_sectors ( sandev, ix86, sandev_read );
}
@ -597,7 +601,7 @@ static int int13_read_sectors ( struct san_device *sandev,
static int int13_write_sectors ( struct san_device *sandev,
struct i386_all_regs *ix86 ) {
DBGC2 ( sandev, "Write: " );
DBGC2 ( sandev->drive, "Write: " );
return int13_rw_sectors ( sandev, ix86, sandev_write );
}
@ -619,12 +623,12 @@ static int int13_get_parameters ( struct san_device *sandev,
unsigned int max_head = int13->heads - 1;
unsigned int max_sector = int13->sectors_per_track; /* sic */
DBGC2 ( sandev, "Get drive parameters\n" );
DBGC2 ( sandev->drive, "Get drive parameters\n" );
/* Validate blocksize */
if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) {
DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) "
"for non-extended parameters\n",
DBGC ( sandev->drive, "\nINT 13 drive %02x invalid blocksize "
"(%zd) for non-extended parameters\n",
sandev->drive, sandev_blksize ( sandev ) );
return -INT13_STATUS_INVALID;
}
@ -657,7 +661,7 @@ static int int13_get_disk_type ( struct san_device *sandev,
struct i386_all_regs *ix86 ) {
uint32_t blocks;
DBGC2 ( sandev, "Get disk type\n" );
DBGC2 ( sandev->drive, "Get disk type\n" );
if ( int13_is_fdd ( sandev ) ) {
return INT13_DISK_TYPE_FDD;
@ -682,7 +686,7 @@ static int int13_extension_check ( struct san_device *sandev,
struct i386_all_regs *ix86 ) {
if ( ( ix86->regs.bx == 0x55aa ) && ! int13_is_fdd ( sandev ) ) {
DBGC2 ( sandev, "INT13 extensions installation check\n" );
DBGC2 ( sandev->drive, "INT13 extensions check\n" );
ix86->regs.bx = 0xaa55;
ix86->regs.cx = ( INT13_EXTENSION_LINEAR |
INT13_EXTENSION_EDD |
@ -725,7 +729,8 @@ static int int13_extended_rw ( struct san_device *sandev,
get_real ( bufsize, ix86->segs.ds,
( ix86->regs.si + offsetof ( typeof ( addr ), bufsize ) ) );
if ( bufsize < offsetof ( typeof ( addr ), buffer_phys ) ) {
DBGC2 ( sandev, "<invalid buffer size %#02x\n>\n", bufsize );
DBGC2 ( sandev->drive, "<invalid buffer size %#02x\n>\n",
bufsize );
return -INT13_STATUS_INVALID;
}
@ -733,17 +738,18 @@ static int int13_extended_rw ( struct san_device *sandev,
memset ( &addr, 0, sizeof ( addr ) );
copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize );
lba = addr.lba;
DBGC2 ( sandev, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) );
DBGC2 ( sandev->drive, "LBA %08llx <-> ",
( ( unsigned long long ) lba ) );
if ( ( addr.count == 0xff ) ||
( ( addr.buffer.segment == 0xffff ) &&
( addr.buffer.offset == 0xffff ) ) ) {
buffer = phys_to_user ( addr.buffer_phys );
DBGC2 ( sandev, "%08llx",
DBGC2 ( sandev->drive, "%08llx",
( ( unsigned long long ) addr.buffer_phys ) );
} else {
buffer = real_to_user ( addr.buffer.segment,
addr.buffer.offset );
DBGC2 ( sandev, "%04x:%04x", addr.buffer.segment,
DBGC2 ( sandev->drive, "%04x:%04x", addr.buffer.segment,
addr.buffer.offset );
}
if ( addr.count <= 0x7f ) {
@ -751,15 +757,15 @@ static int int13_extended_rw ( struct san_device *sandev,
} else if ( addr.count == 0xff ) {
count = addr.long_count;
} else {
DBGC2 ( sandev, " <invalid count %#02x>\n", addr.count );
DBGC2 ( sandev->drive, " <invalid count %#02x>\n", addr.count );
return -INT13_STATUS_INVALID;
}
DBGC2 ( sandev, " (count %ld)\n", count );
DBGC2 ( sandev->drive, " (count %ld)\n", count );
/* Read from / write to block device */
if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x extended I/O failed: %s\n",
sandev->drive, strerror ( rc ) );
DBGC ( sandev->drive, "INT13 drive %02x extended I/O failed: "
"%s\n", sandev->drive, strerror ( rc ) );
/* Record that no blocks were transferred successfully */
addr.count = 0;
put_real ( addr.count, ix86->segs.ds,
@ -781,7 +787,7 @@ static int int13_extended_rw ( struct san_device *sandev,
static int int13_extended_read ( struct san_device *sandev,
struct i386_all_regs *ix86 ) {
DBGC2 ( sandev, "Extended read: " );
DBGC2 ( sandev->drive, "Extended read: " );
return int13_extended_rw ( sandev, ix86, sandev_read );
}
@ -795,7 +801,7 @@ static int int13_extended_read ( struct san_device *sandev,
static int int13_extended_write ( struct san_device *sandev,
struct i386_all_regs *ix86 ) {
DBGC2 ( sandev, "Extended write: " );
DBGC2 ( sandev->drive, "Extended write: " );
return int13_extended_rw ( sandev, ix86, sandev_write );
}
@ -818,7 +824,7 @@ static int int13_extended_verify ( struct san_device *sandev,
sizeof ( addr ));
lba = addr.lba;
count = addr.count;
DBGC2 ( sandev, "Verify: LBA %08llx (count %ld)\n",
DBGC2 ( sandev->drive, "Verify: LBA %08llx (count %ld)\n",
( ( unsigned long long ) lba ), count );
}
@ -845,7 +851,7 @@ static int int13_extended_seek ( struct san_device *sandev,
sizeof ( addr ));
lba = addr.lba;
count = addr.count;
DBGC2 ( sandev, "Seek: LBA %08llx (count %ld)\n",
DBGC2 ( sandev->drive, "Seek: LBA %08llx (count %ld)\n",
( ( unsigned long long ) lba ), count );
}
@ -879,8 +885,8 @@ static int int13_device_path_info ( struct san_device *sandev,
/* Get underlying hardware device */
device = identify_device ( &sanpath->block );
if ( ! device ) {
DBGC ( sandev, "INT13 drive %02x cannot identify hardware "
"device\n", sandev->drive );
DBGC ( sandev->drive, "INT13 drive %02x cannot identify "
"hardware device\n", sandev->drive );
return -ENODEV;
}
@ -895,16 +901,16 @@ static int int13_device_path_info ( struct san_device *sandev,
dpi->interface_path.pci.channel = 0xff; /* unused */
break;
default:
DBGC ( sandev, "INT13 drive %02x unrecognised bus type %d\n",
sandev->drive, desc->bus_type );
DBGC ( sandev->drive, "INT13 drive %02x unrecognised bus "
"type %d\n", sandev->drive, desc->bus_type );
return -ENOTSUP;
}
/* Get EDD block device description */
if ( ( rc = edd_describe ( &sanpath->block, &dpi->interface_type,
&dpi->device_path ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x cannot identify block device: "
"%s\n", sandev->drive, strerror ( rc ) );
DBGC ( sandev->drive, "INT13 drive %02x cannot identify "
"block device: %s\n", sandev->drive, strerror ( rc ) );
return rc;
}
@ -938,8 +944,8 @@ static int int13_get_extended_parameters ( struct san_device *sandev,
get_real ( bufsize, ix86->segs.ds,
( ix86->regs.si + offsetof ( typeof ( params ), bufsize )));
DBGC2 ( sandev, "Get extended drive parameters to %04x:%04x+%02x\n",
ix86->segs.ds, ix86->regs.si, bufsize );
DBGC2 ( sandev->drive, "Get extended drive parameters to "
"%04x:%04x+%02x\n", ix86->segs.ds, ix86->regs.si, bufsize );
/* Build drive parameters */
memset ( &params, 0, sizeof ( params ) );
@ -955,8 +961,8 @@ static int int13_get_extended_parameters ( struct san_device *sandev,
params.sector_size = sandev_blksize ( sandev );
memset ( &params.dpte, 0xff, sizeof ( params.dpte ) );
if ( ( rc = int13_device_path_info ( sandev, &params.dpi ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x could not provide device "
"path information: %s\n",
DBGC ( sandev->drive, "INT13 drive %02x could not provide "
"device path information: %s\n",
sandev->drive, strerror ( rc ) );
len = offsetof ( typeof ( params ), dpi );
}
@ -973,11 +979,11 @@ static int int13_get_extended_parameters ( struct san_device *sandev,
params.bufsize = offsetof ( typeof ( params ), dpi );
}
DBGC ( sandev, "INT 13 drive %02x described using extended "
DBGC ( sandev->drive, "INT 13 drive %02x described using extended "
"parameters:\n", sandev->drive );
address.segment = ix86->segs.ds;
address.offset = ix86->regs.si;
DBGC_HDA ( sandev, address, &params, len );
DBGC_HDA ( sandev->drive, address, &params, len );
/* Return drive parameters */
if ( len > bufsize )
@ -998,13 +1004,13 @@ static int int13_cdrom_status_terminate ( struct san_device *sandev,
struct i386_all_regs *ix86 ) {
struct int13_cdrom_specification specification;
DBGC2 ( sandev, "Get CD-ROM emulation status to %04x:%04x%s\n",
DBGC2 ( sandev->drive, "Get CD-ROM emulation status to %04x:%04x%s\n",
ix86->segs.ds, ix86->regs.si,
( ix86->regs.al ? "" : " and terminate" ) );
/* Fail if we are not a CD-ROM */
if ( ! sandev->is_cdrom ) {
DBGC ( sandev, "INT13 drive %02x is not a CD-ROM\n",
DBGC ( sandev->drive, "INT13 drive %02x is not a CD-ROM\n",
sandev->drive );
return -INT13_STATUS_INVALID;
}
@ -1039,11 +1045,12 @@ static int int13_cdrom_read_boot_catalog ( struct san_device *sandev,
/* Read parameters from command packet */
copy_from_real ( &command, ix86->segs.ds, ix86->regs.si,
sizeof ( command ) );
DBGC2 ( sandev, "Read CD-ROM boot catalog to %08x\n", command.buffer );
DBGC2 ( sandev->drive, "Read CD-ROM boot catalog to %08x\n",
command.buffer );
/* Fail if we have no boot catalog */
if ( ! int13->boot_catalog ) {
DBGC ( sandev, "INT13 drive %02x has no boot catalog\n",
DBGC ( sandev->drive, "INT13 drive %02x has no boot catalog\n",
sandev->drive );
return -INT13_STATUS_INVALID;
}
@ -1052,8 +1059,8 @@ static int int13_cdrom_read_boot_catalog ( struct san_device *sandev,
/* Read from boot catalog */
if ( ( rc = sandev_read ( sandev, start, command.count,
phys_to_user ( command.buffer ) ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x could not read boot catalog: "
"%s\n", sandev->drive, strerror ( rc ) );
DBGC ( sandev->drive, "INT13 drive %02x could not read boot "
"catalog: %s\n", sandev->drive, strerror ( rc ) );
return -INT13_STATUS_READ_ERROR;
}
@ -1080,8 +1087,8 @@ static __asmcall __used void int13 ( struct i386_all_regs *ix86 ) {
if ( bios_drive != sandev->drive ) {
/* Remap any accesses to this drive's natural number */
if ( bios_drive == int13->natural_drive ) {
DBGC2 ( sandev, "INT13,%02x (%02x) remapped to "
"(%02x)\n", ix86->regs.ah,
DBGC2 ( sandev->drive, "INT13,%02x (%02x) "
"remapped to (%02x)\n", ix86->regs.ah,
bios_drive, sandev->drive );
ix86->regs.dl = sandev->drive;
return;
@ -1094,7 +1101,7 @@ static __asmcall __used void int13 ( struct i386_all_regs *ix86 ) {
}
}
DBGC2 ( sandev, "INT13,%02x (%02x): ",
DBGC2 ( sandev->drive, "INT13,%02x (%02x): ",
ix86->regs.ah, bios_drive );
switch ( command ) {
@ -1141,7 +1148,7 @@ static __asmcall __used void int13 ( struct i386_all_regs *ix86 ) {
status = int13_cdrom_read_boot_catalog ( sandev, ix86 );
break;
default:
DBGC2 ( sandev, "*** Unrecognised INT13 ***\n" );
DBGC2 ( sandev->drive, "*** Unrecognised INT13 ***\n" );
status = -INT13_STATUS_INVALID;
break;
}
@ -1152,8 +1159,9 @@ static __asmcall __used void int13 ( struct i386_all_regs *ix86 ) {
/* Negative status indicates an error */
if ( status < 0 ) {
status = -status;
DBGC ( sandev, "INT13,%02x (%02x) failed with status "
"%02x\n", ix86->regs.ah, sandev->drive, status );
DBGC ( sandev->drive, "INT13,%02x (%02x) failed with "
"status %02x\n", ix86->regs.ah, sandev->drive,
status );
} else {
ix86->flags &= ~CF;
}
@ -1269,7 +1277,7 @@ static int int13_hook ( unsigned int drive, struct uri **uris,
/* Register SAN device */
if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x could not register: %s\n",
DBGC ( drive, "INT13 drive %02x could not register: %s\n",
drive, strerror ( rc ) );
goto err_register;
}
@ -1289,10 +1297,9 @@ static int int13_hook ( unsigned int drive, struct uri **uris,
( ( rc = int13_guess_geometry ( sandev, scratch ) ) != 0 ) )
goto err_guess_geometry;
DBGC ( sandev, "INT13 drive %02x (naturally %02x) registered with "
"C/H/S geometry %d/%d/%d\n",
sandev->drive, int13->natural_drive, int13->cylinders,
int13->heads, int13->sectors_per_track );
DBGC ( drive, "INT13 drive %02x (naturally %02x) registered with "
"C/H/S geometry %d/%d/%d\n", drive, int13->natural_drive,
int13->cylinders, int13->heads, int13->sectors_per_track );
/* Hook INT 13 vector if not already hooked */
if ( need_hook ) {
@ -1332,7 +1339,7 @@ static void int13_unhook ( unsigned int drive ) {
/* Find drive */
sandev = sandev_find ( drive );
if ( ! sandev ) {
DBG ( "INT13 cannot find drive %02x\n", drive );
DBGC ( drive, "INT13 drive %02x is not a SAN drive\n", drive );
return;
}
@ -1343,7 +1350,7 @@ static void int13_unhook ( unsigned int drive ) {
* to do so reliably.
*/
DBGC ( sandev, "INT13 drive %02x unregistered\n", sandev->drive );
DBGC ( drive, "INT13 drive %02x unregistered\n", drive );
/* Unhook INT 13 vector if no more drives */
if ( ! have_sandevs() ) {
@ -1387,8 +1394,8 @@ static int int13_load_mbr ( unsigned int drive, struct segoff *address ) {
: "a" ( 0x0201 ), "b" ( *address ),
"c" ( 1 ), "d" ( drive ) );
if ( status ) {
DBG ( "INT13 drive %02x could not read MBR (status %04x)\n",
drive, status );
DBGC ( drive, "INT13 drive %02x could not read MBR (status "
"%04x)\n", drive, status );
return -EIO;
}
@ -1397,8 +1404,8 @@ static int int13_load_mbr ( unsigned int drive, struct segoff *address ) {
( address->offset +
offsetof ( struct master_boot_record, magic ) ) );
if ( magic != INT13_MBR_MAGIC ) {
DBG ( "INT13 drive %02x does not contain a valid MBR\n",
drive );
DBGC ( drive, "INT13 drive %02x does not contain a valid MBR\n",
drive );
return -ENOEXEC;
}
@ -1444,8 +1451,8 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
: "a" ( 0x4d00 ), "d" ( drive ),
"S" ( __from_data16 ( &eltorito_cmd ) ) );
if ( status ) {
DBG ( "INT13 drive %02x could not read El Torito boot catalog "
"(status %04x)\n", drive, status );
DBGC ( drive, "INT13 drive %02x could not read El Torito boot "
"catalog (status %04x)\n", drive, status );
return -EIO;
}
copy_from_user ( &catalog, phys_to_user ( eltorito_cmd.buffer ), 0,
@ -1453,26 +1460,27 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
/* Sanity checks */
if ( catalog.valid.platform_id != ELTORITO_PLATFORM_X86 ) {
DBG ( "INT13 drive %02x El Torito specifies unknown platform "
"%02x\n", drive, catalog.valid.platform_id );
DBGC ( drive, "INT13 drive %02x El Torito specifies unknown "
"platform %02x\n", drive, catalog.valid.platform_id );
return -ENOEXEC;
}
if ( catalog.boot.indicator != ELTORITO_BOOTABLE ) {
DBG ( "INT13 drive %02x El Torito is not bootable\n", drive );
DBGC ( drive, "INT13 drive %02x El Torito is not bootable\n",
drive );
return -ENOEXEC;
}
if ( catalog.boot.media_type != ELTORITO_NO_EMULATION ) {
DBG ( "INT13 drive %02x El Torito requires emulation "
DBGC ( drive, "INT13 drive %02x El Torito requires emulation "
"type %02x\n", drive, catalog.boot.media_type );
return -ENOTSUP;
}
DBG ( "INT13 drive %02x El Torito boot image at LBA %08x (count %d)\n",
drive, catalog.boot.start, catalog.boot.length );
DBGC ( drive, "INT13 drive %02x El Torito boot image at LBA %08x "
"(count %d)\n", drive, catalog.boot.start, catalog.boot.length );
address->segment = ( catalog.boot.load_segment ?
catalog.boot.load_segment : 0x7c0 );
address->offset = 0;
DBG ( "INT13 drive %02x El Torito boot image loads at %04x:%04x\n",
drive, address->segment, address->offset );
DBGC ( drive, "INT13 drive %02x El Torito boot image loads at "
"%04x:%04x\n", drive, address->segment, address->offset );
/* Use INT 13, 42 to read the boot image */
eltorito_address.bufsize =
@ -1491,8 +1499,8 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
: "a" ( 0x4200 ), "d" ( drive ),
"S" ( __from_data16 ( &eltorito_address ) ) );
if ( status ) {
DBG ( "INT13 drive %02x could not read El Torito boot image "
"(status %04x)\n", drive, status );
DBGC ( drive, "INT13 drive %02x could not read El Torito boot "
"image (status %04x)\n", drive, status );
return -EIO;
}
@ -1503,7 +1511,7 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
* Attempt to boot from an INT 13 drive
*
* @v drive Drive number
* @v filename Filename (or NULL to use default)
* @v config Boot configuration parameters
* @ret rc Return status code
*
* This boots from the specified INT 13 drive by loading the Master
@ -1513,7 +1521,8 @@ static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
*
* Note that this function can never return success, by definition.
*/
static int int13_boot ( unsigned int drive, const char *filename __unused ) {
static int int13_boot ( unsigned int drive,
struct san_boot_config *config __unused ) {
struct memory_map memmap;
struct segoff address;
int rc;
@ -1533,8 +1542,8 @@ static int int13_boot ( unsigned int drive, const char *filename __unused ) {
/* Jump to boot sector */
if ( ( rc = call_bootsector ( address.segment, address.offset,
drive ) ) != 0 ) {
DBG ( "INT13 drive %02x boot returned: %s\n",
drive, strerror ( rc ) );
DBGC ( drive, "INT13 drive %02x boot returned: %s\n",
drive, strerror ( rc ) );
return rc;
}

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include <librm.h>
.section ".note.GNU-stack", "", @progbits
.code16
.arch i386
/****************************************************************************

View File

@ -6,10 +6,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#define STACK_SIZE 0x2000
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.section ".prefix", "awx", @progbits
.code16
/*
* Find active partition

View File

@ -26,10 +26,9 @@ FILE_LICENCE ( GPL2_ONLY )
.section ".note.GNU-stack", "", @progbits
.org 0
.arch i386
.text
.section ".prefix", "ax", @progbits
.code16
.arch i386
.section ".prefix", "ax", @progbits
.globl _dsk_start
_dsk_start:

View File

@ -37,10 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#define PSP_CMDLINE_START 0x81
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.org 0
.code16
.section ".prefix", "awx", @progbits
signature:

View File

@ -3,10 +3,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include <librm.h>
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.section ".prefix", "awx", @progbits
.code16
.org 0
.globl _hd_start
_hd_start:

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include <librm.h>
.section ".note.GNU-stack", "", @progbits
.code16
.arch i386
/* Image compression enabled */

View File

@ -5,9 +5,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#define BZI_LOAD_HIGH_ADDR 0x100000
.section ".note.GNU-stack", "", @progbits
.text
.arch i386
.code16
.arch i386
.section ".prefix", "ax", @progbits
.globl _lkrn_start
_lkrn_start:

View File

@ -1,10 +1,9 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.section ".prefix", "awx", @progbits
.code16
.org 0
.globl mbr

View File

@ -42,9 +42,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include "pciromprefix.S"
.section ".note.GNU-stack", "", @progbits
.text
.arch i386
.code16
.arch i386
/* Obtain access to payload by exposing the expansion ROM BAR at the
* address currently used by a suitably large memory BAR on the same

View File

@ -3,9 +3,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include <librm.h>
.section ".note.GNU-stack", "", @progbits
.text
.arch i386
.code16
.arch i386
.section ".prefix", "ax", @progbits
.org 0

View File

@ -2,11 +2,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.org 0
.text
.code16
.arch i386
.section ".prefix", "ax", @progbits
.code16
_prefix:
.section ".text16", "ax", @progbits

View File

@ -12,10 +12,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#define PXE_HACK_EB54 0x0001
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.org 0
.code16
#include <librm.h>
#include <undi.h>

View File

@ -9,10 +9,9 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.org 0
.code16
#include <librm.h>

View File

@ -55,7 +55,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#endif
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.section ".prefix", "ax", @progbits

View File

@ -3,7 +3,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include <librm.h>
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.section ".prefix", "ax", @progbits

View File

@ -44,7 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
.section ".note.GNU-stack", "", @progbits
.text
.code32
.arch i486
.section ".prefix.lib", "ax", @progbits

View File

@ -3,10 +3,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include <config/console.h>
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.section ".prefix", "awx", @progbits
.code16
.org 0
#include "mbr.S"

View File

@ -25,6 +25,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.section ".note.GNU-stack", "", @progbits
.code16
.arch i386
/****************************************************************************

View File

@ -32,10 +32,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#define BOCHSBP xchgw %bx, %bx
.section ".note.GNU-stack", "", @progbits
.text
.code16
.arch i386
.section ".text16", "awx", @progbits
.code16
/****************************************************************************
* init_libkir (real-mode or 16:xx protected-mode far call)

View File

@ -1632,3 +1632,70 @@ init_pages:
/* Return */
ret
/****************************************************************************
* sipi (real-mode jump)
*
* Handle Startup IPI
*
* This code must be copied to a page-aligned boundary in base memory.
* It will be entered with %cs:0000 pointing to the start of the code.
* The stack pointer is undefined and so no stack space can be used.
*
****************************************************************************
*/
.section ".text16.sipi", "ax", @progbits
.code16
.globl sipi
sipi:
/* Retrieve rm_ds from copy */
movw %cs:( sipi_ds - sipi ), %ax
movw %ax, %ds
/* Load GDT and switch to protected mode */
data32 lgdt gdtr
movl %cr0, %eax
orb $CR0_PE, %al
movl %eax, %cr0
data32 ljmp $VIRTUAL_CS, $VIRTUAL(1f)
/* Copy of rm_ds required to access GDT */
.globl sipi_ds
sipi_ds:
.word 0
/* Length of real-mode SIPI handler to be copied */
.globl sipi_len
.equ sipi_len, . - sipi
.section ".text.sipi", "ax", @progbits
.code32
1: /* Set up protected-mode segment registers (with no stack) */
movw $VIRTUAL_DS, %ax
movw %ax, %ds
movw %ax, %ss
movw $PHYSICAL_DS, %ax
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
/* Load register state and clear stack pointer */
movl $VIRTUAL(sipi_regs), %esp
popal
/* Switch to flat physical addressing */
movw $PHYSICAL_DS, %sp
movw %sp, %ds
movw %sp, %ss
/* Clear stack pointer */
xorl %esp, %esp
/* Jump to protected-mode SIPI handler */
ljmp %cs:*VIRTUAL(sipi_handler)
/* Protected-mode SIPI handler vector */
.section ".data.sipi_handler", "aw", @progbits
.globl sipi_handler
sipi_handler:
.long 0, PHYSICAL_CS

View File

@ -45,6 +45,9 @@ struct idtr64 idtr64 = {
.limit = ( sizeof ( idt64 ) - 1 ),
};
/** Startup IPI register state */
struct i386_regs sipi_regs;
/** Length of stack dump */
#define STACK_DUMP_LEN 128
@ -402,6 +405,29 @@ __asmcall void check_fxsr ( struct i386_all_regs *regs ) {
( ( regs->flags & CF ) ? " not" : "" ) );
}
/**
* Set up startup IPI handler
*
* @v vector Startup IPI vector
* @v handler Protected-mode startup IPI handler physical address
* @v regs Initial register state
*/
void setup_sipi ( unsigned int vector, uint32_t handler,
struct i386_regs *regs ) {
/* Record protected-mode handler */
sipi_handler = handler;
/* Update copy of rm_ds */
sipi_ds = rm_ds;
/* Save register state */
memcpy ( &sipi_regs, regs, sizeof ( sipi_regs ) );
/* Copy real-mode handler */
copy_to_real ( ( vector << 8 ), 0, sipi, ( ( size_t ) sipi_len ) );
}
PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
PROVIDE_UACCESS_INLINE ( librm, virt_to_user );

View File

@ -188,6 +188,9 @@ REQUIRE_OBJECT ( zlib );
#ifdef IMAGE_GZIP
REQUIRE_OBJECT ( gzip );
#endif
#ifdef IMAGE_UCODE
REQUIRE_OBJECT ( ucode );
#endif
/*
* Drag in all requested commands

View File

@ -83,6 +83,11 @@ REQUIRE_OBJECT ( oid_sha512_224 );
REQUIRE_OBJECT ( oid_sha512_256 );
#endif
/* X25519 */
#if defined ( CRYPTO_CURVE_X25519 )
REQUIRE_OBJECT ( oid_x25519 );
#endif
/* RSA and MD5 */
#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_MD5 )
REQUIRE_OBJECT ( rsa_md5 );
@ -114,25 +119,79 @@ REQUIRE_OBJECT ( rsa_sha512 );
#endif
/* RSA, AES-CBC, and SHA-1 */
#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_CBC ) && \
defined ( CRYPTO_DIGEST_SHA1 )
#if defined ( CRYPTO_EXCHANGE_PUBKEY ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_CBC ) && defined ( CRYPTO_DIGEST_SHA1 )
REQUIRE_OBJECT ( rsa_aes_cbc_sha1 );
#endif
/* RSA, AES-CBC, and SHA-256 */
#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_CBC ) && \
defined ( CRYPTO_DIGEST_SHA256 )
#if defined ( CRYPTO_EXCHANGE_PUBKEY ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_CBC ) && defined ( CRYPTO_DIGEST_SHA256 )
REQUIRE_OBJECT ( rsa_aes_cbc_sha256 );
#endif
/* RSA, AES-GCM, and SHA-256 */
#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_GCM ) && \
defined ( CRYPTO_DIGEST_SHA256 )
#if defined ( CRYPTO_EXCHANGE_PUBKEY ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_GCM ) && defined ( CRYPTO_DIGEST_SHA256 )
REQUIRE_OBJECT ( rsa_aes_gcm_sha256 );
#endif
/* RSA, AES-GCM, and SHA-384 */
#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_GCM ) && \
defined ( CRYPTO_DIGEST_SHA384 )
#if defined ( CRYPTO_EXCHANGE_PUBKEY ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_GCM ) && defined ( CRYPTO_DIGEST_SHA384 )
REQUIRE_OBJECT ( rsa_aes_gcm_sha384 );
#endif
/* DHE, RSA, AES-CBC, and SHA-1 */
#if defined ( CRYPTO_EXCHANGE_DHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_CBC ) && defined ( CRYPTO_DIGEST_SHA1 )
REQUIRE_OBJECT ( dhe_rsa_aes_cbc_sha1 );
#endif
/* DHE, RSA, AES-CBC, and SHA-256 */
#if defined ( CRYPTO_EXCHANGE_DHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_CBC ) && defined ( CRYPTO_DIGEST_SHA256 )
REQUIRE_OBJECT ( dhe_rsa_aes_cbc_sha256 );
#endif
/* DHE, RSA, AES-GCM, and SHA-256 */
#if defined ( CRYPTO_EXCHANGE_DHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_GCM ) && defined ( CRYPTO_DIGEST_SHA256 )
REQUIRE_OBJECT ( dhe_rsa_aes_gcm_sha256 );
#endif
/* DHE, RSA, AES-GCM, and SHA-384 */
#if defined ( CRYPTO_EXCHANGE_DHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_GCM ) && defined ( CRYPTO_DIGEST_SHA384 )
REQUIRE_OBJECT ( dhe_rsa_aes_gcm_sha384 );
#endif
/* ECDHE, RSA, AES-CBC, and SHA-1 */
#if defined ( CRYPTO_EXCHANGE_ECDHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_CBC ) && defined ( CRYPTO_DIGEST_SHA1 )
REQUIRE_OBJECT ( ecdhe_rsa_aes_cbc_sha1 );
#endif
/* ECDHE, RSA, AES-CBC, and SHA-256 */
#if defined ( CRYPTO_EXCHANGE_ECDHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_CBC ) && defined ( CRYPTO_DIGEST_SHA256 )
REQUIRE_OBJECT ( ecdhe_rsa_aes_cbc_sha256 );
#endif
/* ECDHE, RSA, AES-CBC, and SHA-384 */
#if defined ( CRYPTO_EXCHANGE_ECDHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_CBC ) && defined ( CRYPTO_DIGEST_SHA384 )
REQUIRE_OBJECT ( ecdhe_rsa_aes_cbc_sha384 );
#endif
/* ECDHE, RSA, AES-GCM, and SHA-256 */
#if defined ( CRYPTO_EXCHANGE_ECDHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_GCM ) && defined ( CRYPTO_DIGEST_SHA256 )
REQUIRE_OBJECT ( ecdhe_rsa_aes_gcm_sha256 );
#endif
/* ECDHE, RSA, AES-GCM, and SHA-384 */
#if defined ( CRYPTO_EXCHANGE_ECDHE ) && defined ( CRYPTO_PUBKEY_RSA ) && \
defined ( CRYPTO_CIPHER_AES_GCM ) && defined ( CRYPTO_DIGEST_SHA384 )
REQUIRE_OBJECT ( ecdhe_rsa_aes_gcm_sha384 );
#endif

42
src/config/config_eap.c Normal file
View File

@ -0,0 +1,42 @@
/*
* 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 <config/general.h>
/** @file
*
* EAP configuration options
*
*/
PROVIDE_REQUIRING_SYMBOL();
/*
* Drag in EAP authentication methods
*/
#ifdef EAP_METHOD_MD5
REQUIRE_OBJECT ( eap_md5 );
#endif
#ifdef EAP_METHOD_MSCHAPV2
REQUIRE_OBJECT ( eap_mschapv2 );
#endif

View File

@ -12,6 +12,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Minimum TLS version */
#define TLS_VERSION_MIN TLS_VERSION_TLS_1_1
/** Public-key exchange algorithm */
#define CRYPTO_EXCHANGE_PUBKEY
/** DHE key exchange algorithm */
#define CRYPTO_EXCHANGE_DHE
/** ECDHE key exchange algorithm */
#define CRYPTO_EXCHANGE_ECDHE
/** RSA public-key algorithm */
#define CRYPTO_PUBKEY_RSA
@ -48,6 +57,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** SHA-512/256 digest algorithm */
//#define CRYPTO_DIGEST_SHA512_256
/** X25519 elliptic curve */
#define CRYPTO_CURVE_X25519
/** Margin of error (in seconds) allowed in signed timestamps
*
* We default to allowing a reasonable margin of error: 12 hours to

View File

@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define REBOOT_EFI
#define ACPI_EFI
#define FDT_EFI
#define MPAPI_EFI
#define NET_PROTO_IPV6 /* IPv6 protocol */
#define NET_PROTO_LLDP /* Link Layer Discovery protocol */

View File

@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define PCIAPI_LINUX
#define DMAAPI_FLAT
#define ACPI_LINUX
#define MPAPI_NULL
#define DRIVERS_LINUX

View File

@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define TIME_RTC
#define REBOOT_PCBIOS
#define ACPI_RSDP
#define MPAPI_PCBIOS
#ifdef __x86_64__
#define IOMAP_PAGES

View File

@ -91,6 +91,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define CRYPTO_80211_WPA /* WPA Personal, authenticating with passphrase */
#define CRYPTO_80211_WPA2 /* Add support for stronger WPA cryptography */
/*
* 802.1x EAP authentication methods
*
*/
#define EAP_METHOD_MD5 /* MD5-Challenge port authentication */
//#define EAP_METHOD_MSCHAPV2 /* MS-CHAPv2 port authentication */
/*
* Name resolution modules
*
@ -120,6 +127,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define IMAGE_PEM /* PEM image support */
//#define IMAGE_ZLIB /* ZLIB image support */
//#define IMAGE_GZIP /* GZIP image support */
//#define IMAGE_UCODE /* Microcode update image support */
/*
* Command-line commands to include

View File

@ -78,12 +78,23 @@ int hex_decode ( char separator, const char *encoded, void *data, size_t len ) {
unsigned int count = 0;
unsigned int sixteens;
unsigned int units;
int optional;
/* Strip out optionality flag from separator character */
optional = ( separator & HEX_DECODE_OPTIONAL );
separator &= ~HEX_DECODE_OPTIONAL;
/* Decode string */
while ( *encoded ) {
/* Check separator, if applicable */
if ( count && separator && ( ( *(encoded++) != separator ) ) )
return -EINVAL;
if ( count && separator ) {
if ( *encoded == separator ) {
encoded++;
} else if ( ! optional ) {
return -EINVAL;
}
}
/* Extract digits. Note that either digit may be NUL,
* which would be interpreted as an invalid value by

View File

@ -55,7 +55,7 @@ static int dummy_san_hook ( unsigned int drive, struct uri **uris,
/* Register SAN device */
if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x could not register: %s\n",
DBGC ( sandev->drive, "SAN %#02x could not register: %s\n",
sandev->drive, strerror ( rc ) );
goto err_register;
}
@ -80,7 +80,7 @@ static void dummy_san_unhook ( unsigned int drive ) {
/* Find drive */
sandev = sandev_find ( drive );
if ( ! sandev ) {
DBG ( "SAN %#02x does not exist\n", drive );
DBGC ( drive, "SAN %#02x does not exist\n", drive );
return;
}
@ -95,11 +95,11 @@ static void dummy_san_unhook ( unsigned int drive ) {
* Boot from dummy SAN device
*
* @v drive Drive number
* @v filename Filename (or NULL to use default)
* @v config Boot configuration parameters
* @ret rc Return status code
*/
static int dummy_san_boot ( unsigned int drive __unused,
const char *filename __unused ) {
struct san_boot_config *config __unused ) {
return -EOPNOTSUPP;
}

67
src/core/mp.c Normal file
View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2024 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
*
* Multiprocessor functions
*
*/
#include <ipxe/timer.h>
#include <ipxe/mp.h>
/** Time to wait for application processors */
#define MP_MAX_CPUID_WAIT_MS 10
/**
* Get boot CPU identifier
*
* @ret id Boot CPU identifier
*/
unsigned int mp_boot_cpuid ( void ) {
unsigned int max = 0;
/* Update maximum to accommodate boot processor */
mp_exec_boot ( mp_update_max_cpuid, &max );
DBGC ( &mp_call, "MP boot processor ID is %#x\n", max );
return max;
}
/**
* Get maximum CPU identifier
*
* @ret max Maximum CPU identifier
*/
unsigned int mp_max_cpuid ( void ) {
unsigned int max = mp_boot_cpuid();
/* Update maximum to accommodate application processors */
mp_start_all ( mp_update_max_cpuid, &max );
mdelay ( MP_MAX_CPUID_WAIT_MS );
DBGC ( &mp_call, "MP observed maximum CPU ID is %#x\n", max );
return max;
}

37
src/core/null_mp.c Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2024 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
*
* Null multiprocessor API
*
*/
#include <ipxe/mp.h>
PROVIDE_MPAPI_INLINE ( null, mp_address );
PROVIDE_MPAPI_INLINE ( null, mp_exec_boot );
PROVIDE_MPAPI_INLINE ( null, mp_start_all );

View File

@ -38,7 +38,7 @@ static void null_san_unhook ( unsigned int drive __unused ) {
}
static int null_san_boot ( unsigned int drive __unused,
const char *filename __unused ) {
struct san_boot_config *config __unused ) {
return -EOPNOTSUPP;
}

View File

@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <ipxe/uuid.h>
#include <ipxe/netdevice.h>
#include <ipxe/menu.h>
#include <ipxe/settings.h>
@ -124,6 +125,29 @@ int parse_timeout ( char *text, unsigned long *value ) {
return 0;
}
/**
* Parse UUID
*
* @v text Text
* @ret uuid UUID value
* @ret rc Return status code
*/
int parse_uuid ( char *text, struct uuid_option *uuid ) {
int rc;
/* Sanity check */
assert ( text != NULL );
/* Parse UUID */
if ( ( rc = uuid_aton ( text, &uuid->buf ) ) != 0 ) {
printf ( "\"%s\": invalid UUID\n", text );
return rc;
}
uuid->value = &uuid->buf;
return 0;
}
/**
* Parse network device name
*

View File

@ -44,16 +44,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/quiesce.h>
#include <ipxe/sanboot.h>
/**
* Default SAN drive number
*
* The drive number is a meaningful concept only in a BIOS
* environment, where it represents the INT13 drive number (0x80 for
* the first hard disk). We retain it in other environments to allow
* for a simple way for iPXE commands to refer to SAN drives.
*/
#define SAN_DEFAULT_DRIVE 0x80
/**
* Timeout for block device commands (in ticks)
*
@ -107,6 +97,22 @@ struct san_device * sandev_find ( unsigned int drive ) {
return NULL;
}
/**
* Find next SAN device by drive number
*
* @v drive Minimum drive number
* @ret sandev SAN device, or NULL
*/
struct san_device * sandev_next ( unsigned int drive ) {
struct san_device *sandev;
list_for_each_entry ( sandev, &san_devices, list ) {
if ( sandev->drive >= drive )
return sandev;
}
return NULL;
}
/**
* Free SAN device
*
@ -197,7 +203,7 @@ static int sanpath_open ( struct san_path *sanpath ) {
/* Open interface */
if ( ( rc = xfer_open_uri ( &sanpath->block, sanpath->uri ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not (re)open URI: "
DBGC ( sandev->drive, "SAN %#02x.%d could not (re)open URI: "
"%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
return rc;
}
@ -265,7 +271,7 @@ static void sanpath_block_close ( struct san_path *sanpath, int rc ) {
/* Any closure is an error from our point of view */
if ( rc == 0 )
rc = -ENOTCONN;
DBGC ( sandev, "SAN %#02x.%d closed: %s\n",
DBGC ( sandev->drive, "SAN %#02x.%d closed: %s\n",
sandev->drive, sanpath->index, strerror ( rc ) );
/* Close path */
@ -307,11 +313,11 @@ static void sanpath_step ( struct san_path *sanpath ) {
/* Mark as active path or close as applicable */
if ( ! sandev->active ) {
DBGC ( sandev, "SAN %#02x.%d is active\n",
DBGC ( sandev->drive, "SAN %#02x.%d is active\n",
sandev->drive, sanpath->index );
sandev->active = sanpath;
} else {
DBGC ( sandev, "SAN %#02x.%d is available\n",
DBGC ( sandev->drive, "SAN %#02x.%d is available\n",
sandev->drive, sanpath->index );
sanpath_close ( sanpath, 0 );
}
@ -398,8 +404,9 @@ int sandev_reopen ( struct san_device *sandev ) {
rc = sanpath->path_rc;
break;
}
DBGC ( sandev, "SAN %#02x never became available: %s\n",
sandev->drive, strerror ( rc ) );
DBGC ( sandev->drive, "SAN %#02x never became "
"available: %s\n", sandev->drive,
strerror ( rc ) );
goto err_none;
}
}
@ -453,8 +460,9 @@ static int sandev_command_rw ( struct san_device *sandev,
if ( ( rc = params->rw.block_rw ( &sanpath->block, &sandev->command,
params->rw.lba, params->rw.count,
params->rw.buffer, len ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not initiate read/write: "
"%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
DBGC ( sandev->drive, "SAN %#02x.%d could not initiate "
"read/write: %s\n", sandev->drive, sanpath->index,
strerror ( rc ) );
return rc;
}
@ -480,8 +488,9 @@ sandev_command_read_capacity ( struct san_device *sandev,
/* Initiate read capacity command */
if ( ( rc = block_read_capacity ( &sanpath->block,
&sandev->command ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not initiate read capacity: "
"%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
DBGC ( sandev->drive, "SAN %#02x.%d could not initiate read "
"capacity: %s\n", sandev->drive, sanpath->index,
strerror ( rc ) );
return rc;
}
@ -565,7 +574,7 @@ sandev_command ( struct san_device *sandev,
int sandev_reset ( struct san_device *sandev ) {
int rc;
DBGC ( sandev, "SAN %#02x reset\n", sandev->drive );
DBGC ( sandev->drive, "SAN %#02x reset\n", sandev->drive );
/* Close and reopen underlying block device */
if ( ( rc = sandev_reopen ( sandev ) ) != 0 )
@ -698,8 +707,8 @@ static int sandev_describe ( struct san_device *sandev ) {
if ( ! desc )
continue;
if ( ( rc = desc->model->complete ( desc ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x.%d could not be "
"described: %s\n", sandev->drive,
DBGC ( sandev->drive, "SAN %#02x.%d could not "
"be described: %s\n", sandev->drive,
sanpath->index, strerror ( rc ) );
return rc;
}
@ -792,8 +801,8 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) {
/* Read primary volume descriptor */
if ( ( rc = sandev_read ( sandev, lba, count,
virt_to_user ( scratch ) ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x could not read ISO9660 primary"
"volume descriptor: %s\n",
DBGC ( sandev->drive, "SAN %#02x could not read ISO9660 "
"primary volume descriptor: %s\n",
sandev->drive, strerror ( rc ) );
goto err_rw;
}
@ -801,8 +810,8 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) {
/* Configure as CD-ROM if applicable */
if ( memcmp ( &scratch->primary.fixed, &primary_check,
sizeof ( primary_check ) ) == 0 ) {
DBGC ( sandev, "SAN %#02x contains an ISO9660 filesystem; "
"treating as CD-ROM\n", sandev->drive );
DBGC ( sandev->drive, "SAN %#02x contains an ISO9660 "
"filesystem; treating as CD-ROM\n", sandev->drive );
sandev->blksize_shift = blksize_shift;
sandev->is_cdrom = 1;
}
@ -867,11 +876,12 @@ struct san_device * alloc_sandev ( struct uri **uris, unsigned int count,
*/
int register_sandev ( struct san_device *sandev, unsigned int drive,
unsigned int flags ) {
struct san_device *before;
int rc;
/* Check that drive number is not in use */
if ( sandev_find ( drive ) != NULL ) {
DBGC ( sandev, "SAN %#02x is already in use\n", drive );
DBGC ( sandev->drive, "SAN %#02x is already in use\n", drive );
rc = -EADDRINUSE;
goto err_in_use;
}
@ -900,9 +910,13 @@ int register_sandev ( struct san_device *sandev, unsigned int drive,
if ( ( rc = sandev_parse_iso9660 ( sandev ) ) != 0 )
goto err_iso9660;
/* Add to list of SAN devices */
list_add_tail ( &sandev->list, &san_devices );
DBGC ( sandev, "SAN %#02x registered\n", sandev->drive );
/* Add to list of SAN devices, in drive order */
for_each_sandev ( before ) {
if ( before->drive > sandev->drive )
break;
}
list_add_tail ( &sandev->list, &before->list );
DBGC ( sandev->drive, "SAN %#02x registered\n", sandev->drive );
return 0;
@ -936,7 +950,7 @@ void unregister_sandev ( struct san_device *sandev ) {
/* Remove ACPI descriptors */
sandev_undescribe ( sandev );
DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive );
DBGC ( sandev->drive, "SAN %#02x unregistered\n", sandev->drive );
}
/** The "san-drive" setting */

View File

@ -2194,6 +2194,37 @@ const struct setting_type setting_type_base64 __setting_type = {
.format = format_base64_setting,
};
/**
* Parse UUID/GUID setting value
*
* @v type Setting type
* @v value Formatted setting value
* @v buf Buffer to contain raw value
* @v len Length of buffer
* @v size Integer size, in bytes
* @ret len Length of raw value, or negative error
*/
static int parse_uuid_setting ( const struct setting_type *type,
const char *value, void *buf, size_t len ) {
union uuid uuid;
int rc;
/* Parse UUID */
if ( ( rc = uuid_aton ( value, &uuid ) ) != 0 )
return rc;
/* Mangle GUID byte ordering */
if ( type == &setting_type_guid )
uuid_mangle ( &uuid );
/* Copy value */
if ( len > sizeof ( uuid ) )
len = sizeof ( uuid );
memcpy ( buf, uuid.raw, len );
return ( sizeof ( uuid ) );
}
/**
* Format UUID/GUID setting value
*
@ -2227,12 +2258,14 @@ static int format_uuid_setting ( const struct setting_type *type,
/** UUID setting type */
const struct setting_type setting_type_uuid __setting_type = {
.name = "uuid",
.parse = parse_uuid_setting,
.format = format_uuid_setting,
};
/** GUID setting type */
const struct setting_type setting_type_guid __setting_type = {
.name = "guid",
.parse = parse_uuid_setting,
.format = format_uuid_setting,
};
@ -2617,6 +2650,113 @@ struct builtin_setting unixtime_builtin_setting __builtin_setting = {
.fetch = unixtime_fetch,
};
/**
* Fetch current working URI-related setting
*
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @v rel Relative URI string
* @ret len Length of setting data, or negative error
*/
static int cwuri_fetch_uri ( void *data, size_t len, const char *rel ) {
struct uri *reluri;
struct uri *uri;
char *uristring;
int ret;
/* Check that current working URI is set */
if ( ! cwuri ) {
ret = -ENOENT;
goto err_unset;
}
/* Construct relative URI */
reluri = parse_uri ( rel );
if ( ! reluri ) {
ret = -ENOMEM;
goto err_parse;
}
/* Construct resolved URI */
uri = resolve_uri ( cwuri, reluri );
if ( ! uri ) {
ret = -ENOMEM;
goto err_resolve;
}
/* Format URI string into allocated buffer (with NUL) */
uristring = format_uri_alloc ( uri );
if ( ! uristring ) {
ret = -ENOMEM;
goto err_format;
}
/* Copy URI string to buffer */
strncpy ( data, uristring, len );
ret = strlen ( uristring );
free ( uristring );
err_format:
uri_put ( uri );
err_resolve:
uri_put ( reluri );
err_parse:
err_unset:
return ret;
}
/**
* Fetch current working URI setting
*
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int cwuri_fetch ( void *data, size_t len ) {
return cwuri_fetch_uri ( data, len, "" );
}
/**
* Fetch current working directory URI setting
*
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int cwduri_fetch ( void *data, size_t len ) {
return cwuri_fetch_uri ( data, len, "." );
}
/** Current working URI setting */
const struct setting cwuri_setting __setting ( SETTING_MISC, cwuri ) = {
.name = "cwuri",
.description = "Current working URI",
.type = &setting_type_string,
.scope = &builtin_scope,
};
/** Current working directory URI setting */
const struct setting cwduri_setting __setting ( SETTING_MISC, cwduri ) = {
.name = "cwduri",
.description = "Current working directory URI",
.type = &setting_type_string,
.scope = &builtin_scope,
};
/** Current working URI built-in setting */
struct builtin_setting cwuri_builtin_setting __builtin_setting = {
.setting = &cwuri_setting,
.fetch = cwuri_fetch,
};
/** Current working directory URI built-in setting */
struct builtin_setting cwduri_builtin_setting __builtin_setting = {
.setting = &cwduri_setting,
.fetch = cwduri_fetch,
};
/**
* Fetch built-in setting
*

View File

@ -25,7 +25,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/base16.h>
#include <ipxe/uuid.h>
/** @file
@ -53,3 +55,29 @@ const char * uuid_ntoa ( const union uuid *uuid ) {
uuid->canonical.e[4], uuid->canonical.e[5] );
return buf;
}
/**
* Parse UUID
*
* @v string UUID string
* @v uuid UUID to fill in
* @ret rc Return status code
*/
int uuid_aton ( const char *string, union uuid *uuid ) {
int len;
int rc;
/* Decode as hex string with optional '-' separator */
len = hex_decode ( ( '-' | HEX_DECODE_OPTIONAL ), string, uuid->raw,
sizeof ( *uuid ) );
if ( len < 0 ) {
rc = len;
return rc;
}
/* Check length */
if ( len != sizeof ( *uuid ) )
return -EINVAL;
return 0;
}

View File

@ -50,6 +50,31 @@ static struct profiler bigint_mod_multiply_rescale_profiler __profiler =
static struct profiler bigint_mod_multiply_subtract_profiler __profiler =
{ .name = "bigint_mod_multiply.subtract" };
/**
* Conditionally swap big integers (in constant time)
*
* @v first0 Element 0 of big integer to be conditionally swapped
* @v second0 Element 0 of big integer to be conditionally swapped
* @v size Number of elements in big integers
* @v swap Swap first and second big integers
*/
void bigint_swap_raw ( bigint_element_t *first0, bigint_element_t *second0,
unsigned int size, int swap ) {
bigint_element_t mask;
bigint_element_t xor;
unsigned int i;
/* Construct mask */
mask = ( ( bigint_element_t ) ( ! swap ) - 1 );
/* Conditionally swap elements */
for ( i = 0 ; i < size ; i++ ) {
xor = ( mask & ( first0[i] ^ second0[i] ) );
first0[i] ^= xor;
second0[i] ^= xor;
}
}
/**
* Perform modular multiplication of big integers
*

695
src/crypto/des.c Normal file
View File

@ -0,0 +1,695 @@
/*
* Copyright (C) 2024 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
*
* DES algorithm
*
* DES was not designed to be implemented in software, and therefore
* contains a large number of bit permutation operations that are
* essentially free in hardware (requiring only wires, no gates) but
* expensive in software.
*
* Since DES is no longer used as a practical block cipher for large
* volumes of data, we optimise for code size, and do not attempt to
* obtain fast throughput.
*
* The algorithm is specified in NIST SP 800-67, downloadable from
* https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf
*/
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/rotate.h>
#include <ipxe/crypto.h>
#include <ipxe/ecb.h>
#include <ipxe/cbc.h>
#include <ipxe/init.h>
#include <ipxe/des.h>
/**
* DES shift schedule
*
* The DES shift schedule (ordered from round 16 down to round 1) is
* {1,2,2,2,2,2,2,1,2,2,2,2,2,2,1,1}. In binary, this may be
* represented as {1,10,10,10,10,10,10,1,10,10,10,10,10,10,1,1} and
* concatenated (without padding) to produce a single binary integer
* 1101010101010110101010101011 (equal to 0x0d556aab in hexadecimal).
*
* This integer may then be consumed LSB-first, where a 1 bit
* indicates a shift and the generation of a round key, and a 0 bit
* indicates a shift without the generation of a round key.
*/
#define DES_SCHEDULE 0x0d556aab
/**
* Define an element pair in a DES S-box
*
* @v x Upper element of element pair
* @v y Lower element of element pair
*
* DES S-box elements are 4-bit values. We encode two values per
* byte, ordering the elements so that the six-bit input value may be
* used directly as a lookup index.
*
* Specifically, if the input value is {r1,c3,c2,c1,c0,r0}, where
* {r1,r0} is the table row index and {c3,c2,c1,c0} is the table
* column index (as used in the DES specification), then:
*
* - {r1,c3,c2,c1,c0} is the byte index into the table
*
* - (4*r0) is the required bit shift to extract the 4-bit value
*/
#define SBYTE( x, y ) ( ( (y) << 4 ) | (x) )
/**
* Define a row pair in a DES S-box
*
* @v x0..xf Upper row of row pair
* @v y0..yf Lower row of row pair
*/
#define SBOX( x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe, xf, \
y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, ya, yb, yc, yd, ye, yf ) \
SBYTE ( x0, y0 ), SBYTE ( x1, y1 ), SBYTE ( x2, y2 ), SBYTE ( x3, y3 ),\
SBYTE ( x4, y4 ), SBYTE ( x5, y5 ), SBYTE ( x6, y6 ), SBYTE ( x7, y7 ),\
SBYTE ( x8, y8 ), SBYTE ( x9, y9 ), SBYTE ( xa, ya ), SBYTE ( xb, yb ),\
SBYTE ( xc, yc ), SBYTE ( xd, yd ), SBYTE ( xe, ye ), SBYTE ( xf, yf )
/** DES S-boxes S1..S8 */
static const uint8_t des_s[8][32] = { {
/* S1 */
SBOX ( 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 ),
SBOX ( 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 )
}, {
/* S2 */
SBOX ( 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 ),
SBOX ( 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 )
}, {
/* S3 */
SBOX ( 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 ),
SBOX ( 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 )
}, {
/* S4 */
SBOX ( 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 ),
SBOX ( 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 )
}, {
/* S5 */
SBOX ( 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 ),
SBOX ( 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 )
}, {
/* S6 */
SBOX ( 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 ),
SBOX ( 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 )
}, {
/* S7 */
SBOX ( 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 ),
SBOX ( 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 )
}, {
/* S8 */
SBOX ( 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 ),
SBOX ( 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 )
} };
/**
* Define a bit index within permuted choice 2 (PC2)
*
* @v bit Bit index
*
* Permuted choice 2 (PC2) is used to select bits from a concatenated
* pair of 28-bit registers ("C" and "D") as part of the key schedule.
* We store these as 32-bit registers and so must add 4 to indexes
* above 28.
*/
#define DES_PC2( x ) ( (x) + ( ( (x) > 28 ) ? 4 : 0 ) )
/**
* Define six bits of permuted choice 2 (PC2)
*
* @v r1:r0 Bits corresponding to S-box row index
* @v c3:c0 Bits corresponding to S-box column index
*
* There are 8 steps within a DES round (one step per S-box). Each
* step requires six bits of the round key, corresponding to the S-box
* input value {r1,c3,c2,c1,c0,r0}, where {r1,r0} is the table row
* index and {c3,c2,c1,c0} is the table column index.
*
* As an optimisation, we store the least significant of the 6 bits in
* the sign bit of a signed 8-bit value, and the remaining 5 bits in
* the least significant 5 bits of the 8-bit value. See the comments
* in des_sbox() for further details.
*/
#define DES_PC2R( r1, c3, c2, c1, c0, r0 ) \
DES_PC2 ( r0 ), /* LSB stored in sign bit */ \
DES_PC2 ( r0 ), /* Unused bit */ \
DES_PC2 ( r0 ), /* Unused bit */ \
DES_PC2 ( r1 ), /* Remaining 5 bits */ \
DES_PC2 ( c3 ), /* ... */ \
DES_PC2 ( c2 ), /* ... */ \
DES_PC2 ( c1 ), /* ... */ \
DES_PC2 ( c0 ) /* ... */
/**
* A DES systematic permutation generator
*
* Many of the permutations used in DES comprise systematic bit
* patterns. We generate these permutations at runtime to save on
* code size.
*/
struct des_generator {
/** Permutation */
uint8_t *permutation;
/** Seed value */
uint32_t seed;
};
/**
* Define a DES permutation generator
*
* @v PERMUTATION Permutation
* @v OFFSET Fixed input bit offset (0 or 1)
* @v INV<n> Input bit index bit <n> should be inverted
* @v BIT<n> Source bit for input bit index bit <n>
* @ret generator Permutation generator
*/
#define DES_GENERATOR( PERMUTATION, OFFSET, INV5, BIT5, INV4, BIT4, \
INV3, BIT3, INV2, BIT2, INV1, BIT1, INV0, BIT0 ) \
{ \
.permutation = (PERMUTATION), \
.seed = ( ( (INV0) << 31 ) | ( (BIT0) << 28 ) | \
( (INV1) << 27 ) | ( (BIT1) << 24 ) | \
( (INV2) << 23 ) | ( (BIT2) << 20 ) | \
( (INV3) << 19 ) | ( (BIT3) << 16 ) | \
( (INV4) << 15 ) | ( (BIT4) << 12 ) | \
( (INV5) << 11 ) | ( (BIT5) << 8 ) | \
( ( uint32_t ) sizeof (PERMUTATION) - 1 ) | \
(OFFSET) ), \
}
/** DES permuted choice 1 (PC1) "C" register */
static uint8_t des_pc1c[29];
/** DES permuted choice 1 (PC1) "D" register */
static uint8_t des_pc1d[33];
/** DES permuted choice 2 (PC2) */
static const uint8_t des_pc2[65] = {
DES_PC2R ( 14, 17, 11, 24, 1, 5 ),
DES_PC2R ( 3, 28, 15, 6, 21, 10 ),
DES_PC2R ( 23, 19, 12, 4, 26, 8 ),
DES_PC2R ( 16, 7, 27, 20, 13, 2 ),
DES_PC2R ( 41, 52, 31, 37, 47, 55 ),
DES_PC2R ( 30, 40, 51, 45, 33, 48 ),
DES_PC2R ( 44, 49, 39, 56, 34, 53 ),
DES_PC2R ( 46, 42, 50, 36, 29, 32 ),
0 /* terminator */
};
/** DES initial permutation (IP) */
static uint8_t des_ip[65];
/** DES data permutation (P) */
static const uint8_t des_p[33] = {
16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25,
0 /* terminator */
};
/** DES final / inverse initial permutation (FP / IP^-1) */
static uint8_t des_fp[65];
/** DES permutation generators */
static struct des_generator des_generators[] = {
/* The DES initial permutation transforms the bit index
* {x5,x4,x3,x2,x1,x0}+1 into {~x2,~x1,~x0,x4,x3,~x5}+1
*/
DES_GENERATOR ( des_ip, 1, 1, 2, 1, 1, 1, 0, 0, 4, 0, 3, 1, 5 ),
/* The DES final permutation transforms the bit index
* {x5,x4,x3,x2,x1,x0}+1 into {~x0,x2,x1,~x5,~x4,~x3}+1
*
* There is an asymmetry in the DES block diagram for the last
* of the 16 rounds, which is functionally equivalent to
* performing 16 identical rounds and then swapping the left
* and right halves before applying the final permutation. We
* may therefore account for this asymmetry by inverting the
* MSB in each bit index, to point to the corresponding bit in
* the other half.
*
* This is equivalent to using a permutation that transforms
* {x5,x4,x3,x2,x1,x0}+1 into {x0,x2,x1,~x5,~x4,~x3}+1
*/
DES_GENERATOR ( des_fp, 1, 0, 0, 0, 2, 0, 1, 1, 5, 1, 4, 1, 3 ),
/* The "C" half of DES permuted choice 1 (PC1) transforms the
* bit index {x5,x4,x3,x2,x1,x0}+1 into {~x2,~x1,~x0,x5,x4,x3}+1
*/
DES_GENERATOR ( des_pc1c, 1, 1, 2, 1, 1, 1, 0, 0, 5, 0, 4, 0, 3 ),
/* The "D" half of DES permuted choice 1 (PC1) transforms the
* bit index {x5,x4,x3,x2,x1,x0}+1 into {~x2,~x1,~x0,~x5,~x4,~x3}+0
*
* Due to the idosyncratic design choice of using 28-bit
* registers in the DES key expansion schedule, the final four
* permutation values appear at indices [28:31] instead of
* [24:27]. This is adjusted for in @c des_setkey().
*/
DES_GENERATOR ( des_pc1d, 0, 1, 2, 1, 1, 1, 0, 1, 5, 1, 4, 1, 3 ),
};
/**
* Generate DES permutation
*
* @v generator Generator
*/
static __attribute__ (( noinline )) void
des_generate ( struct des_generator *generator ) {
uint8_t *permutation = generator->permutation;
uint32_t seed = generator->seed;
unsigned int index = 0;
uint8_t accum;
uint8_t bit;
/* Generate permutations
*
* This loop is optimised for code size on a
* register-constrained architecture such as i386.
*/
do {
/* Rotate seed to access MSB's bit descriptor */
seed = ror32 ( seed, 8 );
/* Initialise accumulator with six flag bits */
accum = 0xfc;
/* Accumulate bits until all six flag bits are cleared */
do {
/* Extract specified bit from index. Use a
* rotation instead of a shift, since this
* will allow the mask to be elided.
*/
bit = ror8 ( index, ( seed & 0x07 ) );
seed = ror32 ( seed, 3 );
/* Toggle bit if applicable */
bit ^= seed;
seed = ror32 ( seed, 1 );
/* Add bit to accumulator and clear one flag bit */
accum <<= 1;
accum |= ( bit & 0x01 );
} while ( accum & 0x80 );
/* Add constant offset if applicable */
accum += ( seed & 0x01 );
/* Store permutation */
permutation[index] = accum;
/* Loop until reaching length (which is always even) */
} while ( ++index < ( seed & 0xfe ) );
DBGC2 ( permutation, "DES generated permutation %p:\n", permutation );
DBGC2_HDA ( permutation, 0, permutation,
( ( seed & 0xfe ) + 1 /* zero terminator */ ) );
}
/**
* Initialise permutations
*/
static void des_init ( void ) {
unsigned int i;
/* Generate all generated permutations */
for ( i = 0 ; i < ( sizeof ( des_generators ) /
sizeof ( des_generators[0] ) ) ; i++ ) {
des_generate ( &des_generators[i] );
}
}
/** Initialisation function */
struct init_fn des_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = des_init,
};
/**
* Perform bit permutation
*
* @v permutation Bit permutation (zero-terminated)
* @v in Input value
* @v out Output value
*/
static void des_permute ( const uint8_t *permutation, const uint8_t *in,
uint8_t *out ) {
uint8_t mask = 0x80;
uint8_t accum = 0;
unsigned int bit;
/* Extract individual input bits to construct output value */
while ( ( bit = *(permutation++) ) ) {
bit--;
if ( in[ bit / 8 ] & ( 0x80 >> ( bit % 8 ) ) )
accum |= mask;
*out = accum;
mask = ror8 ( mask, 1 );
if ( mask == 0x80 ) {
out++;
accum = 0;
}
}
}
/**
* Perform DES S-box substitution
*
* @v in 32-bit input value (native endian)
* @v rkey 48-bit round key
* @ret out 32-bit output value (native endian)
*/
static uint32_t des_sbox ( uint32_t in, const union des_round_key *rkey ) {
uint32_t out = 0;
uint32_t lookup;
int32_t key;
uint8_t sub;
unsigned int i;
/* Perform input expansion, key addition, and S-box substitution */
for ( i = 0 ; i < 8 ; i++ ) {
/* Rotate input and output */
out = rol32 ( out, 4 );
in = rol32 ( in, 4 );
/* Extract step key from relevant 6 bits of round key
*
* The least significant of the 6 bits (corresponding
* to bit r0 in the S-box lookup index) is stored in
* the sign bit of the step key byte. It will
* therefore be propagated via sign extension to the
* MSB of the 32-bit step key.
*
* The remaining 5 of the 6 bits (corresponding to
* bits {r1,c3,c2,c1,c0} in the S-box lookup index)
* are stored in the least significant 5 bits of the
* step key byte and will end up in the least
* significant 5 bits of the 32-bit step key.
*/
key = rkey->step[i];
/* Add step key to input to produce S-box lookup index
*
* We do not ever perform an explicit expansion of the
* input value from 32 to 48 bits. Instead, we rotate
* the 32-bit input value by 4 bits on each step, and
* extract the relevant 6 bits.
*
* The least significant of the 6 bits (corresponding
* to bit r0 in the S-box lookup index) is currently
* in the MSB of the 32-bit (rotated) input value.
*
* The remaining 5 of the 6 bits (corresponding to
* bits {r1,c3,c2,c1,c0} in the S-box lookup index)
* are currently in the least significant 5 bits of
* the 32-bit (rotated) input value.
*
* This aligns with the placement of the bits in the
* step key (see above), and we can therefore perform
* a single XOR to add the 6-bit step key to the
* relevant 6 bits of the input value.
*/
lookup = ( in ^ key );
/* Look up S[i][in ^ key] from S-box
*
* We have bits {r1,c3,c2,c1,c0} in the least
* significant 5 bits of the lookup index, and so can
* use the masked lookup index directly as a byte
* index into the relevant S-box to extract the byte
* containing both {r1,c3,c2,c1,c0,'0'} and
* {r1,c3,c2,c1,c0,'1'}.
*
* We then use the MSB of the 32-bit lookup index to
* extract the relevant nibble for the full lookup
* index {r1,c3,c2,c1,c0,r0}.
*/
sub = des_s[i][ lookup & 0x1f ];
sub >>= ( ( lookup >> 29 ) & 4 );
sub &= 0x0f;
/* Substitute S[i][input ^ key] into output */
out |= sub;
}
return out;
}
/**
* Perform a single DES round
*
* @v block DES block
* @v rkey 48-bit round key
*/
static void des_round ( union des_block *block,
const union des_round_key *rkey ) {
union des_dword sbox;
uint32_t left;
uint32_t right;
/* Extract left and right halves L[n-1] and R[n-1] */
left = block->left.dword;
right = block->right.dword;
DBGC2 ( block, "DES L=%08x R=%08x K=%08x%08x", be32_to_cpu ( left ),
be32_to_cpu ( right ), be32_to_cpu ( rkey->dword[0] ),
be32_to_cpu ( rkey->dword[1] ) );
/* L[n] = R[n-1] */
block->left.dword = right;
/* Calculate Feistel function f(R[n-1], K[n]) */
sbox.dword = cpu_to_be32 ( des_sbox ( be32_to_cpu ( right ), rkey ) );
des_permute ( des_p, sbox.byte, block->right.byte );
/* R[n] = L[n-1] + f(R[n-1], K[n]) */
block->right.dword ^= left;
DBGC2 ( block, " => L=%08x R=%08x\n",
be32_to_cpu ( block->left.dword ),
be32_to_cpu ( block->right.dword ) );
}
/**
* Perform all DES rounds
*
* @v in Input DES block
* @v out Output DES block
* @v rkey Starting 48-bit round key
* @v offset Byte offset between round keys
*/
static void des_rounds ( const union des_block *in, union des_block *out,
const union des_round_key *rkey,
ssize_t offset ) {
union des_block tmp;
unsigned int i;
/* Apply initial permutation */
des_permute ( des_ip, in->byte, tmp.byte );
/* Perform all DES rounds, consuming keys in the specified order */
for ( i = 0 ; i < DES_ROUNDS ; i++ ) {
des_round ( &tmp, rkey );
rkey = ( ( ( void * ) rkey ) + offset );
}
/* Apply final permutation */
DBGC ( &tmp, "DES %scrypted %08x%08x => ",
( ( offset > 0 ) ? "en" : "de" ), be32_to_cpu ( in->dword[0] ),
be32_to_cpu ( in->dword[1] ) );
des_permute ( des_fp, tmp.byte, out->byte );
DBGC ( &tmp, "%08x%08x\n", be32_to_cpu ( out->dword[0] ),
be32_to_cpu ( out->dword[1] ) );
}
/**
* Rotate 28-bit word
*
* @v dword 28-bit dword value
* @ret dword Rotated 28-bit dword value
*/
static uint32_t des_rol28 ( uint32_t dword ) {
int32_t sdword;
/* Convert to native-endian */
sdword = be32_to_cpu ( dword );
/* Signed shift right by 4 places to copy bit 31 to bits 27:31 */
sdword >>= 4;
/* Rotate left */
sdword = rol32 ( sdword, 1 );
/* Shift left by 4 places to restore bit positions */
sdword <<= 4;
/* Convert back to big-endian */
dword = cpu_to_be32 ( sdword );
return dword;
}
/**
* Set key
*
* @v ctx Context
* @v key Key
* @v keylen Key length
* @ret rc Return status code
*/
static int des_setkey ( void *ctx, const void *key, size_t keylen ) {
struct des_context *des = ctx;
union des_round_key *rkey = des->rkey;
union des_block reg;
uint32_t schedule;
/* Validate key length */
if ( keylen != DES_BLOCKSIZE )
return -EINVAL;
DBGC ( des, "DES %p new key:\n", des );
DBGC_HDA ( des, 0, key, keylen );
/* Apply permuted choice 1 */
des_permute ( des_pc1c, key, reg.c.byte );
des_permute ( des_pc1d, key, reg.d.byte );
reg.d.byte[3] <<= 4; /* see comment for @c des_pc1d */
DBGC2 ( des, "DES %p C[ 0]=%07x D[ 0]=%07x\n",
des, ( be32_to_cpu ( reg.c.dword ) >> 4 ),
( be32_to_cpu ( reg.d.dword ) >> 4 ) );
/* Generate round keys */
for ( schedule = DES_SCHEDULE ; schedule ; schedule >>= 1 ) {
/* Shift 28-bit words */
reg.c.dword = des_rol28 ( reg.c.dword );
reg.d.dword = des_rol28 ( reg.d.dword );
/* Skip rounds according to shift schedule */
if ( ! ( schedule & 1 ) )
continue;
/* Apply permuted choice 2 */
des_permute ( des_pc2, reg.byte, rkey->byte );
DBGC2 ( des, "DES %p C[%2zd]=%07x D[%2zd]=%07x K[%2zd]="
"%08x%08x\n", des, ( ( rkey - des->rkey ) + 1 ),
( be32_to_cpu ( reg.c.dword ) >> 4 ),
( ( rkey - des->rkey ) + 1 ),
( be32_to_cpu ( reg.d.dword ) >> 4 ),
( ( rkey - des->rkey ) + 1 ),
be32_to_cpu ( rkey->dword[0] ),
be32_to_cpu ( rkey->dword[1] ) );
/* Move to next key */
rkey++;
}
/* Sanity check */
assert ( rkey == &des->rkey[DES_ROUNDS] );
return 0;
}
/**
* Encrypt data
*
* @v ctx Context
* @v src Data to encrypt
* @v dst Buffer for encrypted data
* @v len Length of data
*/
static void des_encrypt ( void *ctx, const void *src, void *dst, size_t len ) {
struct des_context *des = ctx;
/* Sanity check */
assert ( len == DES_BLOCKSIZE );
/* Cipher using keys in forward direction */
des_rounds ( src, dst, &des->rkey[0], sizeof ( des->rkey[0] ) );
}
/**
* Decrypt data
*
* @v ctx Context
* @v src Data to decrypt
* @v dst Buffer for decrypted data
* @v len Length of data
*/
static void des_decrypt ( void *ctx, const void *src, void *dst, size_t len ) {
struct des_context *des = ctx;
/* Sanity check */
assert ( len == DES_BLOCKSIZE );
/* Cipher using keys in reverse direction */
des_rounds ( src, dst, &des->rkey[ DES_ROUNDS - 1 ],
-sizeof ( des->rkey[0] ) );
}
/** Basic DES algorithm */
struct cipher_algorithm des_algorithm = {
.name = "des",
.ctxsize = sizeof ( struct des_context ),
.blocksize = DES_BLOCKSIZE,
.alignsize = 0,
.authsize = 0,
.setkey = des_setkey,
.setiv = cipher_null_setiv,
.encrypt = des_encrypt,
.decrypt = des_decrypt,
.auth = cipher_null_auth,
};
/* DES in Electronic Codebook mode */
ECB_CIPHER ( des_ecb, des_ecb_algorithm,
des_algorithm, struct des_context, DES_BLOCKSIZE );
/* DES in Cipher Block Chaining mode */
CBC_CIPHER ( des_cbc, des_cbc_algorithm,
des_algorithm, struct des_context, DES_BLOCKSIZE );

View File

@ -109,6 +109,9 @@ static union gcm_block gcm_cached_mult[256];
*/
static uint16_t gcm_cached_reduce[256];
/** Offset of a field within GCM context */
#define gcm_offset( field ) offsetof ( struct gcm_context, field )
/**
* Reverse bits in a byte
*
@ -469,16 +472,14 @@ int gcm_setkey ( struct gcm_context *context, const void *key, size_t keylen,
* @v ivlen Initialisation vector length
*/
void gcm_setiv ( struct gcm_context *context, const void *iv, size_t ivlen ) {
union gcm_block *check = ( ( void * ) context );
/* Sanity checks */
linker_assert ( &context->hash == check, gcm_bad_layout );
linker_assert ( &context->len == check + 1, gcm_bad_layout );
linker_assert ( &context->ctr == check + 2, gcm_bad_layout );
linker_assert ( &context->key == check + 3, gcm_bad_layout );
/* Reset non-key state */
memset ( context, 0, offsetof ( typeof ( *context ), key ) );
memset ( context, 0, gcm_offset ( key ) );
build_assert ( gcm_offset ( key ) > gcm_offset ( hash ) );
build_assert ( gcm_offset ( key ) > gcm_offset ( len ) );
build_assert ( gcm_offset ( key ) > gcm_offset ( ctr ) );
build_assert ( gcm_offset ( key ) < gcm_offset ( raw_cipher ) );
build_assert ( gcm_offset ( key ) < gcm_offset ( raw_ctx ) );
/* Reset counter */
context->ctr.ctr.value = cpu_to_be32 ( 1 );
@ -497,7 +498,12 @@ void gcm_setiv ( struct gcm_context *context, const void *iv, size_t ivlen ) {
assert ( context->len.len.add == 0 );
/* Reset non-key, non-counter state */
memset ( context, 0, offsetof ( typeof ( *context ), ctr ) );
memset ( context, 0, gcm_offset ( ctr ) );
build_assert ( gcm_offset ( ctr ) > gcm_offset ( hash ) );
build_assert ( gcm_offset ( ctr ) > gcm_offset ( len ) );
build_assert ( gcm_offset ( ctr ) < gcm_offset ( key ) );
build_assert ( gcm_offset ( ctr ) < gcm_offset ( raw_cipher ) );
build_assert ( gcm_offset ( ctr ) < gcm_offset ( raw_ctx ) );
}
DBGC2 ( context, "GCM %p Y[0]:\n", context );

View File

@ -155,11 +155,11 @@ static void md4_digest ( struct md4_context *context ) {
/* Sanity checks */
assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 );
linker_assert ( &u.ddd.dd.digest.h[0] == a, md4_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[1] == b, md4_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[2] == c, md4_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[3] == d, md4_bad_layout );
linker_assert ( &u.ddd.dd.data.dword[0] == w, md4_bad_layout );
build_assert ( &u.ddd.dd.digest.h[0] == a );
build_assert ( &u.ddd.dd.digest.h[1] == b );
build_assert ( &u.ddd.dd.digest.h[2] == c );
build_assert ( &u.ddd.dd.digest.h[3] == d );
build_assert ( &u.ddd.dd.data.dword[0] == w );
DBGC ( context, "MD4 digesting:\n" );
DBGC_HDA ( context, 0, &context->ddd.dd.digest,

View File

@ -178,11 +178,11 @@ static void md5_digest ( struct md5_context *context ) {
/* Sanity checks */
assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 );
linker_assert ( &u.ddd.dd.digest.h[0] == a, md5_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[1] == b, md5_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[2] == c, md5_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[3] == d, md5_bad_layout );
linker_assert ( &u.ddd.dd.data.dword[0] == w, md5_bad_layout );
build_assert ( &u.ddd.dd.digest.h[0] == a );
build_assert ( &u.ddd.dd.digest.h[1] == b );
build_assert ( &u.ddd.dd.digest.h[2] == c );
build_assert ( &u.ddd.dd.digest.h[3] == d );
build_assert ( &u.ddd.dd.data.dword[0] == w );
DBGC ( context, "MD5 digesting:\n" );
DBGC_HDA ( context, 0, &context->ddd.dd.digest,

View File

@ -0,0 +1,61 @@
/*
* 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha1.h>
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 15 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha1_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_DHE_RSA_WITH_AES_256_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 16 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha1_algorithm,
.handshake = &sha256_algorithm,
};

View File

@ -0,0 +1,60 @@
/*
* 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 13 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 14 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};

View File

@ -0,0 +1,45 @@
/*
* 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 11 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};

View File

@ -0,0 +1,45 @@
/*
* 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha512.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 12 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
.digest = &sha384_algorithm,
.handshake = &sha384_algorithm,
};

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2024 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha1.h>
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_ecdhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 05 ) = {
.code = htons ( TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha1_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_ecdhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 06 ) = {
.code = htons ( TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha1_algorithm,
.handshake = &sha256_algorithm,
};

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 cipher suite */
struct tls_cipher_suite
tls_ecdhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 03 ) = {
.code = htons ( TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha512.h>
#include <ipxe/tls.h>
/** TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 cipher suite */
struct tls_cipher_suite
tls_ecdhe_rsa_with_aes_256_cbc_sha384 __tls_cipher_suite ( 04 ) = {
.code = htons ( TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA384_DIGEST_SIZE,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha384_algorithm,
.handshake = &sha384_algorithm,
};

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 cipher suite */
struct tls_cipher_suite
tls_ecdhe_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 01 ) = {
.code = htons ( TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 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 <byteswap.h>
#include <ipxe/rsa.h>
#include <ipxe/aes.h>
#include <ipxe/sha512.h>
#include <ipxe/tls.h>
/** TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 cipher suite */
struct tls_cipher_suite
tls_ecdhe_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 02 ) = {
.code = htons ( TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
.digest = &sha384_algorithm,
.handshake = &sha384_algorithm,
};

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 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 <byteswap.h>
#include <ipxe/x25519.h>
#include <ipxe/asn1.h>
#include <ipxe/tls.h>
/** "x25519" object identifier */
static uint8_t oid_x25519[] = { ASN1_OID_X25519 };
/** "x25519" OID-identified algorithm */
struct asn1_algorithm x25519_algorithm __asn1_algorithm = {
.name = "x25519",
.curve = &x25519_curve,
.oid = ASN1_CURSOR ( oid_x25519 ),
};
/** X25519 named curve */
struct tls_named_curve tls_x25519_named_curve __tls_named_curve ( 01 ) = {
.curve = &x25519_curve,
.code = htons ( TLS_NAMED_CURVE_X25519 ),
};

View File

@ -30,39 +30,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 05 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha1_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_DHE_RSA_WITH_AES_256_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 06 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha1_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_RSA_WITH_AES_128_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 15 ) = {
tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 25 ) = {
.code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
@ -77,7 +47,7 @@ tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 15 ) = {
/** TLS_RSA_WITH_AES_256_CBC_SHA cipher suite */
struct tls_cipher_suite
tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 16 ) = {
tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 26 ) = {
.code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,

View File

@ -29,39 +29,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 03 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 04 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite */
struct tls_cipher_suite
tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 13 ) = {
tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 23 ) = {
.code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 0,
@ -76,7 +46,7 @@ tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 13 ) = {
/** TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite */
struct tls_cipher_suite
tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 14 ) = {
tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 24 ) = {
.code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 0,

View File

@ -29,24 +29,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/sha256.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 01 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
.digest = &sha256_algorithm,
.handshake = &sha256_algorithm,
};
/** TLS_RSA_WITH_AES_128_GCM_SHA256 cipher suite */
struct tls_cipher_suite
tls_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 11 ) = {
tls_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 21 ) = {
.code = htons ( TLS_RSA_WITH_AES_128_GCM_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 4,

View File

@ -29,24 +29,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/sha512.h>
#include <ipxe/tls.h>
/** TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 cipher suite */
struct tls_cipher_suite
tls_dhe_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 02 ) = {
.code = htons ( TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
.digest = &sha384_algorithm,
.handshake = &sha384_algorithm,
};
/** TLS_RSA_WITH_AES_256_GCM_SHA384 cipher suite */
struct tls_cipher_suite
tls_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 12 ) = {
tls_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 22 ) = {
.code = htons ( TLS_RSA_WITH_AES_256_GCM_SHA384 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 4,

363
src/crypto/mschapv2.c Normal file
View File

@ -0,0 +1,363 @@
/*
* Copyright (C) 2024 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
*
* MS-CHAPv2 authentication
*
* The algorithms used for MS-CHAPv2 authentication are defined in
* RFC 2759 section 8.
*/
#include <stdio.h>
#include <string.h>
#include <byteswap.h>
#include <ipxe/md4.h>
#include <ipxe/sha1.h>
#include <ipxe/des.h>
#include <ipxe/mschapv2.h>
/**
* MS-CHAPv2 context block
*
* For no particularly discernible reason, MS-CHAPv2 uses two
* different digest algorithms and one block cipher. The uses do not
* overlap, so share the context storage between these to reduce stack
* usage.
*/
union mschapv2_context {
/** SHA-1 digest context */
uint8_t sha1[SHA1_CTX_SIZE];
/** MD4 digest context */
uint8_t md4[MD4_CTX_SIZE];
/** DES cipher context */
uint8_t des[DES_CTX_SIZE];
};
/**
* MS-CHAPv2 challenge hash
*
* MS-CHAPv2 calculates the SHA-1 digest of the peer challenge, the
* authenticator challenge, and the username, and then uses only the
* first 8 bytes of the result (as a DES plaintext block).
*/
union mschapv2_challenge_hash {
/** SHA-1 digest */
uint8_t sha1[SHA1_DIGEST_SIZE];
/** DES plaintext block */
uint8_t des[DES_BLOCKSIZE];
};
/**
* MS-CHAPv2 password hash
*
* MS-CHAPv2 calculates the MD4 digest of an unspecified two-byte
* little-endian Unicode encoding (presumably either UCS-2LE or
* UTF-16LE) of the password.
*
* For constructing the challenge response, the MD4 digest is then
* zero-padded to 21 bytes and used as three separate 56-bit DES keys.
*
* For constructing the authenticator response, the MD4 digest is then
* used as an input to a SHA-1 digest along with the NT response and a
* magic constant.
*/
union mschapv2_password_hash {
/** MD4 digest */
uint8_t md4[MD4_DIGEST_SIZE];
/** SHA-1 digest */
uint8_t sha1[SHA1_DIGEST_SIZE];
/** DES keys */
uint8_t des[3][DES_BLOCKSIZE];
/** DES key expansion */
uint8_t expand[ 3 * DES_BLOCKSIZE ];
};
/** MS-CHAPv2 magic constant 1 */
static const char mschapv2_magic1[39] =
"Magic server to client signing constant";
/** MS-CHAPv2 magic constant 2 */
static const char mschapv2_magic2[41] =
"Pad to make it do more than one iteration";
/**
* Calculate MS-CHAPv2 challenge hash
*
* @v ctx Context block
* @v challenge Authenticator challenge
* @v peer Peer challenge
* @v username User name (or NULL to use empty string)
* @v chash Challenge hash to fill in
*
* This is the ChallengeHash() function as documented in RFC 2759
* section 8.2.
*/
static void
mschapv2_challenge_hash ( union mschapv2_context *ctx,
const struct mschapv2_challenge *challenge,
const struct mschapv2_challenge *peer,
const char *username,
union mschapv2_challenge_hash *chash ) {
struct digest_algorithm *sha1 = &sha1_algorithm;
/* Calculate SHA-1 hash of challenges and username */
digest_init ( sha1, ctx->sha1 );
digest_update ( sha1, ctx->sha1, peer, sizeof ( *peer ) );
digest_update ( sha1, ctx->sha1, challenge, sizeof ( *challenge ) );
if ( username ) {
digest_update ( sha1, ctx->sha1, username,
strlen ( username ) );
}
digest_final ( sha1, ctx->sha1, chash->sha1 );
DBGC ( ctx, "MSCHAPv2 authenticator challenge:\n" );
DBGC_HDA ( ctx, 0, challenge, sizeof ( *challenge ) );
DBGC ( ctx, "MSCHAPv2 peer challenge:\n" );
DBGC_HDA ( ctx, 0, peer, sizeof ( *peer ) );
DBGC ( ctx, "MSCHAPv2 challenge hash:\n" );
DBGC_HDA ( ctx, 0, chash->des, sizeof ( chash->des ) );
}
/**
* Calculate MS-CHAPv2 password hash
*
* @v ctx Context block
* @v password Password (or NULL to use empty string)
* @v phash Password hash to fill in
*
* This is the NtPasswordHash() function as documented in RFC 2759
* section 8.3.
*/
static void mschapv2_password_hash ( union mschapv2_context *ctx,
const char *password,
union mschapv2_password_hash *phash ) {
struct digest_algorithm *md4 = &md4_algorithm;
uint16_t wc;
uint8_t c;
/* Construct zero-padded MD4 hash of encoded password */
memset ( phash, 0, sizeof ( *phash ) );
digest_init ( md4, ctx->md4 );
if ( password ) {
while ( ( c = *(password++) ) ) {
wc = cpu_to_le16 ( c );
digest_update ( md4, ctx->md4, &wc, sizeof ( wc ) );
}
}
digest_final ( md4, ctx->md4, phash->md4 );
DBGC ( ctx, "MSCHAPv2 password hash:\n" );
DBGC_HDA ( ctx, 0, phash->md4, sizeof ( phash->md4 ) );
}
/**
* Hash the MS-CHAPv2 password hash
*
* @v ctx Context block
* @v phash Password hash to be rehashed
*
* This is the HashNtPasswordHash() function as documented in RFC 2759
* section 8.4.
*/
static void mschapv2_hash_hash ( union mschapv2_context *ctx,
union mschapv2_password_hash *phash ) {
struct digest_algorithm *md4 = &md4_algorithm;
/* Calculate MD4 hash of existing MD4 hash */
digest_init ( md4, ctx->md4 );
digest_update ( md4, ctx->md4, phash->md4, sizeof ( phash->md4 ) );
digest_final ( md4, ctx->md4, phash->md4 );
DBGC ( ctx, "MSCHAPv2 password hash hash:\n" );
DBGC_HDA ( ctx, 0, phash->md4, sizeof ( phash->md4 ) );
}
/**
* Expand MS-CHAPv2 password hash by inserting DES dummy parity bits
*
* @v ctx Context block
* @v phash Password hash to expand
*
* This is part of the DesEncrypt() function as documented in RFC 2759
* section 8.6.
*/
static void mschapv2_expand_hash ( union mschapv2_context *ctx,
union mschapv2_password_hash *phash ) {
uint8_t *dst;
uint8_t *src;
unsigned int i;
/* Expand password hash by inserting (unused) DES parity bits */
for ( i = ( sizeof ( phash->expand ) - 1 ) ; i > 0 ; i-- ) {
dst = &phash->expand[i];
src = ( dst - ( i / 8 ) );
*dst = ( ( ( src[-1] << 8 ) | src[0] ) >> ( i % 8 ) );
}
DBGC ( ctx, "MSCHAPv2 expanded password hash:\n" );
DBGC_HDA ( ctx, 0, phash->expand, sizeof ( phash->expand ) );
}
/**
* Calculate MS-CHAPv2 challenge response
*
* @v ctx Context block
* @v chash Challenge hash
* @v phash Password hash (after expansion)
* @v nt NT response to fill in
*
* This is the ChallengeResponse() function as documented in RFC 2759
* section 8.5.
*/
static void
mschapv2_challenge_response ( union mschapv2_context *ctx,
const union mschapv2_challenge_hash *chash,
const union mschapv2_password_hash *phash,
struct mschapv2_nt_response *nt ) {
struct cipher_algorithm *des = &des_algorithm;
unsigned int i;
int rc;
/* Construct response. The design of the algorithm here is
* interesting, suggesting that an intern at Microsoft had
* heard the phrase "Triple DES" and hazarded a blind guess at
* what it might mean.
*/
for ( i = 0 ; i < ( sizeof ( phash->des ) /
sizeof ( phash->des[0] ) ) ; i++ ) {
rc = cipher_setkey ( des, ctx->des, phash->des[i],
sizeof ( phash->des[i] ) );
assert ( rc == 0 ); /* no failure mode exists */
cipher_encrypt ( des, ctx->des, chash->des, nt->block[i],
sizeof ( chash->des ) );
}
DBGC ( ctx, "MSCHAPv2 NT response:\n" );
DBGC_HDA ( ctx, 0, nt, sizeof ( *nt ) );
}
/**
* Calculate MS-CHAPv2 challenge response
*
* @v username User name (or NULL to use empty string)
* @v password Password (or NULL to use empty string)
* @v challenge Authenticator challenge
* @v peer Peer challenge
* @v response Challenge response to fill in
*
* This is essentially the GenerateNTResponse() function as documented
* in RFC 2759 section 8.1.
*/
void mschapv2_response ( const char *username, const char *password,
const struct mschapv2_challenge *challenge,
const struct mschapv2_challenge *peer,
struct mschapv2_response *response ) {
union mschapv2_context ctx;
union mschapv2_challenge_hash chash;
union mschapv2_password_hash phash;
/* Zero reserved fields */
memset ( response, 0, sizeof ( *response ) );
/* Copy peer challenge to response */
memcpy ( &response->peer, peer, sizeof ( response->peer ) );
/* Construct challenge hash */
mschapv2_challenge_hash ( &ctx, challenge, peer, username, &chash );
/* Construct expanded password hash */
mschapv2_password_hash ( &ctx, password, &phash );
mschapv2_expand_hash ( &ctx, &phash );
/* Construct NT response */
mschapv2_challenge_response ( &ctx, &chash, &phash, &response->nt );
DBGC ( &ctx, "MSCHAPv2 challenge response:\n" );
DBGC_HDA ( &ctx, 0, response, sizeof ( *response ) );
}
/**
* Calculate MS-CHAPv2 authenticator response
*
* @v username User name (or NULL to use empty string)
* @v password Password (or NULL to use empty string)
* @v challenge Authenticator challenge
* @v response Challenge response
* @v auth Authenticator response to fill in
*
* This is essentially the GenerateAuthenticatorResponse() function as
* documented in RFC 2759 section 8.7.
*/
void mschapv2_auth ( const char *username, const char *password,
const struct mschapv2_challenge *challenge,
const struct mschapv2_response *response,
struct mschapv2_auth *auth ) {
struct digest_algorithm *sha1 = &sha1_algorithm;
union mschapv2_context ctx;
union mschapv2_challenge_hash chash;
union mschapv2_password_hash phash;
char tmp[3];
char *wtf;
unsigned int i;
/* Construct hash of password hash */
mschapv2_password_hash ( &ctx, password, &phash );
mschapv2_hash_hash ( &ctx, &phash );
/* Construct unnamed intermediate hash */
digest_init ( sha1, ctx.sha1 );
digest_update ( sha1, ctx.sha1, phash.md4, sizeof ( phash.md4 ) );
digest_update ( sha1, ctx.sha1, &response->nt,
sizeof ( response->nt ) );
digest_update ( sha1, ctx.sha1, mschapv2_magic1,
sizeof ( mschapv2_magic1 ) );
digest_final ( sha1, ctx.sha1, phash.sha1 );
DBGC ( &ctx, "MSCHAPv2 NT response:\n" );
DBGC_HDA ( &ctx, 0, &response->nt, sizeof ( response->nt ) );
DBGC ( &ctx, "MSCHAPv2 unnamed intermediate hash:\n" );
DBGC_HDA ( &ctx, 0, phash.sha1, sizeof ( phash.sha1 ) );
/* Construct challenge hash */
mschapv2_challenge_hash ( &ctx, challenge, &response->peer,
username, &chash );
/* Construct authenticator response hash */
digest_init ( sha1, ctx.sha1 );
digest_update ( sha1, ctx.sha1, phash.sha1, sizeof ( phash.sha1 ) );
digest_update ( sha1, ctx.sha1, chash.des, sizeof ( chash.des ) );
digest_update ( sha1, ctx.sha1, mschapv2_magic2,
sizeof ( mschapv2_magic2 ) );
digest_final ( sha1, ctx.sha1, phash.sha1 );
DBGC ( &ctx, "MSCHAPv2 authenticator response hash:\n" );
DBGC_HDA ( &ctx, 0, phash.sha1, sizeof ( phash.sha1 ) );
/* Encode authenticator response hash */
wtf = auth->wtf;
*(wtf++) = 'S';
*(wtf++) = '=';
DBGC ( &ctx, "MSCHAPv2 authenticator response: S=" );
for ( i = 0 ; i < sizeof ( phash.sha1 ) ; i++ ) {
snprintf ( tmp, sizeof ( tmp ), "%02X", phash.sha1[i] );
*(wtf++) = tmp[0];
*(wtf++) = tmp[1];
DBGC ( &ctx, "%s", tmp );
}
DBGC ( &ctx, "\n" );
}

View File

@ -145,12 +145,12 @@ static void sha1_digest ( struct sha1_context *context ) {
/* Sanity checks */
assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 );
linker_assert ( &u.ddd.dd.digest.h[0] == a, sha1_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[1] == b, sha1_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[2] == c, sha1_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[3] == d, sha1_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[4] == e, sha1_bad_layout );
linker_assert ( &u.ddd.dd.data.dword[0] == w, sha1_bad_layout );
build_assert ( &u.ddd.dd.digest.h[0] == a );
build_assert ( &u.ddd.dd.digest.h[1] == b );
build_assert ( &u.ddd.dd.digest.h[2] == c );
build_assert ( &u.ddd.dd.digest.h[3] == d );
build_assert ( &u.ddd.dd.digest.h[4] == e );
build_assert ( &u.ddd.dd.data.dword[0] == w );
DBGC ( context, "SHA1 digesting:\n" );
DBGC_HDA ( context, 0, &context->ddd.dd.digest,

View File

@ -140,15 +140,15 @@ static void sha256_digest ( struct sha256_context *context ) {
/* Sanity checks */
assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 );
linker_assert ( &u.ddd.dd.digest.h[0] == a, sha256_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[1] == b, sha256_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[2] == c, sha256_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[3] == d, sha256_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[4] == e, sha256_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[5] == f, sha256_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[6] == g, sha256_bad_layout );
linker_assert ( &u.ddd.dd.digest.h[7] == h, sha256_bad_layout );
linker_assert ( &u.ddd.dd.data.dword[0] == w, sha256_bad_layout );
build_assert ( &u.ddd.dd.digest.h[0] == a );
build_assert ( &u.ddd.dd.digest.h[1] == b );
build_assert ( &u.ddd.dd.digest.h[2] == c );
build_assert ( &u.ddd.dd.digest.h[3] == d );
build_assert ( &u.ddd.dd.digest.h[4] == e );
build_assert ( &u.ddd.dd.digest.h[5] == f );
build_assert ( &u.ddd.dd.digest.h[6] == g );
build_assert ( &u.ddd.dd.digest.h[7] == h );
build_assert ( &u.ddd.dd.data.dword[0] == w );
DBGC ( context, "SHA256 digesting:\n" );
DBGC_HDA ( context, 0, &context->ddd.dd.digest,

View File

@ -156,15 +156,15 @@ static void sha512_digest ( struct sha512_context *context ) {
/* Sanity checks */
assert ( ( context->len % sizeof ( context->ddq.dd.data ) ) == 0 );
linker_assert ( &u.ddq.dd.digest.h[0] == a, sha512_bad_layout );
linker_assert ( &u.ddq.dd.digest.h[1] == b, sha512_bad_layout );
linker_assert ( &u.ddq.dd.digest.h[2] == c, sha512_bad_layout );
linker_assert ( &u.ddq.dd.digest.h[3] == d, sha512_bad_layout );
linker_assert ( &u.ddq.dd.digest.h[4] == e, sha512_bad_layout );
linker_assert ( &u.ddq.dd.digest.h[5] == f, sha512_bad_layout );
linker_assert ( &u.ddq.dd.digest.h[6] == g, sha512_bad_layout );
linker_assert ( &u.ddq.dd.digest.h[7] == h, sha512_bad_layout );
linker_assert ( &u.ddq.dd.data.qword[0] == w, sha512_bad_layout );
build_assert ( &u.ddq.dd.digest.h[0] == a );
build_assert ( &u.ddq.dd.digest.h[1] == b );
build_assert ( &u.ddq.dd.digest.h[2] == c );
build_assert ( &u.ddq.dd.digest.h[3] == d );
build_assert ( &u.ddq.dd.digest.h[4] == e );
build_assert ( &u.ddq.dd.digest.h[5] == f );
build_assert ( &u.ddq.dd.digest.h[6] == g );
build_assert ( &u.ddq.dd.digest.h[7] == h );
build_assert ( &u.ddq.dd.data.qword[0] == w );
DBGC ( context, "SHA512 digesting:\n" );
DBGC_HDA ( context, 0, &context->ddq.dd.digest,

844
src/crypto/x25519.c Normal file
View File

@ -0,0 +1,844 @@
/*
* Copyright (C) 2024 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
*
* X25519 key exchange
*
* This implementation is inspired by and partially based upon the
* paper "Implementing Curve25519/X25519: A Tutorial on Elliptic Curve
* Cryptography" by Martin Kleppmann, available for download from
* https://www.cl.cam.ac.uk/teaching/2122/Crypto/curve25519.pdf
*
* The underlying modular addition, subtraction, and multiplication
* operations are completely redesigned for substantially improved
* efficiency compared to the TweetNaCl implementation studied in that
* paper.
*
* TweetNaCl iPXE
* --------- ----
*
* Storage size of each big integer 128 40
* (in bytes)
*
* Stack usage for key exchange 1144 360
* (in bytes, large objects only)
*
* Cost of big integer addition 16 5
* (in number of 64-bit additions)
*
* Cost of big integer multiplication 273 31
* (in number of 64-bit multiplications)
*
* The implementation is constant-time (provided that the underlying
* big integer operations are also constant-time).
*/
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <ipxe/init.h>
#include <ipxe/crypto.h>
#include <ipxe/x25519.h>
/** X25519 reduction constant
*
* The X25519 field prime is p=2^255-19. This gives us:
*
* p = 2^255 - 19
* 2^255 = p + 19
* 2^255 = 19 (mod p)
* k * 2^255 = k * 19 (mod p)
*
* We can therefore reduce a value modulo p by taking the high-order
* bits of the value from bit 255 and above, multiplying by 19, and
* adding this to the low-order 255 bits of the value.
*
* This would be cumbersome to do in practice since it would require
* partitioning the value at a 255-bit boundary (and hence would
* require some shifting and masking operations). However, we can
* note that:
*
* k * 2^255 = k * 19 (mod p)
* k * 2 * 2^255 = k * 2 * 19 (mod p)
* k * 2^256 = k * 38 (mod p)
*
* We can therefore simplify the reduction to taking the high order
* bits of the value from bit 256 and above, multiplying by 38, and
* adding this to the low-order 256 bits of the value.
*
* Since 256 will inevitably be a multiple of the big integer element
* size (typically 32 or 64 bits), this avoids the need to perform any
* shifting or masking operations.
*/
#define X25519_REDUCE_256 38
/** X25519 multiplication step 1 result
*
* Step 1 of X25519 multiplication is to compute the product of two
* X25519 unsigned 258-bit integers.
*
* Both multiplication inputs are limited to 258 bits, and so the
* product will have at most 516 bits.
*/
union x25519_multiply_step1 {
/** Raw product
*
* Big integer multiplication produces a result with a number
* of elements equal to the sum of the number of elements in
* each input.
*/
bigint_t ( X25519_SIZE + X25519_SIZE ) product;
/** Partition into low-order and high-order bits
*
* Reduction modulo p requires separating the low-order 256
* bits from the remaining high-order bits.
*
* Since the value will never exceed 516 bits (see above),
* there will be at most 260 high-order bits.
*/
struct {
/** Low-order 256 bits */
bigint_t ( bigint_required_size ( ( 256 /* bits */ + 7 ) / 8 ) )
low_256bit;
/** High-order 260 bits */
bigint_t ( bigint_required_size ( ( 260 /* bits */ + 7 ) / 8 ) )
high_260bit;
} __attribute__ (( packed )) parts;
};
/** X25519 multiplication step 2 result
*
* Step 2 of X25519 multiplication is to multiply the high-order 260
* bits from step 1 with the 6-bit reduction constant 38, and to add
* this to the low-order 256 bits from step 1.
*
* The multiplication inputs are limited to 260 and 6 bits
* respectively, and so the product will have at most 266 bits. After
* adding the low-order 256 bits from step 1, the result will have at
* most 267 bits.
*/
union x25519_multiply_step2 {
/** Raw product
*
* Big integer multiplication produces a result with a number
* of elements equal to the sum of the number of elements in
* each input.
*/
bigint_t ( bigint_required_size ( ( 260 /* bits */ + 7 ) / 8 ) +
bigint_required_size ( ( 6 /* bits */ + 7 ) / 8 ) ) product;
/** Big integer value
*
* The value will never exceed 267 bits (see above), and so
* may be consumed as a normal X25519 big integer.
*/
x25519_t value;
/** Partition into low-order and high-order bits
*
* Reduction modulo p requires separating the low-order 256
* bits from the remaining high-order bits.
*
* Since the value will never exceed 267 bits (see above),
* there will be at most 11 high-order bits.
*/
struct {
/** Low-order 256 bits */
bigint_t ( bigint_required_size ( ( 256 /* bits */ + 7 ) / 8 ) )
low_256bit;
/** High-order 11 bits */
bigint_t ( bigint_required_size ( ( 11 /* bits */ + 7 ) / 8 ) )
high_11bit;
} __attribute__ (( packed )) parts;
};
/** X25519 multiplication step 3 result
*
* Step 3 of X25519 multiplication is to multiply the high-order 11
* bits from step 2 with the 6-bit reduction constant 38, and to add
* this to the low-order 256 bits from step 2.
*
* The multiplication inputs are limited to 11 and 6 bits
* respectively, and so the product will have at most 17 bits. After
* adding the low-order 256 bits from step 2, the result will have at
* most 257 bits.
*/
union x25519_multiply_step3 {
/** Raw product
*
* Big integer multiplication produces a result with a number
* of elements equal to the sum of the number of elements in
* each input.
*/
bigint_t ( bigint_required_size ( ( 11 /* bits */ + 7 ) / 8 ) +
bigint_required_size ( ( 6 /* bits */ + 7 ) / 8 ) ) product;
/** Big integer value
*
* The value will never exceed 267 bits (see above), and so
* may be consumed as a normal X25519 big integer.
*/
x25519_t value;
};
/** X25519 multiplication temporary working space
*
* We overlap the buffers used by each step of the multiplication
* calculation to reduce the total stack space required:
*
* |--------------------------------------------------------|
* | <- pad -> | <------------ step 1 result -------------> |
* | | <- low 256 bits -> | <-- high 260 bits --> |
* | <------- step 2 result ------> | <-- step 3 result --> |
* |--------------------------------------------------------|
*/
union x25519_multiply_workspace {
/** Step 1 result */
struct {
/** Padding to avoid collision between steps 1 and 2
*
* The step 2 multiplication consumes the high 260
* bits of step 1, and so the step 2 multiplication
* result must not overlap this portion of the step 1
* result.
*/
uint8_t pad[ sizeof ( union x25519_multiply_step2 ) -
offsetof ( union x25519_multiply_step1,
parts.high_260bit ) ];
/** Step 1 result */
union x25519_multiply_step1 step1;
} __attribute__ (( packed ));
/** Steps 2 and 3 results */
struct {
/** Step 2 result */
union x25519_multiply_step2 step2;
/** Step 3 result */
union x25519_multiply_step3 step3;
} __attribute__ (( packed ));
};
/** An X25519 elliptic curve point in projective coordinates
*
* A point (x,y) on the Montgomery curve used in X25519 is represented
* using projective coordinates (X/Z,Y/Z) so that intermediate
* calculations may be performed on both numerator and denominator
* separately, with the division step performed only once at the end
* of the calculation.
*
* The group operation calculation is performed using a Montgomery
* ladder as:
*
* X[2i] = ( X[i]^2 - Z[i]^2 )^2
* X[2i+1] = ( X[i] * X[i+1] - Z[i] * Z[i+1] )^2
* Z[2i] = 4 * X[i] * Z[i] * ( X[i]^2 + A * X[i] * Z[i] + Z[i]^2 )
* Z[2i+1] = X[0] * ( X[i] * Z[i+1] - X[i+1] * Z[i] ) ^ 2
*
* It is therefore not necessary to store (or use) the value of Y.
*/
struct x25519_projective {
/** X coordinate */
union x25519_quad257 X;
/** Z coordinate */
union x25519_quad257 Z;
};
/** An X25519 Montgomery ladder step */
struct x25519_step {
/** X[n]/Z[n] */
struct x25519_projective x_n;
/** X[n+1]/Z[n+1] */
struct x25519_projective x_n1;
};
/** Constant p=2^255-19 (the finite field prime) */
static const uint8_t x25519_p_raw[] = {
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed
};
/** Constant p=2^255-19 (the finite field prime) */
static x25519_t x25519_p;
/** Constant 2p=2^256-38 */
static x25519_t x25519_2p;
/** Constant 4p=2^257-76 */
static x25519_t x25519_4p;
/** Reduction constant (used during multiplication) */
static const uint8_t x25519_reduce_256_raw[] = { X25519_REDUCE_256 };
/** Reduction constant (used during multiplication) */
static bigint_t ( bigint_required_size ( sizeof ( x25519_reduce_256_raw ) ) )
x25519_reduce_256;
/** Constant 121665 (used in the Montgomery ladder) */
static const uint8_t x25519_121665_raw[] = { 0x01, 0xdb, 0x41 };
/** Constant 121665 (used in the Montgomery ladder) */
static union x25519_oct258 x25519_121665;
/** Constant g=9 (the group generator) */
static struct x25519_value x25519_generator = {
.raw = { 9, }
};
/**
* Initialise constants
*
*/
static void x25519_init_constants ( void ) {
/* Construct constant p */
bigint_init ( &x25519_p, x25519_p_raw, sizeof ( x25519_p_raw ) );
/* Construct constant 2p */
bigint_copy ( &x25519_p, &x25519_2p );
bigint_add ( &x25519_p, &x25519_2p );
/* Construct constant 4p */
bigint_copy ( &x25519_2p, &x25519_4p );
bigint_add ( &x25519_2p, &x25519_4p );
/* Construct reduction constant */
bigint_init ( &x25519_reduce_256, x25519_reduce_256_raw,
sizeof ( x25519_reduce_256_raw ) );
/* Construct constant 121665 */
bigint_init ( &x25519_121665.value, x25519_121665_raw,
sizeof ( x25519_121665_raw ) );
}
/** Initialisation function */
struct init_fn x25519_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = x25519_init_constants,
};
/**
* Add big integers modulo field prime
*
* @v augend Big integer to add
* @v addend Big integer to add
* @v result Big integer to hold result (may overlap augend)
*/
static inline __attribute__ (( always_inline )) void
x25519_add ( const union x25519_quad257 *augend,
const union x25519_quad257 *addend,
union x25519_oct258 *result ) {
int copy;
/* Copy augend if necessary */
copy = ( result != &augend->oct258 );
build_assert ( __builtin_constant_p ( copy ) );
if ( copy ) {
build_assert ( result != &addend->oct258 );
bigint_copy ( &augend->oct258.value, &result->value );
}
/* Perform addition
*
* Both inputs are in the range [0,4p-1] and the resulting
* sum is therefore in the range [0,8p-2].
*
* This range lies within the range [0,8p-1] and the result is
* therefore a valid X25519 unsigned 258-bit integer, as
* required.
*/
bigint_add ( &addend->value, &result->value );
}
/**
* Subtract big integers modulo field prime
*
* @v minuend Big integer from which to subtract
* @v subtrahend Big integer to subtract
* @v result Big integer to hold result (may overlap minuend)
*/
static inline __attribute__ (( always_inline )) void
x25519_subtract ( const union x25519_quad257 *minuend,
const union x25519_quad257 *subtrahend,
union x25519_oct258 *result ) {
int copy;
/* Copy minuend if necessary */
copy = ( result != &minuend->oct258 );
build_assert ( __builtin_constant_p ( copy ) );
if ( copy ) {
build_assert ( result != &subtrahend->oct258 );
bigint_copy ( &minuend->oct258.value, &result->value );
}
/* Perform subtraction
*
* Both inputs are in the range [0,4p-1] and the resulting
* difference is therefore in the range [1-4p,4p-1].
*
* This range lies partially outside the range [0,8p-1] and
* the result is therefore not yet a valid X25519 unsigned
* 258-bit integer.
*/
bigint_subtract ( &subtrahend->value, &result->value );
/* Add constant multiple of field prime p
*
* Add the constant 4p to the result. This brings the result
* within the range [1,8p-1] (without changing the value
* modulo p).
*
* This range lies within the range [0,8p-1] and the result is
* therefore now a valid X25519 unsigned 258-bit integer, as
* required.
*/
bigint_add ( &x25519_4p, &result->value );
}
/**
* Multiply big integers modulo field prime
*
* @v multiplicand Big integer to be multiplied
* @v multiplier Big integer to be multiplied
* @v result Big integer to hold result (may overlap either input)
*/
void x25519_multiply ( const union x25519_oct258 *multiplicand,
const union x25519_oct258 *multiplier,
union x25519_quad257 *result ) {
union x25519_multiply_workspace tmp;
union x25519_multiply_step1 *step1 = &tmp.step1;
union x25519_multiply_step2 *step2 = &tmp.step2;
union x25519_multiply_step3 *step3 = &tmp.step3;
/* Step 1: perform raw multiplication
*
* step1 = multiplicand * multiplier
*
* Both inputs are 258-bit numbers and the step 1 result is
* therefore 258+258=516 bits.
*/
static_assert ( sizeof ( step1->product ) >= sizeof ( step1->parts ) );
bigint_multiply ( &multiplicand->value, &multiplier->value,
&step1->product );
/* Step 2: reduce high-order 516-256=260 bits of step 1 result
*
* Use the identity 2^256=38 (mod p) to reduce the high-order
* bits of the step 1 result. We split the 516-bit result
* from step 1 into its low-order 256 bits and high-order 260
* bits:
*
* step1 = step1(low 256 bits) + step1(high 260 bits) * 2^256
*
* and then perform the calculation:
*
* step2 = step1 (mod p)
* = step1(low 256 bits) + step1(high 260 bits) * 2^256 (mod p)
* = step1(low 256 bits) + step1(high 260 bits) * 38 (mod p)
*
* There are 6 bits in the constant value 38. The step 2
* multiplication product will therefore have 260+6=266 bits,
* and the step 2 result (after the addition) will therefore
* have 267 bits.
*/
static_assert ( sizeof ( step2->product ) >= sizeof ( step2->value ) );
static_assert ( sizeof ( step2->product ) >= sizeof ( step2->parts ) );
bigint_grow ( &step1->parts.low_256bit, &result->value );
bigint_multiply ( &step1->parts.high_260bit, &x25519_reduce_256,
&step2->product );
bigint_add ( &result->value, &step2->value );
/* Step 3: reduce high-order 267-256=11 bits of step 2 result
*
* Use the identity 2^256=38 (mod p) again to reduce the
* high-order bits of the step 2 result. As before, we split
* the 267-bit result from step 2 into its low-order 256 bits
* and high-order 11 bits:
*
* step2 = step2(low 256 bits) + step2(high 11 bits) * 2^256
*
* and then perform the calculation:
*
* step3 = step2 (mod p)
* = step2(low 256 bits) + step2(high 11 bits) * 2^256 (mod p)
* = step2(low 256 bits) + step2(high 11 bits) * 38 (mod p)
*
* There are 6 bits in the constant value 38. The step 3
* multiplication product will therefore have 11+6=19 bits,
* and the step 3 result (after the addition) will therefore
* have 257 bits.
*
* A loose upper bound for the step 3 result (after the
* addition) is given by:
*
* step3 < ( 2^256 - 1 ) + ( 2^19 - 1 )
* < ( 2^257 - 2^256 - 1 ) + ( 2^19 - 1 )
* < ( 2^257 - 76 ) - 2^256 + 2^19 + 74
* < 4 * ( 2^255 - 19 ) - 2^256 + 2^19 + 74
* < 4p - 2^256 + 2^19 + 74
*
* and so the step 3 result is strictly less than 4p, and
* therefore lies within the range [0,4p-1].
*/
memset ( &step3->value, 0, sizeof ( step3->value ) );
bigint_grow ( &step2->parts.low_256bit, &result->value );
bigint_multiply ( &step2->parts.high_11bit, &x25519_reduce_256,
&step3->product );
bigint_add ( &step3->value, &result->value );
/* Step 1 calculates the product of the input operands, and
* each subsequent step reduces the number of bits in the
* result while preserving this value (modulo p). The final
* result is therefore equal to the product of the input
* operands (modulo p), as required.
*
* The step 3 result lies within the range [0,4p-1] and the
* final result is therefore a valid X25519 unsigned 257-bit
* integer, as required.
*/
}
/**
* Compute multiplicative inverse
*
* @v invertend Big integer to be inverted
* @v result Big integer to hold result (may not overlap input)
*/
void x25519_invert ( const union x25519_oct258 *invertend,
union x25519_quad257 *result ) {
int i;
/* Sanity check */
assert ( invertend != &result->oct258 );
/* Calculate inverse as x^(-1)=x^(p-2) where p is the field prime
*
* The field prime is p=2^255-19 and so:
*
* p - 2 = 2^255 - 21
* = (2^255 - 1) - 2^4 - 2^2
*
* i.e. p-2 is a 254-bit number in which all bits are set
* apart from bit 2 and bit 4.
*
* We use the square-and-multiply method to compute x^(p-2).
*/
bigint_copy ( &invertend->value, &result->value );
for ( i = 253 ; i >= 0 ; i-- ) {
/* Square running total */
x25519_multiply ( &result->oct258, &result->oct258, result );
/* For each set bit in the exponent, multiply by invertend */
if ( ( i != 2 ) && ( i != 4 ) ) {
x25519_multiply ( invertend, &result->oct258, result );
}
}
}
/**
* Reduce big integer via conditional subtraction
*
* @v subtrahend Big integer to subtract
* @v value Big integer to be subtracted from, if possible
*/
static void x25519_reduce_by ( const x25519_t *subtrahend, x25519_t *value ) {
unsigned int max_bit = ( ( 8 * sizeof ( *value ) ) - 1 );
x25519_t tmp;
/* Conditionally subtract subtrahend
*
* Subtract the subtrahend, discarding the result (in constant
* time) if the subtraction underflows.
*/
bigint_copy ( value, &tmp );
bigint_subtract ( subtrahend, value );
bigint_swap ( value, &tmp, bigint_bit_is_set ( value, max_bit ) );
}
/**
* Reduce big integer to canonical range
*
* @v value Big integer to be reduced
*/
void x25519_reduce ( union x25519_quad257 *value ) {
/* Conditionally subtract 2p
*
* Subtract twice the field prime, discarding the result (in
* constant time) if the subtraction underflows.
*
* The input value is in the range [0,4p-1]. After this
* conditional subtraction, the value is in the range
* [0,2p-1].
*/
x25519_reduce_by ( &x25519_2p, &value->value );
/* Conditionally subtract p
*
* Subtract the field prime, discarding the result (in
* constant time) if the subtraction underflows.
*
* The value is already in the range [0,2p-1]. After this
* conditional subtraction, the value is in the range [0,p-1]
* and is therefore the canonical representation.
*/
x25519_reduce_by ( &x25519_p, &value->value );
}
/**
* Compute next step of the Montgomery ladder
*
* @v base Base point
* @v bit Bit value
* @v step Ladder step
*/
static void x25519_step ( const union x25519_quad257 *base, int bit,
struct x25519_step *step ) {
union x25519_quad257 *a = &step->x_n.X;
union x25519_quad257 *b = &step->x_n1.X;
union x25519_quad257 *c = &step->x_n.Z;
union x25519_quad257 *d = &step->x_n1.Z;
union x25519_oct258 e;
union x25519_quad257 f;
union x25519_oct258 *v1_e;
union x25519_oct258 *v2_a;
union x25519_oct258 *v3_c;
union x25519_oct258 *v4_b;
union x25519_quad257 *v5_d;
union x25519_quad257 *v6_f;
union x25519_quad257 *v7_a;
union x25519_quad257 *v8_c;
union x25519_oct258 *v9_e;
union x25519_oct258 *v10_a;
union x25519_quad257 *v11_b;
union x25519_oct258 *v12_c;
union x25519_quad257 *v13_a;
union x25519_oct258 *v14_a;
union x25519_quad257 *v15_c;
union x25519_quad257 *v16_a;
union x25519_quad257 *v17_d;
union x25519_quad257 *v18_b;
/* See the referenced paper "Implementing Curve25519/X25519: A
* Tutorial on Elliptic Curve Cryptography" for the reasoning
* behind this calculation.
*/
/* Reuse storage locations for intermediate results where possible */
v1_e = &e;
v2_a = container_of ( &a->value, union x25519_oct258, value );
v3_c = container_of ( &c->value, union x25519_oct258, value );
v4_b = container_of ( &b->value, union x25519_oct258, value );
v5_d = d;
v6_f = &f;
v7_a = a;
v8_c = c;
v9_e = &e;
v10_a = container_of ( &a->value, union x25519_oct258, value );
v11_b = b;
v12_c = container_of ( &c->value, union x25519_oct258, value );
v13_a = a;
v14_a = container_of ( &a->value, union x25519_oct258, value );
v15_c = c;
v16_a = a;
v17_d = d;
v18_b = b;
/* Select inputs */
bigint_swap ( &a->value, &b->value, bit );
bigint_swap ( &c->value, &d->value, bit );
/* v1 = a + c */
x25519_add ( a, c, v1_e );
/* v2 = a - c */
x25519_subtract ( a, c, v2_a );
/* v3 = b + d */
x25519_add ( b, d, v3_c );
/* v4 = b - d */
x25519_subtract ( b, d, v4_b );
/* v5 = v1^2 = (a + c)^2 = a^2 + 2ac + c^2 */
x25519_multiply ( v1_e, v1_e, v5_d );
/* v6 = v2^2 = (a - c)^2 = a^2 - 2ac + c^2 */
x25519_multiply ( v2_a, v2_a, v6_f );
/* v7 = v3 * v2 = (b + d) * (a - c) = ab - bc + ad - cd */
x25519_multiply ( v3_c, v2_a, v7_a );
/* v8 = v4 * v1 = (b - d) * (a + c) = ab + bc - ad - cd */
x25519_multiply ( v4_b, v1_e, v8_c );
/* v9 = v7 + v8 = 2 * (ab - cd) */
x25519_add ( v7_a, v8_c, v9_e );
/* v10 = v7 - v8 = 2 * (ad - bc) */
x25519_subtract ( v7_a, v8_c, v10_a );
/* v11 = v10^2 = 4 * (ad - bc)^2 */
x25519_multiply ( v10_a, v10_a, v11_b );
/* v12 = v5 - v6 = (a + c)^2 - (a - c)^2 = 4ac */
x25519_subtract ( v5_d, v6_f, v12_c );
/* v13 = v12 * 121665 = 486660ac = (A-2) * ac */
x25519_multiply ( v12_c, &x25519_121665, v13_a );
/* v14 = v13 + v5 = (A-2) * ac + a^2 + 2ac + c^2 = a^2 + A * ac + c^2 */
x25519_add ( v13_a, v5_d, v14_a );
/* v15 = v12 * v14 = 4ac * (a^2 + A * ac + c^2) */
x25519_multiply ( v12_c, v14_a, v15_c );
/* v16 = v5 * v6 = (a + c)^2 * (a - c)^2 = (a^2 - c^2)^2 */
x25519_multiply ( &v5_d->oct258, &v6_f->oct258, v16_a );
/* v17 = v11 * base = 4 * base * (ad - bc)^2 */
x25519_multiply ( &v11_b->oct258, &base->oct258, v17_d );
/* v18 = v9^2 = 4 * (ab - cd)^2 */
x25519_multiply ( v9_e, v9_e, v18_b );
/* Select outputs */
bigint_swap ( &a->value, &b->value, bit );
bigint_swap ( &c->value, &d->value, bit );
}
/**
* Multiply X25519 elliptic curve point
*
* @v base Base point
* @v scalar Scalar multiple
* @v result Point to hold result (may overlap base point)
*/
static void x25519_ladder ( const union x25519_quad257 *base,
struct x25519_value *scalar,
union x25519_quad257 *result ) {
static const uint8_t zero[] = { 0 };
static const uint8_t one[] = { 1 };
struct x25519_step step;
union x25519_quad257 *tmp;
int bit;
int i;
/* Initialise ladder */
bigint_init ( &step.x_n.X.value, one, sizeof ( one ) );
bigint_init ( &step.x_n.Z.value, zero, sizeof ( zero ) );
bigint_copy ( &base->value, &step.x_n1.X.value );
bigint_init ( &step.x_n1.Z.value, one, sizeof ( one ) );
/* Use ladder */
for ( i = 254 ; i >= 0 ; i-- ) {
bit = ( ( scalar->raw[ i / 8 ] >> ( i % 8 ) ) & 1 );
x25519_step ( base, bit, &step );
}
/* Convert back to affine coordinate */
tmp = &step.x_n1.X;
x25519_invert ( &step.x_n.Z.oct258, tmp );
x25519_multiply ( &step.x_n.X.oct258, &tmp->oct258, result );
x25519_reduce ( result );
}
/**
* Reverse X25519 value endianness
*
* @v value Value to reverse
*/
static void x25519_reverse ( struct x25519_value *value ) {
uint8_t *low = value->raw;
uint8_t *high = &value->raw[ sizeof ( value->raw ) - 1 ];
uint8_t tmp;
/* Reverse bytes */
do {
tmp = *low;
*low = *high;
*high = tmp;
} while ( ++low < --high );
}
/**
* Calculate X25519 key
*
* @v base Base point
* @v scalar Scalar multiple
* @v result Point to hold result (may overlap base point)
* @ret rc Return status code
*/
int x25519_key ( const struct x25519_value *base,
const struct x25519_value *scalar,
struct x25519_value *result ) {
struct x25519_value *tmp = result;
union x25519_quad257 point;
/* Reverse base point and clear high bit as required by RFC7748 */
memcpy ( tmp, base, sizeof ( *tmp ) );
x25519_reverse ( tmp );
tmp->raw[0] &= 0x7f;
bigint_init ( &point.value, tmp->raw, sizeof ( tmp->raw ) );
/* Clamp scalar as required by RFC7748 */
memcpy ( tmp, scalar, sizeof ( *tmp ) );
tmp->raw[0] &= 0xf8;
tmp->raw[31] |= 0x40;
/* Multiply elliptic curve point */
x25519_ladder ( &point, tmp, &point );
/* Reverse result */
bigint_done ( &point.value, result->raw, sizeof ( result->raw ) );
x25519_reverse ( result );
/* Fail if result was all zeros (as required by RFC8422) */
return ( bigint_is_zero ( &point.value ) ? -EPERM : 0 );
}
/**
* Multiply scalar by curve point
*
* @v base Base point (or NULL to use generator)
* @v scalar Scalar multiple
* @v result Result point to fill in
* @ret rc Return status code
*/
static int x25519_curve_multiply ( const void *base, const void *scalar,
void *result ) {
/* Use base point if applicable */
if ( ! base )
base = &x25519_generator;
return x25519_key ( base, scalar, result );
}
/** X25519 elliptic curve */
struct elliptic_curve x25519_curve = {
.name = "x25519",
.keysize = sizeof ( struct x25519_value ),
.multiply = x25519_curve_multiply,
};

View File

@ -1603,19 +1603,12 @@ int x509_check_name ( struct x509_certificate *cert, const char *name ) {
static void x509_free_chain ( struct refcnt *refcnt ) {
struct x509_chain *chain =
container_of ( refcnt, struct x509_chain, refcnt );
struct x509_link *link;
struct x509_link *tmp;
DBGC2 ( chain, "X509 chain %p freed\n", chain );
/* Free each link in the chain */
list_for_each_entry_safe ( link, tmp, &chain->links, list ) {
x509_put ( link->cert );
list_del ( &link->list );
free ( link );
}
/* Free chain */
x509_truncate ( chain, NULL );
assert ( list_empty ( &chain->links ) );
free ( chain );
}
@ -1696,6 +1689,27 @@ int x509_append_raw ( struct x509_chain *chain, const void *data,
return rc;
}
/**
* Truncate X.509 certificate chain
*
* @v chain X.509 certificate chain
* @v link Link after which to truncate chain, or NULL
*/
void x509_truncate ( struct x509_chain *chain, struct x509_link *link ) {
struct x509_link *tmp;
/* Truncate entire chain if no link is specified */
if ( ! link )
link = list_entry ( &chain->links, struct x509_link, list );
/* Free each link in the chain */
list_for_each_entry_safe_continue ( link, tmp, &chain->links, list ) {
x509_put ( link->cert );
list_del ( &link->list );
free ( link );
}
}
/**
* Identify X.509 certificate by subject
*

View File

@ -545,8 +545,8 @@ static int arbel_mad ( struct ib_device *ibdev, union ib_mad *mad ) {
union arbelprm_mad mad_ifc;
int rc;
linker_assert ( sizeof ( *mad ) == sizeof ( mad_ifc.mad ),
mad_size_mismatch );
/* Sanity check */
static_assert ( sizeof ( *mad ) == sizeof ( mad_ifc.mad ) );
/* Copy in request packet */
memcpy ( &mad_ifc.mad, mad, sizeof ( mad_ifc.mad ) );
@ -3139,8 +3139,8 @@ static void arbel_remove ( struct pci_device *pci ) {
}
static struct pci_device_id arbel_nics[] = {
PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver", 0 ),
PCI_ROM ( 0x15b3, 0x6274, "mt25204", "MT25204 HCA driver", 0 ),
PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver", 0 ),
};
struct pci_driver arbel_driver __pci_driver = {

View File

@ -2502,7 +2502,7 @@ static mlx_status shomron_fill_eth_send_wqe ( struct ib_device *ibdev,
}
#define SHOMRON_GENERATE_CQE 0x3
#define SHOMRON_INLINE_HEADERS_SIZE 18
#define SHOMRON_INLINE_HEADERS_SIZE ETH_HLEN
#define SHOMRON_INLINE_HEADERS_OFFSET 32
MLX_FILL_2 ( &eth_wqe->ctrl, 0, opcode, FLEXBOOT_NODNIC_OPCODE_SEND,
wqe_index, wqe_index & 0xFFFF);

Some files were not shown because too many files have changed in this diff Show More