Compare commits

...

191 Commits

Author SHA1 Message Date
c7f2e75519 [aqc1xx] Add support for Marvell AQtion Ethernet controller
This patch adds support for the AQtion Ethernet controller, enabling
iPXE to recognize and utilize the specific models (AQC114, AQC113, and
AQC107).

Tested-by: Animesh Bhatt <animeshb@marvell.com>
Signed-off-by: Animesh Bhatt <animeshb@marvell.com>
2024-09-02 13:45:54 +01:00
7f75d320f6 [etherfabric] Fix use of uninitialised variable in falcon_xaui_link_ok()
The link status check in falcon_xaui_link_ok() reads from the
FCN_XX_CORE_STAT_REG_MAC register only on production hardware (where
the FPGA version reads as zero), but modifies the value and writes
back to this register unconditionally.  This triggers an uninitialised
variable warning on newer versions of gcc.

Fix by assuming that the register exists only on production hardware,
and so moving the "modify-write" portion of the "read-modify-write"
operation to also be covered by the same conditional check.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-09-02 12:24:57 +01:00
301644ab48 [test] Add CMS decryption self-tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-29 23:36:00 +01:00
5e69cf08d7 [crypto] Allow cms_decrypt() to be called on unregistered images
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-29 23:31:10 +01:00
72316b820d [image] Add the "imgdecrypt" command
Add the "imgdecrypt" command that can be used to decrypt a detached
encrypted data image using a cipher key obtained from a separate CMS
envelope image.  For example:

  # Create non-detached encrypted CMS messages
  #
  openssl cms -encrypt -binary -aes-256-gcm -recip client.crt \
              -in vmlinuz -outform DER -out vmlinuz.cms
  openssl cms -encrypt -binary -aes-256-gcm -recip client.crt \
              -in initrd.img -outform DER -out initrd.img.cms

  # Detach data from envelopes (using iPXE's contrib/crypto/cmsdetach)
  #
  cmsdetach vmlinuz.cms -d vmlinuz.dat -e vmlinuz.env
  cmsdetach initrd.img.cms -d initrd.img.dat -e initrd.img.env

and then within iPXE:

  #!ipxe
  imgfetch http://192.168.0.1/vmlinuz.dat
  imgfetch http://192.168.0.1/initrd.img.dat
  imgdecrypt vmlinuz.dat    http://192.168.0.1/vmlinuz.env
  imgdecrypt initrd.img.dat http://192.168.0.1/initrd.img.env
  boot vmlinuz

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-29 15:11:30 +01:00
486b15b3c1 [crypto] Support decryption of images via CMS envelopes
Add support for decrypting images containing detached encrypted data
using a cipher key obtained from a separate CMS envelope image (in DER
or PEM format).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-29 14:47:13 +01:00
49404bfea9 [image] Split image_strip_suffix() out from image_extract()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-29 13:09:41 +01:00
748cab7745 [crypto] Add cmsdetach script for detaching encrypted data from CMS messages
The openssl toolchain does not currently seem to support creating CMS
envelopedData or authEnvelopedData messages with detached encrypted
data.

Add a standalone tool "cmsdetach" that can be used to detach the
encrypted data from a CMS message.  For example:

  openssl cms -encrypt -binary -aes-256-gcm -recip client.crt \
              -in bootfile -outform DER -out bootfile.cms

  cmsdetach bootfile.cms --data bootfile.dat --envelope bootfile.env

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-28 16:17:14 +01:00
b053ba1988 [test] Update CMS self-test terminology
Generalise CMS self-test data structure and macro names to refer to
"messages" rather than "signatures", in preparation for adding image
decryption tests.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-28 13:03:55 +01:00
4b4a362f07 [crypto] Allow for extraction of ASN.1 algorithm parameters
Some ASN.1 OID-identified algorithms require additional parameters,
such as an initialisation vector for a block cipher.  The structure of
the parameters is defined by the individual algorithm.

Extend asn1_algorithm() to allow these additional parameters to be
returned via a separate ASN.1 cursor.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-28 13:03:55 +01:00
bdb5b4aef4 [crypto] Hold CMS message as a single ASN.1 object
Reduce the number of dynamic allocations required to parse a CMS
message by retaining the ASN.1 cursor returned from image_asn1() for
the lifetime of the CMS message.  This allows embedded ASN.1 cursors
to be used for parsed objects within the message, such as embedded
signatures.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-23 13:43:42 +01:00
46937a9df6 [crypto] Remove the concept of a public-key algorithm reusable context
Instances of cipher and digest algorithms tend to get called
repeatedly to process substantial amounts of data.  This is not true
for public-key algorithms, which tend to get called only once or twice
for a given key.

Simplify the public-key algorithm API so that there is no reusable
algorithm context.  In particular, this allows callers to omit the
error handling currently required to handle memory allocation (or key
parsing) errors from pubkey_init(), and to omit the cleanup calls to
pubkey_final().

This change does remove the ability for a caller to distinguish
between a verification failure due to a memory allocation failure and
a verification failure due to a bad signature.  This difference is not
material in practice: in both cases, for whatever reason, the caller
was unable to verify the signature and so cannot proceed further, and
the cause of the error will be visible to the user via the return
status code.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-21 21:00:57 +01:00
acbabdb335 [tls] Group client and server state in TLS connection structure
The TLS connection structure has grown to become unmanageably large as
new features and support for new TLS protocol versions have been added
over time.

Split out the portions of struct tls_connection that are specific to
client and server operations into separate structures, and simplify
some structure field names.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-21 12:15:24 +01:00
c9cac76a5c [tls] Group transmit and receive state in TLS connection structure
The TLS connection structure has grown to become unmanageably large as
new features and support for new TLS protocol versions have been added
over time.

Split out the portions of struct tls_connection that are specific to
transmit and receive operations into separate structures, and simplify
some structure field names.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-21 11:59:43 +01:00
be2784649d [gve] Add missing error codes in EUNIQ() list of potential errors
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-20 22:44:15 +01:00
ab5743efc5 [contrib] Remove obsolete rom-o-matic code
The rom-o-matic code does not form part of the iPXE codebase, has not
been maintained for over a decade, and does not appear to still be in
use anywhere in the world.

It does, however, result in a large number of false positive security
vulnerability reports from some low quality automated code analysis
tools such as Fortify SCA.

Remove this unused and obsolete code to reduce the burden of
responding to these false positives.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-20 10:22:18 +01:00
633f4f362d [test] Generalise public-key algorithm tests and use okx()
Generalise the existing support for performing RSA public-key
encryption, decryption, signature, and verification tests, and update
the code to use okx() for neater reporting of test results.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-18 23:51:43 +01:00
53f089b723 [crypto] Pass asymmetric keys as ASN.1 cursors
Asymmetric keys are invariably encountered within ASN.1 structures
such as X.509 certificates, and the various large integers within an
RSA key are themselves encoded using ASN.1.

Simplify all code handling asymmetric keys by passing keys as a single
ASN.1 cursor, rather than separate data and length pointers.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-18 15:44:38 +01:00
950f6b5861 [efi] Allow discovery of PCI bus:dev.fn address ranges
Generalise the logic for identifying the matching PCI root bridge I/O
protocol to allow for identifying the closest matching PCI bus:dev.fn
address range, and use this to provide PCI address range discovery
(while continuing to inhibit automatic PCI bus probing).

This allows the "pciscan" command to work as expected under UEFI.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-15 09:39:01 +01:00
7c82ff0b6b [pci] Separate permission to probe buses from bus:dev.fn range discovery
The UEFI device model requires us to not probe the PCI bus directly,
but instead to wait to be offered the opportunity to drive devices via
our driver service binding handle.

We currently inhibit PCI bus probing by having pci_discover() return
an empty range when using the EFI PCI I/O API.  This has the unwanted
side effect that scanning the bus manually using the "pciscan" command
will also fail to discover any devices.

Separate out the concept of being allowed to probe PCI buses from the
mechanism for discovering PCI bus:dev.fn address ranges, so that this
limitation may be removed.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-15 09:31:14 +01:00
9d9465b140 [crypto] Fix debug name for empty certificate chain validators
An attempt to use a validator for an empty certificate chain will
correctly fail the overall validation with the "empty certificate
chain" error propagated from x509_auto_append().

In a debug build, the call to validator_name() will attempt to call
x509_name() on a non-existent certificate, resulting in garbage in the
debug message.

Fix by checking for the special case of an empty certificate chain.

This issue does not affect non-debug builds, since validator_name() is
(as per its description) called only for debug messages.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-14 14:07:41 +01:00
97635eb71b [crypto] Generalise cms_signature to cms_message
There is some exploitable similarity between the data structures used
for representing CMS signatures and CMS encryption keys.  In both
cases, the CMS message fundamentally encodes a list of participants
(either message signers or message recipients), where each participant
has an associated certificate and an opaque octet string representing
the signature or encrypted cipher key.  The ASN.1 structures are not
identical, but are sufficiently similar to be worth exploiting: for
example, the SignerIdentifier and RecipientIdentifier data structures
are defined identically.

Rename data structures and functions, and add the concept of a CMS
message type.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-14 13:04:01 +01:00
998edc6ec5 [crypto] Add OID-identified algorithms for AES ciphers
Extend the definition of an ASN.1 OID-identified algorithm to include
a potential cipher suite, and add identifiers for AES-CBC and AES-GCM.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-14 13:04:01 +01:00
3b4d0cb555 [crypto] Pass image as parameter to CMS functions
The cms_signature() and cms_verify() functions currently accept raw
data pointers.  This will not be possible for cms_decrypt(), which
will need the ability to extract fragments of ASN.1 data from a
potentially large image.

Change cms_signature() and cms_verify() to accept an image as an input
parameter, and move the responsibility for setting the image trust
flag within cms_verify() since that now becomes a more natural fit.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-13 12:30:51 +01:00
96fb7a0a93 [crypto] Allow passing a NULL certificate store to x509_find() et al
Allow passing a NULL value for the certificate list to all functions
used for identifying an X.509 certificate from an existing set of
certificates, and rename function parameters to indicate that this
certificate list represents an unordered certificate store (rather
than an ordered certificate chain).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-13 12:26:31 +01:00
d85590b658 [crypto] Centralise mechanisms for identifying X.509 certificates
Centralise all current mechanisms for identifying an X.509 certificate
(by raw content, by subject, by issuer and serial number, and by
matching public key), and remove the certstore-specific and
CMS-specific variants of these functions.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-12 12:38:08 +01:00
59e2b03e6a [crypto] Extend asn1_enter() to handle partial object cursors
Handling large ASN.1 objects such as encrypted CMS files will require
the ability to use the asn1_enter() and asn1_skip() family of
functions on partial object cursors, where a defined additional length
is known to exist after the end of the data buffer pointed to by the
ASN.1 object cursor.

We already have support for partial object cursors in the underlying
asn1_start() operation used by both asn1_enter() and asn1_skip(), and
this is used by the DER image probe routine to check that the
potential DER file comprises a single ASN.1 SEQUENCE object.

Add asn1_enter_partial() to formalise the process of entering an ASN.1
partial object, and refactor the DER image probe routine to use this
instead of open-coding calls to the underlying asn1_start() operation.

There is no need for an equivalent asn1_skip_partial() function, since
only objects that are wholly contained within the partial cursor may
be successfully skipped.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-07 16:26:19 +01:00
0e73b48f77 [crypto] Clarify ASN.1 cursor invalidation behaviour
Calling asn1_skip_if_exists() on a malformed ASN.1 object may
currently leave the cursor in a partially-updated state, where the tag
byte and one of the length bytes have been stripped.  The cursor is
left with a valid data pointer and length and so no out-of-bounds
access can arise, but the cursor no longer points to the start of an
ASN.1 object.

Ensure that each ASN.1 cursor manipulation code path leads to the
cursor being either fully updated, left unmodified, or invalidated,
and update the function descriptions to reflect this.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-07 16:11:57 +01:00
309ac8fd21 [crypto] Do not return an error when skipping the final ASN.1 object
Successfully reaching the end of a well-formed ASN.1 object list is
arguably not an error, but the current code (dating back to the
original ASN.1 commit in 2007) will explicitly check for and report
this as an error condition.

Remove the explicit check for reaching the end of a well-formed ASN.1
object list, and instead return success along with a zero-length (and
hence implicitly invalidated) cursor.

Almost every existing caller of asn1_skip() or asn1_skip_if_exists()
currently ignores the return value anyway.  Skipped objects are (by
definition) not of interest to the caller, and the invalidation
behaviour of asn1_skip() ensures that any errors will be safely caught
on a subsequent attempt to actually use the ASN.1 object content.
Since these existing callers ignore the return value, they cannot be
affected by this change.

There is one existing caller of asn1_skip_if_exists() that does check
the return value: in asn1_skip() itself, an error returned from
asn1_skip_if_exists() will cause the cursor to be invalidated.  In the
case of an error indicating only that the cursor length is already
zero, invalidation is a no-op, and so this change affects only the
return value propagated from asn1_skip().

This leaves only a single call site within ocsp_request() where the
return value from asn1_skip() is currently checked.  The return status
here is moot since there is no way for the code in question to fail
(absent a bug in the ASN.1 construction or parsing code).

There are therefore no callers of asn1_skip() or asn1_skip_if_exists()
that rely on an error being returned for successfully reaching the end
of a well-formed ASN.1 object list.  Simplify the code by redefining
this as a successful outcome.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-07 13:06:23 +01:00
a064d39768 [cpuid] Allow hypervisor CPUID leaves to be accessed as settings
Redefine bit 30 of an SMBIOS numerical setting to be part of the
function number, in order to allow access to hypervisor CPUID leaves.

This technically breaks backwards compatibility with scripts
attempting to read more than 64 consecutive functions.  Since there is
no meaningful block of 64 consecutive related functions, it is
vanishingly unlikely that this capability has ever been used.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-01 12:54:49 +01:00
121d96b903 [cpuid] Allow reading hypervisor CPUID leaves
Hypervisors typically intercept CPUID leaves in the range 0x40000000
to 0x400000ff, with leaf 0x40000000 returning the maximum supported
function within this range in register %eax.

iPXE currently masks off bit 30 from the requested CPUID leaf when
checking to see if a function is supported, which causes this check to
read from leaf 0x00000000 instead of 0x40000000.

Fix by including bit 30 within the mask.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-08-01 12:49:48 +01:00
c117e6a481 [smbios] Allow reading an entire SMBIOS data structure as a setting
The general syntax for SMBIOS settings:

  smbios/<instance>.<type>.<offset>.<length>

is currently extended such that a <length> of zero indicates that the
byte at <offset> contains a string index, and an <offset> of zero
indicates that the <length> contains a literal string index.

Since the byte at offset zero can never contain a string index, and a
literal string index can never have a zero value, the combination of
both <length> and <offset> being zero is currently invalid and will
always return "not found".

Extend the syntax such that the combination of both <length> and
<offset> being zero may be used to read the entire data structure.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-07-31 16:26:48 +01:00
60d682409e [smbios] Avoid reading beyond end of constructed SMBIOS setting
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-07-31 16:20:37 +01:00
0dc8933f67 [cloud] Add utility to read INT13CON partition in Google Compute Engine
Following the example of aws-int13con, add a utility that can be used
to read the INT13 console log from a used iPXE boot disk in Google
Compute Engine.

There seems to be no easy way to directly read the contents of either
a disk image or a snapshot in Google Cloud.  Work around this
limitation by creating a snapshot and attaching this snapshot as a
data disk to a temporary Linux instance, which is then used to echo
the INT13 console log to the serial port.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-07-30 16:11:28 +01:00
d2d194bc60 [gve] Increase number of receive buffers to reduce packet loss
Experiments suggest that using fewer than 64 receive buffers leads to
excessive packet drop rates on some instance types (observed with a
c3-standard-4 instance in europe-west4-a).

Fix by increasing the number of receive data buffers (and adjusting
the length of the registrable queue page address list to match).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-07-25 00:13:33 +01:00
c7b76e3adc [gve] Add driver for Google Virtual Ethernet NIC
The Google Virtual Ethernet NIC (GVE or gVNIC) is found only in Google
Cloud instances.  There is essentially zero documentation available
beyond the mostly uncommented source code in the Linux kernel.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-07-24 14:45:46 +01:00
5a9f476d4f [cloud] Add utility for importing images to Google Compute Engine
Following the example of aws-import, add a utility that can be used to
upload an iPXE disk image to Google Compute Engine as a bootable
image.  For example:

  make CONFIG=cloud EMBED=config/cloud/gce.ipxe \
       bin-x86_64-pcbios/ipxe.usb bin-x86_64-efi/ipxe.usb

  make CONFIG=cloud EMBED=config/cloud/gce.ipxe \
       CROSS=aarch64-linux-gnu- bin-arm64-efi/ipxe.usb

  ../contrib/cloud/gce-import -p \
       bin-x86_64-pcbios/ipxe.usb \
       bin-x86_64-efi/ipxe.usb \
       bin-arm64-efi/ipxe.usb

The iPXE disk image is automatically wrapped into a tarball containing
a single file named "disk.raw", uploaded to a temporary bucket in
Google Cloud Storage, and used to create a bootable image.  The
temporary bucket is deleted after use.

An appropriate image family name is identified automatically: "ipxe"
for BIOS images, "ipxe-uefi-x86-64" for x86_64 UEFI images, and
"ipxe-uefi-arm64" for AArch64 UEFI images.  This allows the latest
image within each family to be launched within needing to know the
precise image name.

Google Compute Engine images are globally scoped and are available
(and cached upon first use) in all regions.  The initial placement of
the image may be controlled indirectly by using the "--location"
option to specify the Google Cloud Storage location used for the
temporary upload bucket: the image will then be created in the closest
multi-region to the storage location.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-07-08 13:31:43 +01:00
b66e27d9b2 [ipv6] Expose router address for DHCPv6 leased addresses
The DHCPv6 protocol does not itself provide a router address or a
prefix length.  This information is instead obtained from the router
advertisements.

Our IPv6 minirouting table construction logic will first construct an
entry for each advertised prefix, and later update the entry to
include an address assigned within that prefix via stateful DHCPv6 (if
applicable).

This logic fails if the address assigned via stateful DHCPv6 does not
fall within any of the advertised prefixes (e.g. if the network is
configured to use DHCPv6-assigned /128 addresses with no advertised
on-link prefixes).  We will currently treat this situation as
equivalent to having a manually assigned address with no corresponding
router address or prefix length: the routing table entry will use the
default /64 prefix length and will not include the router address.

DHCPv6 is triggered only in response to a router advertisement with
the "Managed Address Configuration (M)" or "Other Configuration (O)"
flags set, and a router address is therefore available at the point
that we initiate DHCPv6.

Record the router address when initiating DHCPv6, and expose this
router address as part of the DHCPv6 settings block.  This allows the
routing table entry for any address assigned via stateful DHCPv6 to
correctly include the router address, even if the assigned address
does not fall within an advertised prefix.

Also provide a fixed /128 prefix length as part of the DHCPv6 settings
block.  When an address assigned via stateful DHCPv6 does not fall
within an advertised prefix, this will cause the routing table entry
to have a /128 prefix length as expected.  (When such an address does
fall within an advertised prefix, it will continue to use the
advertised prefix length.)

Originally-fixed-by: Guvenc Gulce <guevenc.guelce@sap.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-27 13:43:37 +01:00
77acf6b41f [ipv4] Support small subnets with no directed broadcast address
In a small subnet (with a /31 or /32 subnet mask), all addresses
within the subnet are valid host addresses: there is no separate
network address or directed broadcast address.

The logic used in iPXE to determine whether or not to use a link-layer
broadcast address will currently fail in these subnets.  In a /31
subnet, the higher of the two host addresses (i.e. the address with
all host bits set) will be treated as a broadcast address.  In a /32
subnet, the single valid host address will be treated as a broadcast
address.

Fix by adding the concept of a host mask, defined such that an address
in the local subnet with all of the mask bits set to zero represents
the network address, and an address in the local subnet with all of
the mask bits set to one represents the directed broadcast address.
For most subnets, this is simply the inverse of the subnet mask.  For
small subnets (/31 or /32) we can obtain the desired behaviour by
setting the host mask to all ones, so that only the local broadcast
address 255.255.255.255 will be treated as a broadcast address.

Originally-fixed-by: Lukas Stockner <lstockner@genesiscloud.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-26 05:01:58 -07:00
821bb326f8 [hci] Remove the generalised widget user interface abstraction
Remove the now-unused generalised text widget user interface, along
with the associated concept of a widget set and the implementation of
a read-only label widget.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-21 09:57:03 -07:00
162cc51b6d [form] Reimplement the "login" user interface
Rewrite the code implementing the "login" user interface to use a
predefined interactive form.  The command "login" then becomes roughly
equivalent to:

  #!ipxe

  form
  item          username   Username
  item --secret password   Password
  present

with the result that login form customisations (e.g. to add a Windows
domain name) may be implemented within the scripting language.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-21 09:45:44 -07:00
f417f0b6a5 [form] Add support for dynamically created interactive forms
Add support for presenting a dynamic user interface as an interactive
form, alongside the existing support for presenting a dynamic user
interface as a menu.

An interactive form may be used to allow a user to input (or edit)
values for multiple settings on a single screen, as a user-friendly
alternative to prompting for setting values via the "read" command.

In the present implementation, all input fields must fit on a single
screen (with no scrolling), and the only supported widget type is an
editable text box.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-20 16:28:46 -07:00
1c3c5e2b22 [dynui] Add concept of a secret user interface item
For interactive forms, the concept of a secret value becomes
meaningful (e.g. for password fields).

Add a flag to indicate that an item represents a secret value, and
allow this flag to be set via the "--secret" option of the "item"
command.

This flag has no meaning for menu items, but is silently accepted
anyway to keep the code size minimal.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-20 16:24:38 -07:00
039019039e [dynui] Allow for multiple flags on a user interface item
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-20 16:24:38 -07:00
c8e50bb0fd [dynui] Generalise mechanisms for looking up user interface items
Generalise the ability to look up a dynamic user interface item by
index or by shortcut key, to allow for reuse of this code for
interactive forms.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-20 14:51:28 -07:00
5719cde838 [dynui] Generalise the concept of a menu to a dynamic user interface
We currently have an abstract model of a dynamic menu as a list of
items, each of which has a name, a description, and assorted metadata
such as a shortcut key.  The "menu" and "item" commands construct
representations in this abstract model, and the "choose" command then
presents the items as a single-choice menu, with the selected item's
name used as the output value.

This same abstraction may be used to model a dynamic form as a list of
editable items, each of which has a corresponding setting name, an
optional description label, and assorted metadata such as a shortcut
key.  By defining a "form" command as an alias for the "menu" command,
we could construct and present forms using commands such as:

  #!ipxe
  form                     Login to ${url}
  item          username   Username or email address
  item --secret password   Password
  present

or

  #!ipxe
  form                Configure IPv4 networking for ${netX/ifname}
  item netX/ip        IPv4 address
  item netX/netmask   Subnet mask
  item netX/gateway   Gateway address
  item netX/dns       DNS server address
  present

Reusing the same abstract model for both menus and forms allows us to
minimise the increase in code size, since the implementation of the
"form" and "item" commands is essentially zero-cost.

Rename everything within the abstract data model from "menu" to
"dynamic user interface" to reflect this generalisation.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-06-20 14:26:06 -07:00
122777f789 [hci] Allow tab key to be used to cycle through UI elements
Add support for wraparound scrolling and allow the tab key to be used
to move forward through a list of elements, wrapping back around to
the beginning of the list on overflow.

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-17 15:55:28 +01:00
cb95b5b378 [efi] Veto the Dhcp6Dxe driver on all platforms
The reference implementation of Dhcp6Dxe in EDK2 has a fatal flaw: the
code in EfiDhcp6Stop() will poll the network in a tight loop until
either a response is received or a timer tick (at TPL_CALLBACK)
occurs.  When EfiDhcp6Stop() is called at TPL_CALLBACK or higher, this
will result in an endless loop and an apparently frozen system.

Since this is the reference implementation of Dhcp6Dxe, it is likely
that almost all platforms have the same problem.

Fix by vetoing the broken driver.  If the upstream driver is ever
fixed and a new version number issued, then we could plausibly test
against the version number exposed via the driver binding protocol.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-16 14:19:01 +01:00
40b5112440 [hci] Use dynamically allocated buffers for editable strings
Editable strings currently require a fixed-size buffer, which is
inelegant and limits the potential for creating interactive forms with
a variable number of edit box widgets.

Remove this limitation by switching to using a dynamically allocated
buffer for editable strings and edit box widgets.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-15 15:59:49 +01:00
27ecc36c0b [efi] Do not attempt to download autoexec.ipxe without a valid base URI
If we do not have a current working URI (after applying the EFI device
path settings and any cached DHCP settings), then an attempt to
download autoexec.ipxe will fail since there is no base URI from which
to resolve the full autoexec.ipxe URI.

Avoid this potentially confusing error message by attempting the
download only if we have successfully obtained a current working URI.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-15 14:28:38 +01:00
59f27d6935 [netdevice] Add "linktype" setting
Add a new setting to provide access to the link layer protocol type
from scripts.  This can be useful in order to skip configuring
interfaces based on their link layer protocol or, conversely,
configure only selected interface types (Ethernet, IPoIB, etc.)

Example script:

    set idx:int32 0
    :loop
    isset ${net${idx}/mac} || exit 0
    iseq ${net${idx}/linktype} IPoIB && goto try_next ||
    autoboot net${idx} ||
    :try_next
    inc idx && goto loop

Signed-off-by: Pavel Krotkiy <porsh@nebius.com>
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-03 12:53:46 +01:00
165995b7e9 [efi] Restructure handling of autoexec.ipxe script
We currently attempt to obtain the autoexec.ipxe script via early use
of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or EFI_PXE_BASE_CODE_PROTOCOL
interfaces to obtain an opaque block of memory, which is then
registered as an image at an appropriate point during our startup
sequence.  The early use of these existent interfaces allows us to
obtain the script even if our subsequent actions (e.g. disconnecting
drivers in order to connect up our own) may cause the script to become
inaccessible.

This mirrors the approach used under BIOS, where the autoexec.ipxe
script is provided by the prefix (e.g. as an initrd image when using
the .lkrn build of iPXE) and so must be copied into a normally
allocated image from wherever it happens to previously exist in
memory.

We do not currently have support for downloading an autoexec.ipxe
script if we were ourselves downloaded via UEFI HTTP boot.

There is an EFI_HTTP_PROTOCOL defined within the UEFI specification,
but it is so poorly designed as to be unusable for the simple purpose
of downloading an additional file from the same directory.  It
provides almost nothing more than a very slim wrapper around
EFI_TCP4_PROTOCOL (or EFI_TCP6_PROTOCOL).  It will not handle
redirection, content encoding, retries, or even fundamentals such as
the Content-Length header, leaving all of this up to the caller.

The UEFI HTTP Boot driver will install an EFI_LOAD_FILE_PROTOCOL
instance on the loaded image's device handle.  This looks promising at
first since it provides the LoadFile() API call which is specified to
accept an arbitrary filename parameter.  However, experimentation (and
inspection of the code in EDK2) reveals a multitude of problems that
prevent this from being usable.  Calling LoadFile() will idiotically
restart the entire DHCP process (and potentially pop up a UI requiring
input from the user for e.g. a wireless network password).  The
filename provided to LoadFile() will be ignored.  Any downloaded file
will be rejected unless it happens to match one of the limited set of
types expected by the UEFI HTTP Boot driver.  The list of design
failures and conceptual mismatches is fairly impressive.

Choose to bypass every possible aspect of UEFI HTTP support, and
instead use our own HTTP client and network stack to download the
autoexec.ipxe script over a temporary MNP network device.  Since this
approach works for TFTP as well as HTTP, drop the direct use of
EFI_PXE_BASE_CODE_PROTOCOL.  For consistency and simplicity, also drop
the direct use of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and rely upon our
existing support to access local files via "file:" URIs.

This approach results in console output during the "iPXE initialising
devices...ok" message that appears while startup is in progress.
Remove the trailing "ok" so that this intermediate output appears at a
sensible location on the screen.  The welcome banner that will be
printed immediately afterwards provides an indication that startup has
completed successfully even absent the explicit "ok".

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-03 00:03:49 +01:00
b940d54235 [cachedhcp] Allow cached DHCPACK to apply to temporary network devices
Retain a reference to the cached DHCPACK until the late startup phase,
and allow it to be recycled for reuse.  This allows the cached DHCPACK
to be used for a temporary MNP network device and then subsequently
reused for the corresponding real network device.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-04-02 22:59:50 +01:00
b66f6025fa [efi] Add the ability to create a temporary MNP network device
An MNP network device may be temporarily and non-destructively
installed on top of an existing UEFI network stack without having to
disconnect existing drivers.

Add the ability to create such a temporary network device.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-29 14:46:13 +00:00
b52b4a46d9 [efi] Allow for allocating EFI devices from arbitrary handles
Split out the code that allocates our internal struct efi_device
representations, to allow for the creation of temporary MNP devices in
order to download the autoexec.ipxe script.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-29 14:46:13 +00:00
764e34f15a [http] Add error table entry for HTTP 404 Not Found error
Add an abbreviated "Not found" error message for an HTTP 404 status
code, so that any automatic attempt to download a non-existent
autoexec.ipxe script produces only a minimal error message.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-29 14:46:13 +00:00
afae881782 [tftp] Add error table entry for TFTP "file not found" error code
Add an abbreviated "Not found" error message for a TFTP "file not
found" error code, so that any automatic attempt to download a
non-existent autoexec.ipxe script produces only a minimal error
message.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-29 14:46:13 +00:00
43deab89c3 [efi] Add error table entry for local filesystem EFI_NOT_FOUND error
Add an abbreviated "Not found" error message for an EFI_NOT_FOUND
error encountered when attempting to open a file on a local
filesystem, so that any automatic attempt to download a non-existent
autoexec.ipxe script produces only a minimal error message.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-29 14:46:13 +00:00
19f39bc07a [efi] Report local file errors during download, rather than on opening
iPXE is designed around fully asynchronous I/O, including asynchronous
connection opening.  Almost all errors are therefore necessarily
reported as occurring during an in-progress download, rather than
occurring at the time that the URI is opened.

Local file access is currently an exception to this: errors such as
nonexistent files will be encountered while opening the URI.  This
results in mildly unexpected error messages of the form "Could not
start download", rather than the usual pattern of showing the URI, the
initial progress dots, and then the error message.

Fix this inconsistency by deferring the local filesystem access until
the local file download process is running.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-29 14:41:06 +00:00
f39b48d5f8 [image] Allow opaque URI component to provide image name
Some URI schemes allow for a path name to be specified via the opaque
component of the URI (e.g. "file:/script.ipxe" to specify a path on
the filesystem from which iPXE itself was loaded).  Files loaded from
such paths will currently fail to be assigned an appropriate name,
since only the path component of the URI will be used to construct a
default image name.

Fix by falling back to attempt deriving an image name from the opaque
component of a URI, if no path component is specified.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-03-29 14:12:10 +00:00
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
4ed7a5718f [build] Reduce scope of wildcard .gitignore rules
Ensure that .gitignore rules do not cover any files that do exist
within the repository.

Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-09 12:39:48 +00:00
fa62213231 [smbios] Support scanning for the 64-bit SMBIOS3 entry point
Support scanning for the 64-bit SMBIOS3 entry point in addition to the
32-bit SMBIOS2 entry point.

Prefer use of the 32-bit entry point if present, since this is
guaranteed to be within accessible memory.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-12-29 19:38:47 +00:00
119c415ee4 [intel] Add PCI ID for I219-LM (23)
Successfully tested on FUJITSU LIFEBOOK U7413.

Signed-off-by: Christian Helmuth <christian.helmuth@genode-labs.com>
2023-12-21 13:53:24 +01:00
9e92c39894 [efi] Add potentially missing relocation types
Add definitions for relocation types that may be missing on older
versions of the host system's elf.h.

This mirrors wimboot commit 47f6298 ("[efi] Add potentially missing
relocation types").

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-12-21 12:29:03 +00:00
3fc1b407d2 [efi] Fix Coverity warning about unintended sign extension
The result of multiplying a uint16_t by another uint16_t will be a
signed int.  Comparing this against a size_t will perform an unwanted
sign extension.

Fix by explicitly casting e_phnum to an unsigned int, thereby matching
the data type used for the loop index variable (and avoiding the
unwanted sign extension).

This mirrors wimboot commit 15f6162 ("[efi] Fix Coverity warning about
unintended sign extension").

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-12-19 16:56:34 +00:00
0958e01463 [efi] Add relocation types generated by clang
Add additional PC-relative relocation types that may be encountered
when converting binaries compiled with clang.

This mirrors the relevant elf2efi portions of wimboot commit 7910830
("[build] Support building with the clang compiler").

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-12-19 16:29:42 +00:00
337880deaa [build] Use SOURCE_DATE_EPOCH for FAT serial number if it exists
Reported-by: Bernhard M. Wiedemann <bwiedemann@suse.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-12-19 14:39:36 +00:00
f22879ca99 [efi] Allow compiling elf2efi with clang
The clang compiler does not (and apparently will not ever) allow for
variable-length arrays within structs.

Work around this limitation by using a fixed-length array to hold the
PDB filename in the debug section.

This mirrors wimboot commit f52c3ff ("[efi] Allow compiling elf2efi
with clang").

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-12-05 15:49:17 +00:00
98dd25a3bb [efi] Avoid modifying PE/COFF debug filename
The function efi_pecoff_debug_name() (called by efi_handle_name()) is
used to extract a filename from the debug data directory entry located
within a PE/COFF image.  The name is copied into a temporary static
buffer to allow for modifications, but the code currently erroneously
modifies the original name within the loaded PE/COFF image.

Fix by performing the modification on the copy in the temporary
buffer, as originally intended.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-29 12:49:06 +00:00
a147245f1a [efi] Extend PE header size to cover space up to first section
Hybrid bzImage and UEFI binaries (such as wimboot) may place sections
at explicit offsets within the PE file, as described in commit b30a098
("[efi] Use load memory address as file offset for hybrid binaries").
This can leave a gap after the PE headers that is not covered by any
section.  It is not entirely clear whether or not such gaps are
permitted in binaries submitted for Secure Boot signing.

To minimise potential problems, extend the PE header size to cover any
space before the first explicitly placed section.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-27 12:42:58 +00:00
c3dd3168c9 [efi] Fix dependency list construction in EDK2 header import script
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-24 22:27:51 +00:00
b829b1750d [efi] Maximise image base address
iPXE images are linked with a starting virtual address of zero.  Other
images (such as wimboot) may use a non-zero starting virtual address.

There is no direct equivalent of the PE ImageBase address field within
ELF object files.  Choose to use the highest possible address that
accommodates all sections and the PE header itself, since this will
minimise the memory allocated to hold the loaded image.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-24 21:57:39 +00:00
03ff1bb99a [efi] Do not assume canonical PE section ordering
The BaseOfCode (and, in PE32, BaseOfData) fields imply an assumption
that binaries are laid out as code followed by initialised data
followed by uninitialised data.  This assumption may not be valid for
complex binaries such as wimboot.

Remove this implicit assumption, and use arguably justifiable values
for the assorted summary start and size fields within the PE headers.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-24 16:45:11 +00:00
18582a05fc [efi] Treat 16-bit sections as hidden in hybrid binaries
Hybrid bzImage and UEFI binaries (such as wimboot) may include 16-bit
sections such as .bss16 that do not need to consume an entry in the PE
section list.  Treat any such sections as hidden.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-24 12:16:49 +00:00
6714b20ea2 [efi] Place PE debug information in a hidden section
The PE debug information generated by elf2efi is used only to hold the
image filename, and the debug information is located via the relevant
data directory entry rather than via the section table.

Make the .debug section a hidden section in order to save one entry in
the PE section list.  Choose to place the debug information in the
unused space at the end of the PE headers, since it no longer needs to
satisfy the general section alignment constraints.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-23 15:43:21 +00:00
b37d89db90 [efi] Fix recorded overall size of headers in NT optional header
Commit 1e4c378 ("[efi] Shrink size of data directory in PE header")
reduced the number of entries used in the data directory and reduced
the recorded size of the NT "optional" header, but did not also adjust
the recorded overall size of the PE headers, resulting in unused space
between the PE headers and the first section.

Fix by reducing the initial recorded size of the PE headers by the
size of the omitted data directory entries.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-23 15:07:46 +00:00
cc858acd32 [efi] Write out PE header only after writing sections
Hybrid bzImage and UEFI binaries (such as wimboot) include a bzImage
header within a section starting at offset zero, with the PE header
effectively occupying unused space within this section.

Allow for this by treating a section placed at offset zero as hidden,
and by deferring the writing of the PE header until after the output
sections have been written.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-23 13:37:47 +00:00
b30a0987e2 [efi] Use load memory address as file offset for hybrid binaries
Hybrid bzImage and UEFI binaries (such as wimboot) may be loaded as a
single contiguous blob without reference to the PE headers, and the
placement of sections within the PE file must therefore be known at
link time.

Use the load memory address (extracted from the ELF program headers)
to determine the physical placement of the section within the PE file
when generating a hybrid binary.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-23 13:37:47 +00:00
3d8a614657 [efi] Mark PE images as large address aware
The images generated by elf2efi can be loaded anywhere in the address
space, and are not limited to the low 2GB.

Indicate this by setting the "large address aware" flag within the PE
header, for compatibility with EFI images generated by the EDK2 build
process.  (The EDK2 PE loader does not ever check this flag, and it is
unlikely that any other EFI PE loader ever does so, but we may as well
report it accurately.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-23 13:29:34 +00:00
a9e89787d0 [efi] Set NXCOMPAT bit in PE header
Indicate that the binary is compatible with W^X protections by setting
the NXCOMPAT bit in the DllCharacteristics field of the PE header.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-23 13:21:42 +00:00
678a60f61d [efi] Treat writable sections as data sections
Hybrid bzImage and UEFI binaries (such as wimboot) may include 16-bit
executable code that is opaque data from the perspective of a UEFI PE
binary, as described in wimboot commit fe456ca ("[efi] Use separate
.text and .data PE sections").

The ELF section will be marked as containing both executable code and
writable data.  Choose to treat such a section as a data section
rather than a code section, since that matches the expected semantics
for ELF files that we expect to process.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-23 13:20:21 +00:00
8c8ead2530 [efi] Update to current EDK2 headers
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-22 23:42:22 +00:00
77b07ea4fd [cloud] Add utility script to read iPXE output from INT13CON partition
Some AWS instance types still do not support serial console output or
screenshots.  For these instance types, the only viable way to extract
debugging information is to use the INT13 console (which is already
enabled via CONFIG=cloud for all AWS images).

Obtaining the INT13 console output can be very cumbersome, since there
is no direct way to read from an AWS volume.  The simplest current
approach is to stop the instance under test, detach its root volume,
and reattach the volume to a Linux instance in the same region.

Add a utility script aws-int13con to retrieve the INT13 console output
by creating a temporary snapshot, reading the first block from the
snapshot, and extracting the INT13 console partition content.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-07 18:05:45 +00:00
d8f9c221ed [cloud] Add ability to overwrite existing AMI images
AMI names must be unique within a region.  Add a --overwrite option
that allows an existing AMI of the same name to be deregistered (and
its underlying snapshot deleted).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-07 15:57:13 +00:00
595b1796f6 [eapol] Limit number of EAPoL-Start packets transmitted per attempt
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-07 13:51:16 +00:00
1bd01b761f [eapol] Delay EAPoL-Start while waiting for EAP to complete
EAP exchanges may take a long time to reach a final status, especially
when relying upon MAC Authentication Bypass (MAB).  Our current
behaviour of sending EAPoL-Start every few seconds until a final
status is obtained can prevent these exchanges from ever completing.

Fix by redefining the EAP supplicant state to allow EAPoL-Start to be
suppressed: either temporarily (while waiting for a full EAP exchange
to complete, in which case we need to eventually resend EAPoL-Start if
the final Success or Failure packet is lost), or permanently (while
waiting for the potentially very long MAC Authentication Bypass
timeout period).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-07 13:31:20 +00:00
5524bb9832 [pci] Require discovery of a PCI device when determining usable PCI APIs
The PCI cloud API (PCIAPI_CLOUD) currently selects the first PCI API
that successfully discovers a PCI device address range.  The ECAM API
may discover an address range but subsequently be unable to map the
configuration space region, which would result in the selected PCI API
being unusable.

Fix by instead selecting the first PCI API that can be successfully
used to discover a PCI device.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-02 16:11:38 +00:00
36e1a559a2 [pci] Check that ECAM configuration space is within reachable memory
Some machines (observed with an AWS EC2 m7a.large instance) will place
the ECAM configuration space window above 4GB, thereby making it
unreachable from non-paged 32-bit code.  This problem is currently
ignored by iPXE, since the address is silently truncated in the call
to ioremap().  (Note that other uses of ioremap() are not affected
since the PCI core will already have checked for unreachable 64-bit
BARs when retrieving the physical address to be mapped.)

Fix by adding an explicit check that the region to be mapped starts
within the reachable memory address space.  (Assume that no machines
will be sufficiently peverse to provide a region that straddles the
4GB boundary.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-02 15:38:08 +00:00
1f3a37e342 [pci] Cache ECAM mapping errors
When an error occurs during ECAM configuration space mapping, preserve
the error within the existing cached mapping (instead of invalidating
the cached mapping) in order to avoid flooding the debug log with
repeated identical mapping errors.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-02 15:20:27 +00:00
74ec00a9f3 [pci] Handle non-zero starting bus in ECAM allocations
The base address provided in the PCI ECAM allocation within the ACPI
MCFG table is the base address for the segment as a whole, not for the
starting bus within that allocation.  On machines that provide ECAM
allocations with a non-zero starting bus number (observed with an AWS
EC2 m7a.large instance), this will result in iPXE accessing the wrong
memory addresses within the ECAM region.

Fix by adding the appropriate starting bus offset to the base address.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-02 15:05:15 +00:00
f883203132 [pci] Force completion of ECAM configuration space writes
The PCIe specification requires that "processor and host bridge
implementations must ensure that a method exists for the software to
determine when the write using the ECAM is completed by the completer"
but does not specify any particular method to be used.  Some platforms
might treat writes to the ECAM region as non-posted, others might
require reading back from a dedicated (and implementation-specific)
completion register to determine when the configuration space write
has completed.

Since PCI configuration space writes will never be used for any
performance-critical datapath operations (on any sane hardware), a
simple and platform-independent solution is to always read back from
the written register in order to guarantee that the write must have
completed.  This is safe to do, since the PCIe specification defines a
limited set of configuration register types, none of which have read
side effects.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-11-01 22:32:21 +00:00
115707c0ed [iphone] Add missing va_start()/va_end() around reused argument list
The ipair_tx() function uses a va_list twice (first to calculate the
formatted string length before allocation, then to construct the
string in the allocated buffer) but is missing the va_start() and
va_end() around the second usage.  This is undefined behaviour that
happens to work on some build platforms.

Fix by adding the missing va_start() and va_end() around the second
usage of the variadic argument list.

Reported-by: Andreas Hammarskjöld <andreas@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-10-24 11:43:56 +01:00
ff0f860483 [libc] Use wall clock time as seed for the (non-cryptographic) RNG
We currently use the number of timer ticks since power-on as a seed
for the non-cryptographic RNG implemented by random().  Since iPXE is
often executed directly after power-on, and since the timer tick
resolution is generally low, this can often result in identical seed
values being used on each cold boot attempt.

As of commit 41f786c ("[settings] Add "unixtime" builtin setting to
expose the current time"), the current wall-clock time is always
available within the default build of iPXE.  Use this time instead, to
introduce variability between cold boot attempts on the same host.
(Note that variability between different hosts is obtained by using
the MAC address as an additional seed value.)

This has no effect on the separate DRBG used by cryptographic code.

Suggested-by: Heiko <heik0@xs4all.nl>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-10-06 12:50:43 +01:00
8b14652e50 [eapol] Send EAPoL-Start packets to trigger EAP authentication
We have no way to force a link-layer restart in iPXE, and therefore no
way to explicitly trigger a restart of EAP authentication.  If an iPXE
script has performed some action that requires such a restart
(e.g. registering a device such that the port VLAN assignment will be
changed), then the only means currently available to effect the
restart is to reboot the whole system.  If iPXE is taking over a
physical link already used by a preceding bootloader, then even a
reboot may not work.

In the EAP model, the supplicant is a pure responder and never
initiates transmissions.  EAPoL extends this to include an EAPoL-Start
packet type that may be sent by the supplicant to (re)trigger EAP.

Add support for sending EAPoL-Start packets at two-second intervals on
links that are open and have reached physical link-up, but for which
EAP has not yet completed.  This allows "ifclose ; ifopen" to be used
to restart the EAP process.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-19 23:16:58 +01:00
56cc61a168 [eap] Define a supplicant model for EAP and EAPoL
Extend the EAP model to include a record of whether or not EAP
authentication has completed (successfully or otherwise), and to
provide a method for transmitting EAP responses.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-18 12:07:28 +01:00
cac3a584dc [fcoe] Use driver-private data to hold FCoE port structure
Simplify the FCoE code by using driver-private data to hold the FCoE
port for each network device, instead of using a separate allocation.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-14 13:25:19 +01:00
8cbf248198 [vmware] Use driver-private data to hold GuestInfo settings block
Simplify the per-netdevice GuestInfo settings code by using
driver-private data to hold the settings block, instead of using a
separate allocation.

The settings block (if existent) will be automatically unregistered
when the parent network device settings block is unregistered, and no
longer needs to be separately freed.  The guestinfo_net_remove()
function may therefore be omitted completely.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-14 12:55:56 +01:00
8b1d34badf [ipv6] Use driver-private data to hold link-local IPv6 settings block
Simplify the IPv6 link-local settings code by using driver-private
data to hold the settings block, instead of using a separate
allocation.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-13 23:02:54 +01:00
cc1e27e525 [lldp] Use driver-private data to hold LLDP settings block
Simplify the LLDP code by using driver-private data to hold the LLDP
settings block, instead of using a separate allocation.  This avoids
the need to maintain a list of LLDP settings blocks (since the LLDP
settings block pointer can always be obtained using netdev_priv()) and
obviates several failure paths.

Any recorded LLDP data is now freed when the network device is
unregistered, since there is no longer a dedicated reference counter
for the LLDP settings block.  To minimise surprise, we also now
explicitly unregister the settings block.  This is not strictly
necessary (since the block will be automatically unregistered when the
parent network device settings block is unregistered), but it
maintains symmetry between lldp_probe() and lldp_remove().

The overall reduction in the size of the LLDP code is around 15%.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-13 23:02:47 +01:00
ae4e85bde9 [netdevice] Allocate private data for each network upper-layer driver
Allow network upper-layer drivers (such as LLDP, which attaches to
each network device in order to provide a corresponding LLDP settings
block) to specify a size for private data, which will be allocated as
part of the network device structure (as with the existing private
data allocated for the underlying device driver).

This will allow network upper-layer drivers to be simplified by
omitting memory allocation and freeing code.  If the upper-layer
driver requires a reference counter (e.g. for interface
initialisation), then it may use the network device's existing
reference counter, since this is now the reference counter for the
containing block of memory.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-13 20:23:46 +01:00
eeb7cd56e5 [netdevice] Remove netdev_priv() helper function
Some network device drivers use the trivial netdev_priv() helper
function while others use the netdev->priv pointer directly.

Standardise on direct use of netdev->priv, in order to free up the
function name netdev_priv() for reuse.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-13 16:29:48 +01:00
0aa2e4ec96 [librm] Use explicit operand size when pushing a label address
We currently use "push $1f" within inline assembly to push the address
of the real-mode code fragment, relying on the assembler to treat this
as "pushl" for 32-bit code or "pushq" for 64-bit code.

As of binutils commit 5cc0077 ("x86: further adjust extend-to-32bit-
address conditions"), first included in binutils-2.41, this implicit
operand size is no longer calculated as expected and 64-bit builds
will fail with

  Error: operand size mismatch for `push'

Fix by adding an explicit operand size to the "push" instruction.

Originally-fixed-by: Justin Cano <jstncno@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-05 12:47:42 +01:00
9e99a55b31 [virtio] Fix implementation of vpm_ioread32()
The current implementation of vpm_ioread32() erroneously reads only 16
bits of data, which fails when used with the (stricter) virtio device
emulation in VirtualBox.

Fix by using the correct readl()/inl() I/O wrappers.

Reworded-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-08-22 13:45:44 +01:00
c1834f323f [dhcp] Request NTP server option
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-07-19 11:13:52 +01:00
d5c08f78bd [ntp] Define NTP server setting
Define the IPv4 NTP server setting to simplify the use of a
DHCP-provided NTP server in scripts, using e.g.

  #!ipxe
  dhcp
  ntp ${ntp}

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-07-19 11:10:15 +01:00
388 changed files with 24574 additions and 6817 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

@ -46,11 +46,19 @@ def create_snapshot(region, description, image):
return snapshot_id
def import_image(region, name, architecture, image, public):
def import_image(region, name, architecture, image, public, overwrite):
"""Import an AMI image"""
client = boto3.client('ec2', region_name=region)
resource = boto3.resource('ec2', region_name=region)
description = '%s (%s)' % (name, architecture)
images = client.describe_images(Filters=[{'Name': 'name',
'Values': [description]}])
if overwrite and images['Images']:
images = images['Images'][0]
image_id = images['ImageId']
snapshot_id = images['BlockDeviceMappings'][0]['Ebs']['SnapshotId']
resource.Image(image_id).deregister()
resource.Snapshot(snapshot_id).delete()
snapshot_id = create_snapshot(region=region, description=description,
image=image)
client.get_waiter('snapshot_completed').wait(SnapshotIds=[snapshot_id])
@ -88,6 +96,8 @@ parser.add_argument('--name', '-n',
help="Image name")
parser.add_argument('--public', '-p', action='store_true',
help="Make image public")
parser.add_argument('--overwrite', action='store_true',
help="Overwrite any existing image with same name")
parser.add_argument('--region', '-r', action='append',
help="AWS region(s)")
parser.add_argument('--wiki', '-w', metavar='FILE',
@ -115,7 +125,8 @@ with ThreadPoolExecutor(max_workers=len(imports)) as executor:
name=args.name,
architecture=architectures[image],
image=image,
public=args.public): (region, image)
public=args.public,
overwrite=args.overwrite): (region, image)
for region, image in imports}
results = {futures[future]: future.result()
for future in as_completed(futures)}

68
contrib/cloud/aws-int13con Executable file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import argparse
import boto3
BLOCKSIZE = 512 * 1024
IPXELOG_OFFSET = 16 * 1024
IPXELOG_MAGIC = b'iPXE LOG'
def create_snapshot(region, instance_id):
"""Create root volume snapshot"""
client = boto3.client('ec2', region_name=region)
resource = boto3.resource('ec2', region_name=region)
instance = resource.Instance(instance_id)
volumes = list(instance.volumes.all())
snapshot = volumes[0].create_snapshot()
snapshot.wait_until_completed()
return snapshot.id
def get_snapshot_block(region, snapshot_id, index):
"""Get block content from snapshot"""
client = boto3.client('ebs', region_name=region)
blocks = client.list_snapshot_blocks(SnapshotId=snapshot_id,
StartingBlockIndex=index)
token = blocks['Blocks'][0]['BlockToken']
block = client.get_snapshot_block(SnapshotId=snapshot_id,
BlockIndex=index,
BlockToken=token)
return block['BlockData'].read()
def get_block0_content(region, instance_id):
"""Get content of root volume block zero from instance"""
client = boto3.client('ec2', region_name=region)
resource = boto3.resource('ec2', region_name=region)
snapshot_id = create_snapshot(region, instance_id)
block = get_snapshot_block(region, snapshot_id, 0)
resource.Snapshot(snapshot_id).delete()
return block
def get_int13con_output(region, instance_id):
"""Get INT13 console output"""
block = get_block0_content(region, instance_id)
logpart = block[IPXELOG_OFFSET:]
magic = logpart[:len(IPXELOG_MAGIC)]
if magic != IPXELOG_MAGIC:
raise ValueError("Invalid log magic signature")
log = logpart[len(IPXELOG_MAGIC):].split(b'\0')[0]
return log.decode()
# Parse command-line arguments
parser = argparse.ArgumentParser(description="Get AWS INT13 console output")
parser.add_argument('--region', '-r', help="AWS region")
parser.add_argument('id', help="Instance ID")
args = parser.parse_args()
# Get console output from INT13CON partition
output = get_int13con_output(args.region, args.id)
# Print console output
print(output)

167
contrib/cloud/gce-import Executable file
View File

@ -0,0 +1,167 @@
#!/usr/bin/env python3
import argparse
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import date
import io
import subprocess
import tarfile
from uuid import uuid4
from google.cloud import compute
from google.cloud import exceptions
from google.cloud import storage
IPXE_STORAGE_PREFIX = 'ipxe-upload-temp-'
FEATURE_GVNIC = compute.GuestOsFeature(type_="GVNIC")
FEATURE_IDPF = compute.GuestOsFeature(type_="IDPF")
FEATURE_UEFI = compute.GuestOsFeature(type_="UEFI_COMPATIBLE")
POLICY_PUBLIC = compute.Policy(bindings=[{
"role": "roles/compute.imageUser",
"members": ["allAuthenticatedUsers"],
}])
def delete_temp_bucket(bucket):
"""Remove temporary bucket"""
assert bucket.name.startswith(IPXE_STORAGE_PREFIX)
for blob in bucket.list_blobs(prefix=IPXE_STORAGE_PREFIX):
assert blob.name.startswith(IPXE_STORAGE_PREFIX)
blob.delete()
if not list(bucket.list_blobs()):
bucket.delete()
def create_temp_bucket(location):
"""Create temporary bucket (and remove any stale temporary buckets)"""
client = storage.Client()
for bucket in client.list_buckets(prefix=IPXE_STORAGE_PREFIX):
delete_temp_bucket(bucket)
name = '%s%s' % (IPXE_STORAGE_PREFIX, uuid4())
return client.create_bucket(name, location=location)
def create_tarball(image):
"""Create raw disk image tarball"""
tarball = io.BytesIO()
with tarfile.open(fileobj=tarball, mode='w:gz',
format=tarfile.GNU_FORMAT) as tar:
tar.add(image, arcname='disk.raw')
tarball.seek(0)
return tarball
def upload_blob(bucket, image):
"""Upload raw disk image blob"""
blob = bucket.blob('%s%s.tar.gz' % (IPXE_STORAGE_PREFIX, uuid4()))
tarball = create_tarball(image)
blob.upload_from_file(tarball)
return blob
def detect_uefi(image):
"""Identify UEFI CPU architecture(s)"""
mdir = subprocess.run(['mdir', '-b', '-i', image, '::/EFI/BOOT'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=False)
mapping = {
b'BOOTX64.EFI': 'x86_64',
b'BOOTAA64.EFI': 'arm64',
}
uefi = [
arch
for filename, arch in mapping.items()
if filename in mdir.stdout
]
return uefi
def image_architecture(uefi):
"""Get image architecture"""
return uefi[0] if len(uefi) == 1 else None if uefi else 'x86_64'
def image_features(uefi):
"""Get image feature list"""
features = [FEATURE_GVNIC, FEATURE_IDPF]
if uefi:
features.append(FEATURE_UEFI)
return features
def image_name(base, uefi):
"""Calculate image name or family name"""
suffix = ('-uefi-%s' % uefi[0].replace('_', '-') if len(uefi) == 1 else
'-uefi-multi' if uefi else '')
return '%s%s' % (base, suffix)
def create_image(project, basename, basefamily, overwrite, public, bucket,
image):
"""Create image"""
client = compute.ImagesClient()
uefi = detect_uefi(image)
architecture = image_architecture(uefi)
features = image_features(uefi)
name = image_name(basename, uefi)
family = image_name(basefamily, uefi)
if overwrite:
try:
client.delete(project=project, image=name).result()
except exceptions.NotFound:
pass
blob = upload_blob(bucket, image)
disk = compute.RawDisk(source=blob.public_url)
image = compute.Image(name=name, family=family, architecture=architecture,
guest_os_features=features, raw_disk=disk)
client.insert(project=project, image_resource=image).result()
if public:
request = compute.GlobalSetPolicyRequest(policy=POLICY_PUBLIC)
client.set_iam_policy(project=project, resource=name,
global_set_policy_request_resource=request)
image = client.get(project=project, image=name)
return image
# Parse command-line arguments
#
parser = argparse.ArgumentParser(description="Import Google Cloud image")
parser.add_argument('--name', '-n',
help="Base image name")
parser.add_argument('--family', '-f',
help="Base family name")
parser.add_argument('--public', '-p', action='store_true',
help="Make image public")
parser.add_argument('--overwrite', action='store_true',
help="Overwrite any existing image with same name")
parser.add_argument('--project', '-j', default="ipxe-images",
help="Google Cloud project")
parser.add_argument('--location', '-l',
help="Google Cloud Storage initial location")
parser.add_argument('image', nargs='+', help="iPXE disk image")
args = parser.parse_args()
# Use default family name if none specified
if not args.family:
args.family = 'ipxe'
# Use default name if none specified
if not args.name:
args.name = '%s-%s' % (args.family, date.today().strftime('%Y%m%d'))
# Create temporary upload bucket
bucket = create_temp_bucket(args.location)
# Use one thread per image to maximise parallelism
with ThreadPoolExecutor(max_workers=len(args.image)) as executor:
futures = {executor.submit(create_image,
project=args.project,
basename=args.name,
basefamily=args.family,
overwrite=args.overwrite,
public=args.public,
bucket=bucket,
image=image): image
for image in args.image}
results = {futures[future]: future.result()
for future in as_completed(futures)}
# Delete temporary upload bucket
delete_temp_bucket(bucket)
# Show created images
for image in args.image:
result = results[image]
print("%s (%s) %s" % (result.name, result.family, result.status))

146
contrib/cloud/gce-int13con Executable file
View File

@ -0,0 +1,146 @@
#!/usr/bin/env python3
import argparse
import textwrap
import time
from uuid import uuid4
from google.cloud import compute
IPXE_LOG_PREFIX = 'ipxe-log-temp-'
IPXE_LOG_MAGIC = 'iPXE LOG'
IPXE_LOG_END = '----- END OF iPXE LOG -----'
def get_log_disk(instances, project, zone, name):
"""Get log disk source URL"""
instance = instances.get(project=project, zone=zone, instance=name)
disk = next(x for x in instance.disks if x.boot)
return disk.source
def delete_temp_snapshot(snapshots, project, name):
"""Delete temporary snapshot"""
assert name.startswith(IPXE_LOG_PREFIX)
snapshots.delete(project=project, snapshot=name)
def delete_temp_snapshots(snapshots, project):
"""Delete all old temporary snapshots"""
filter = "name eq %s.+" % IPXE_LOG_PREFIX
request = compute.ListSnapshotsRequest(project=project, filter=filter)
for snapshot in snapshots.list(request=request):
delete_temp_snapshot(snapshots, project, snapshot.name)
def create_temp_snapshot(snapshots, project, source):
"""Create temporary snapshot"""
name = '%s%s' % (IPXE_LOG_PREFIX, uuid4())
snapshot = compute.Snapshot(name=name, source_disk=source)
snapshots.insert(project=project, snapshot_resource=snapshot).result()
return name
def delete_temp_instance(instances, project, zone, name):
"""Delete log dumper temporary instance"""
assert name.startswith(IPXE_LOG_PREFIX)
instances.delete(project=project, zone=zone, instance=name)
def delete_temp_instances(instances, project, zone):
"""Delete all old log dumper temporary instances"""
filter = "name eq %s.+" % IPXE_LOG_PREFIX
request = compute.ListInstancesRequest(project=project, zone=zone,
filter=filter)
for instance in instances.list(request=request):
delete_temp_instance(instances, project, zone, instance.name)
def create_temp_instance(instances, project, zone, family, image, machine,
snapshot):
"""Create log dumper temporary instance"""
image = "projects/%s/global/images/family/%s" % (family, image)
machine_type = "zones/%s/machineTypes/%s" % (zone, machine)
logsource = "global/snapshots/%s" % snapshot
bootparams = compute.AttachedDiskInitializeParams(source_image=image)
bootdisk = compute.AttachedDisk(boot=True, auto_delete=True,
initialize_params=bootparams)
logparams = compute.AttachedDiskInitializeParams(source_snapshot=logsource)
logdisk = compute.AttachedDisk(boot=False, auto_delete=True,
initialize_params=logparams,
device_name="ipxelog")
nic = compute.NetworkInterface()
name = '%s%s' % (IPXE_LOG_PREFIX, uuid4())
script = textwrap.dedent(f"""
#!/bin/sh
tr -d '\\000' < /dev/disk/by-id/google-ipxelog-part3 > /dev/ttyS3
echo "{IPXE_LOG_END}" > /dev/ttyS3
""").strip()
items = compute.Items(key="startup-script", value=script)
metadata = compute.Metadata(items=[items])
instance = compute.Instance(name=name, machine_type=machine_type,
network_interfaces=[nic], metadata=metadata,
disks=[bootdisk, logdisk])
instances.insert(project=project, zone=zone,
instance_resource=instance).result()
return name
def get_log_output(instances, project, zone, name):
"""Get iPXE log output"""
request = compute.GetSerialPortOutputInstanceRequest(project=project,
zone=zone, port=4,
instance=name)
while True:
log = instances.get_serial_port_output(request=request).contents.strip()
if log.endswith(IPXE_LOG_END):
if log.startswith(IPXE_LOG_MAGIC):
return log[len(IPXE_LOG_MAGIC):-len(IPXE_LOG_END)]
else:
return log[:-len(IPXE_LOG_END)]
time.sleep(1)
# Parse command-line arguments
#
parser = argparse.ArgumentParser(description="Import Google Cloud image")
parser.add_argument('--project', '-j', default="ipxe-images",
help="Google Cloud project")
parser.add_argument('--zone', '-z', required=True,
help="Google Cloud zone")
parser.add_argument('--family', '-f', default="debian-cloud",
help="Helper OS image family")
parser.add_argument('--image', '-i', default="debian-12",
help="Helper OS image")
parser.add_argument('--machine', '-m', default="e2-micro",
help="Helper machine type")
parser.add_argument('instance', help="Instance name")
args = parser.parse_args()
# Construct client objects
#
instances = compute.InstancesClient()
snapshots = compute.SnapshotsClient()
# Clean up old temporary objects
#
delete_temp_instances(instances, project=args.project, zone=args.zone)
delete_temp_snapshots(snapshots, project=args.project)
# Create log disk snapshot
#
logdisk = get_log_disk(instances, project=args.project, zone=args.zone,
name=args.instance)
logsnap = create_temp_snapshot(snapshots, project=args.project, source=logdisk)
# Create log dumper instance
#
dumper = create_temp_instance(instances, project=args.project, zone=args.zone,
family=args.family, image=args.image,
machine=args.machine, snapshot=logsnap)
# Wait for log output
#
output = get_log_output(instances, project=args.project, zone=args.zone,
name=dumper)
# Print log output
#
print(output)
# Clean up
#
delete_temp_instance(instances, project=args.project, zone=args.zone,
name=dumper)
delete_temp_snapshot(snapshots, project=args.project, name=logsnap)

80
contrib/crypto/cmsdetach Executable file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""Detach CMS encrypted data.
Detach encrypted data from a CMS envelopedData or authEnvelopedData
message into a separate file.
"""
import argparse
import asn1
# Parse command-line arguments
#
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("-d", "--data", metavar="FILE",
help="Write detached data (without envelope) to FILE")
parser.add_argument("-e", "--envelope", metavar="FILE",
help="Write envelope (without data) to FILE")
parser.add_argument("-o", "--overwrite", action="store_true",
help="Overwrite output files")
parser.add_argument("file", help="Input envelope file")
args = parser.parse_args()
if args.data is None and args.envelope is None:
parser.error("at least one of --data and --envelope is required")
outmode = "wb" if args.overwrite else "xb"
# Create decoder
#
decoder = asn1.Decoder()
with open(args.file, mode="rb") as fh:
decoder.start(fh.read())
# Create encoder
#
encoder = asn1.Encoder()
encoder.start()
# Detach encrypted data
#
data = None
datastack = [
asn1.Numbers.Sequence, 0, asn1.Numbers.Sequence, asn1.Numbers.Sequence
]
stack = []
while stack or not decoder.eof():
if decoder.eof():
encoder.leave()
decoder.leave()
stack.pop()
else:
tag = decoder.peek()
if tag.typ == asn1.Types.Constructed:
encoder.enter(nr=tag.nr, cls=tag.cls)
decoder.enter()
stack.append(tag.nr)
else:
(tag, value) = decoder.read()
if stack == datastack and tag.nr == 0:
data = value
else:
encoder.write(value, nr=tag.nr, cls=tag.cls)
envelope = encoder.output()
if data is None:
parser.error("Input file does not contain any encrypted data")
# Write envelope (without data), if applicable
#
if args.envelope:
with open(args.envelope, mode=outmode) as fh:
fh.write(envelope)
# Write data (without envelope), if applicable
#
if args.data:
with open(args.data, mode=outmode) as fh:
fh.write(data)

View File

@ -1,62 +0,0 @@
ROM-o-matic web interface for building iPXE ROMs
------------------------------------------------
This web application generates iPXE images and sends them to a web
browser.
Available as part of the iPXE source code distribution, which can be
downlaoded from http://etherboot.org/
Author: Marty Connor <mdc@etherboot.org>
License: GPLv2
Support: http://etherboot.org/mailman/listinfo/ipxe
Please send support questions to the iPXE mailing list
System Requirements
-------------------
- Apache web server
- PHP 4+
- Tools required to build iPXE installed on the server
- gcc, mtools, syslinux, perl, etc.
Setup
-----
As distributed, it is expected that the rom-o-matic source code
directory is in the contrib directory of a iPXE source distribution.
The easiest way to do this is to simply put a iPXE source distribution
in a web server accessible directory.
If this is not the case, you will need to either edit the file
"globals.php"
or create a file called
"local-config.php"
containing the following lines:
<?php
$src_dir = "../../src";
?>
Then change the line beginning "$src_dir = " to the path of your iPXE
source code tree.
To make build times shorter, before you run rom-o-matic for the first time
you should cd to the ipxe "src" directory and enter the following
commands:
$ make
$ make bin/NIC
This will pro-compile most object files and will make your rom-o-matic
builds much faster.
Running rom-o-matic from a web browser
--------------------------------------
Enter a URL like:
http://example.com/ipxe-1.x.x/contrib/rom-o-matic

View File

@ -1,62 +0,0 @@
<?php
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
?>
<hr>
<h4>
Resources:
</h4>
<ul>
<li>
Source code for iPXE images is available at
<a href="http://www.ipxe.org/download" target="_blank">
http://www.ipxe.org/download</a>
<br><br>
</li>
<li>
For general information about using iPXE, please visit the
<a href="http://www.ipxe.org/" target="_blank">
iPXE Project Home Page</a>
<br><br>
</li>
<li>
For Email-based support for iPXE please join
<a href="http://www.ipxe.org/contact" target="_blank">
iPXE Project mailing lists.</a>
<br><br>
</li>
<li>
For real-time online iPXE support via IRC please visit the
<a href="irc://irc.freenode.net/%23ipxe"> #ipxe channel
of irc.freenode.net</a>.
<br><br>
</li>
</ul>
<hr>
<font size="-1">
<br>
Please email <a href="mailto:<?php echo "${webmaster_email}" ?>"><?php echo "${webmaster_email}"?></a>
with questions or comments about this website.
</font>
<br><br>
<hr>
</body>
</html>

View File

@ -1,311 +0,0 @@
<?php // -*- Mode: PHP; -*-
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Get utility functions and set globals
require_once "utils.php";
// Make sure at least $A (action) was supplied
if ( ! isset ( $_POST['A'] ) ) {
// Present user with form to customize build options
require_once "customize-flags.php";
exit ();
// If user chose "Customize" option on form
} else if ( $_POST['A'] == "Customize" ) {
// Present user with form to customize build options
require_once "customize-flags.php";
exit ();
// The following conditional includes all other cases except "Get Image"
// particularly the explicit ($A == "Start Over") case
} else if ( $_POST['A'] != "Get Image" ) {
// Note that this method of redirections discards all the
// configuration flags, which is intentional in this case.
$dest = curDirURL ();
header ( "Location: $dest" );
// This next "echo" should normally not be seen, because
// the "header" statement above should cause immediate
// redirection but just in case...
echo "Try this link: <a href=\"$dest\">$dest</a>";
exit ();
}
// OK, we're going to try to use whatever options have been set
// to build an image.
// Make sure at least $nic was supplied
if ( ! isset ( $_POST['nic'] ) ) {
die ( "No NIC supplied!" );
}
if ( isset ( $nics[$_POST['nic']] ) ) {
$nic = $nics[$_POST['nic']];
} else {
die ( "Invalid NIC \"${_POST['nic']}\" supplied!" );
}
// Fetch flags
$flags = get_flags ();
// Get requested format
$ofmt = isset ( $_POST['ofmt'] ) ? $_POST['ofmt'] : "";
$fmt_extension = isset ( $ofmts[$ofmt] ) ? $ofmts[$ofmt] : 'dsk';
// Handle some special cases
$pci_vendor_code = "";
$pci_device_code = "";
if ( $nic == 'undionly' && $fmt_extension == "pxe" ) {
// undionly.pxe can't work because it unloads the PXE stack
// that it needs to communicate with, so we set the extension
// to .kpxe, which has a chance of working. The extension
// .kkpxe is another option.
$fmt_extension = "kpxe";
} else if ( $fmt_extension == "rom" ) {
if ( ! isset ( $_POST['pci_vendor_code'] )
|| ! isset ( $_POST['pci_device_code'] ) ) {
die ( "rom output format selected but PCI code(s) missing!" );
}
$pci_vendor_code = $_POST['pci_vendor_code'];
$pci_device_code = $_POST['pci_device_code'];
if ( $pci_vendor_code == ""
|| $pci_device_code == "" ) {
die ( "rom output format selected but PCI code(s) missing!" );
}
// Try to be forgiving of 0xAAAA format
if ( strtolower ( substr ( $pci_vendor_code, 0, 2 ) ) == "0x"
&& strlen ( $pci_vendor_code ) == 6 ) {
$pci_vendor_code = substr ( $pci_vendor_code, 2, 4 );
}
if ( strtolower ( substr ( $pci_device_code, 0, 2 ) ) == "0x"
&& strlen ( $pci_device_code ) == 6 ) {
$pci_device_code = substr ( $pci_device_code, 2, 4 );
}
// concatenate the pci codes to get the $nic part of the
// Make target
$pci_codes = strtolower ( $pci_vendor_code . $pci_device_code );
$nic = $pci_codes;
if ( ! isset ( $roms[$pci_codes] ) ) {
die ( "Sorry, no network driver supports PCI codes<br>"
. "${_POST['pci_vendor_code']}:"
. "${_POST['pci_device_code']}" );
}
} else if ( $fmt_extension != "rom"
&& ( $pci_vendor_code != "" || $pci_device_code != "" ) ) {
die ( "'$fmt_extension' format was selected but PCI IDs were"
. " also entered.<br>Did you mean to select 'rom' output format"
. " instead?" );
}
/**
* remove temporary build directory
*
* @return bool true if removal is successful, false otherwise
*/
function rm_build_dir ()
{
global $build_dir;
global $keep_build_dir;
if ( $keep_build_dir !== true ) {
rm_file_or_dir ( $build_dir );
}
}
// Arrange for the build directory to always be removed on exit.
$build_dir = "";
$keep_build_dir = false;
register_shutdown_function ( 'rm_build_dir' );
// Make temporary copy of src directory
$build_dir = mktempcopy ( "$src_dir", "/tmp", "MDCROM" );
$config_dir = $build_dir . "/config";
// Write config files with supplied flags
write_ipxe_config_files ( $config_dir, $flags );
// Handle a possible embedded script
$emb_script_cmd = "";
$embedded_script = isset ( $_POST['embedded_script'] ) ? $_POST['embedded_script'] : "";
if ( $embedded_script != "" ) {
$emb_script_path = "$build_dir" . "/script0.ipxe";
if ( substr ( $embedded_script, 0, 5 ) != "#!ipxe" ) {
$embedded_script = "#!ipxe\n" . $embedded_script;
}
// iPXE 0.9.7 doesn't like '\r\n" in the shebang...
$embedded_script = str_replace ( "\r\n", "\n", $embedded_script );
write_file_from_string ( $emb_script_path, $embedded_script );
$emb_script_cmd = "EMBEDDED_IMAGE=${emb_script_path}";
}
// Make the requested image. $status is set to 0 on success
$make_target = "bin/${nic}.${fmt_extension}";
$gitversion = exec('git describe --always --abbrev=1 --match "" 2>/dev/null');
if ($gitversion) {
$gitversion = "GITVERSION=$gitversion";
}
$make_cmd = "make -C '$build_dir' '$make_target' $gitversion $emb_script_cmd 2>&1";
exec ( $make_cmd, $maketxt, $status );
// Uncomment the following section for debugging
/**
echo "<h2>build.php:</h2>";
echo "<h3>Begin debugging output</h3>";
//echo "<h3>\$_POST variables</h3>";
//echo "<pre>"; var_dump ( $_POST ); echo "</pre>";
echo "<h3>Build options:</h3>";
echo "<strong>Build directory is:</strong> $build_dir" . "<br><br>";
echo "\$_POST['ofmt'] = " . "\"${_POST['ofmt']}\"" . "<br>";
echo "\$_POST['nic'] = " . "\"${_POST['nic']}\"" . "<br>";
echo "\$_POST['pci_vendor_code'] = " . "\"${_POST['pci_vendor_code']}\"" . "<br>";
echo "\$_POST['pci_device_code'] = " . "\"${_POST['pci_device_code']}\"" . "<br>";
echo "<h3>Flags:</h3>";
show_flags ( $flags );
if ( $embedded_script != "" ) {
echo "<h3>Embedded script:</h3>";
echo "<blockquote>"."<pre>";
echo $embedded_script;
echo "</pre>"."</blockquote>";
}
echo "<h3>Make output:</h3>";
echo "Make command: " . $make_cmd . "<br>";
echo "Build status = <? echo $status ?>" . "<br>";
echo "<blockquote>"."<pre>";
echo htmlentities ( implode ("\n", $maketxt ) );
echo "</pre>"."</blockquote>";
// Uncomment the next line if you want to keep the
// build directory around for inspection after building.
$keep_build_dir = true;
die ( "<h3>End debugging output</h3>" );
**/ // End debugging section
// Send ROM to browser (with extreme prejudice)
if ( $status == 0 ) {
$fp = fopen("${build_dir}/${make_target}", "rb" );
if ( $fp > 0 ) {
$len = filesize ( "${build_dir}/${make_target}" );
if ( $len > 0 ) {
$buf = fread ( $fp, $len );
fclose ( $fp );
// Delete build directory as soon as it is not needed
rm_build_dir ();
$output_filename = preg_replace('/[^a-z0-9\+\.\-]/i', '', "ipxe-${version}-${nic}.${fmt_extension}");
// Try to force IE to handle downloading right.
Header ( "Cache-control: private");
Header ( "Content-Type: application/x-octet-stream; " .
"name=$output_filename");
Header ( "Content-Disposition: attachment; " .
"Filename=$output_filename");
Header ( "Content-Location: $output_filename");
Header ( "Content-Length: $len");
echo $buf;
exit ();
}
}
}
/*
* If we reach this point, the build has failed, and we provide
* debugging information for a potential bug report
*
*/
// Remove build directory
rm_build_dir ();
// Announce failure if $status from make was non-zero
echo "<h2>Build failed. Status = " . $status . "</h2>";
echo "<h2>build.php:</h2>";
echo "<h3>Build options:</h3>";
echo "<strong>Build directory is:</strong> $build_dir" . "<br><br>";
echo "\$_POST['ofmt'] = " . "\"${_POST['ofmt']}\"" . "<br>";
echo "\$_POST['nic'] = " . "\"${_POST['nic']}\"" . "<br>";
echo "\$_POST['pci_vendor_code'] = " . "\"${_POST['pci_vendor_code']}\"" . "<br>";
echo "\$_POST['pci_device_code'] = " . "\"${_POST['pci_device_code']}\"" . "<br>";
echo "<h3>Flags:</h3>";
show_flags ( $flags );
if ( $embedded_script != "" ) {
echo "<h3>Embedded script:</h3>";
echo "<blockquote>"."<pre>";
echo $embedded_script;
echo "</pre>"."</blockquote>";
}
echo "<h3>Make output:</h3>";
echo "Make command: " . $make_cmd . "<br>";
echo "<blockquote>"."<pre>";
echo htmlentities ( implode ("\n", $maketxt ) );
echo "</pre>"."</blockquote>";
echo "Please let us know that this happened, and paste the above output into your email message.<br>";
include_once $bottom_inc;
// For emacs:
// Local variables:
// c-basic-offset: 4
// c-indent-level: 4
// tab-width: 4
// End:
?>

View File

@ -1,69 +0,0 @@
<?php // -*- Mode: PHP; -*-
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Get utility functions and set globals
require_once "utils.php";
// Prepare settable compile options for presentation to user
$flags = default_flags ();
$build = "<input type=\"submit\" name=\"A\" value=\"Get Image\">";
$restart = "<input type=\"submit\" name=\"A\" value=\"Start Over\">";
// Begin html output
include_once $top_inc;
?>
<form action="build.php" method=POST>
<input type="hidden" name="version" value = "<?php echo $version ?>">
<input type="hidden" name="use_flags" value="1">
<h3>
Make changes below and press <?php echo $build ?> to create an image, <br>
Or press <?php echo $restart ?> to return to the main page.
</h3>
<hr>
<ul>
<?php require ( "directions.php" ); ?>
</ul>
<hr>
<?php echo_flags( $flags ); ?>
<hr>
<h3>Embedded Script:</h3>
<?php echo textarea ( "embedded_script", "", "10", "50" ); ?>
<br><br>
<hr>
<center><table width="35%"><tr>
<td align="left"> <?php echo $build; ?> </td>
<td align="right"> <?php echo $restart ?></td>
</tr></table></center>
</form>
<?php include_once $bottom_inc; ?>
<?
// For emacs:
//
// Local variables:
// c-basic-offset: 4
// c-indent-level: 4
// tab-width: 4
// End:
?>

View File

@ -1,63 +0,0 @@
<?php
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
?>
<li>
Choose an output format: <?php echo keys_menubox ( "ofmt", $ofmts,
isset ( $_POST['ofmt'] ) ? $_POST['ofmt'] : "") ?>
<br><br>
</li>
<li>
Choose a NIC type: <?php echo keys_menubox ( "nic", $nics,
isset ( $_POST['nic'] ) ? $_POST['nic'] : "" ) ?>
<br><br>
</li>
<li>
<strong>( optional &mdash; for binary ROM image format only )</strong> <br><br>
If you choose <em>Binary ROM image</em> as your output format, you must<br>
enter <strong>4 hex digits</strong> below for
<em>PCI VENDOR CODE</em> and <em>PCI DEVICE CODE</em> <br>
that match the NIC device for which you are making this image.<br><br>
Information on how to determine NIC PCI IDs may be found
<a href="http://www.ipxe.org/howto/romburning"
target="_blank">here</a>.
<br><br>
PCI VENDOR CODE: <?php echo textbox ( "pci_vendor_code",
isset ( $_POST['pci_vendor_code'] ) ? $_POST['pci_vendor_code']
: "", 6 ); ?>
&nbsp;&nbsp;
PCI DEVICE CODE: <?php echo textbox ( "pci_device_code",
isset ( $_POST['pci_device_code'] ) ? $_POST['pci_device_code']
: "", 6 ); ?>
<h4>Please note for ROM images:</h4>
<ul>
<li>
If you enter PCI IDs, we will attempt to determine the correct<br>
driver to support them, and will ignore any NIC type entered
above.<br><br>
</li>
<li>
iPXE does not support all possible PCI IDs for supported
NICs.
<br><br>
</li>
</ul>
</li>

View File

@ -1 +0,0 @@
Automatic booting

View File

@ -1 +0,0 @@
Tenths of a second for which the shell banner should appear

View File

@ -1,3 +0,0 @@
Serial Console I/O port address. Common addresses are:<br>
COM1 => 0x3f8, COM2 => 0x2f8, COM3 => 0x3e8, COM4 => 0x2e8

View File

@ -1 +0,0 @@
Serial Console Data bits

View File

@ -1 +0,0 @@
Serial Console Parity: 0=None, 1=Odd, 2=Even

View File

@ -1 +0,0 @@
Keep settings from a previous user of the serial port

View File

@ -1 +0,0 @@
Serial Console Baud rate

View File

@ -1 +0,0 @@
Serial Console Stop bits

View File

@ -1 +0,0 @@
Option configuration console

View File

@ -1 +0,0 @@
Enable Default BIOS console

View File

@ -1 +0,0 @@
Enable Serial port console

View File

@ -1 +0,0 @@
Wireless WEP encryption support

View File

@ -1 +0,0 @@
Wireless WPA encryption support

View File

@ -1 +0,0 @@
Wireless WPA2 encryption support

View File

@ -1 +0,0 @@
DHCP management commands

View File

@ -1 +0,0 @@
DNS resolver

View File

@ -1 +0,0 @@
File Transfer Protocol

View File

@ -1 +0,0 @@
Hypertext Transfer Protocol

View File

@ -1 +0,0 @@
Trivial File Transfer Protocol

View File

@ -1 +0,0 @@
Interface management commands

View File

@ -1 +0,0 @@
Linux bzImage image support

View File

@ -1 +0,0 @@
Image management commands

View File

@ -1 +0,0 @@
ELF image support

View File

@ -1 +0,0 @@
MultiBoot image support

View File

@ -1 +0,0 @@
NBI image support

View File

@ -1 +0,0 @@
PXE image support

View File

@ -1 +0,0 @@
iPXE script image support

View File

@ -1 +0,0 @@
Wireless interface management commands

View File

@ -1 +0,0 @@
NMB resolver

View File

@ -1 +0,0 @@
Non-volatile option storage commands

View File

@ -1 +0,0 @@
Routing table management commands

View File

@ -1 +0,0 @@
SAN boot commands

View File

@ -1,531 +0,0 @@
<?php // -*- Mode: PHP; -*-
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
$ofmts = array
( "Floppy bootable image (.dsk)" => "dsk",
"SYSLINUX-based bootable floppy image (.sdsk)" => "sdsk",
"ISO bootable image (.iso)" => "iso",
"ISO bootable image with legacy floppy emulation (.liso)" => "liso",
"Linux kernel (SYSLINUX/GRUB/LILO) loadable image (.lkrn)" => "lkrn",
"USB Keychain disk image (.usb)" => "usb",
"ROM binary (flashable) image (.rom)" => "rom",
"ROM binary (flashable) for problem PMM BIOSES (.hrom)" => "hrom",
"PXE bootstrap loader image [Unload PXE stack] (.pxe)" => "pxe",
"PXE bootstrap loader keep [Keep PXE stack method 1] (.kpxe)" => "kpxe",
"PXE bootstrap loader keep [Keep PXE stack method 2] (.kkpxe)" => "kkpxe",
);
$flag_table = array (
// Begin General Options:
"HDR_MISC_OPTIONS"
=> array (
"flag" => "HDR_MISC_OPTIONS",
"hide_from_user" => "yes", // Hide even the header
"type" => "header",
"label" => "Miscellaneous Options"
),
"PRODUCT_NAME"
=> array (
"flag" => "PRODUCT_NAME",
"hide_from_user" => "yes",
"type" => "string",
"value" => "",
"cfgsec" => "general"
),
"PRODUCT_SHORT_NAME"
=> array (
"flag" => "PRODUCT_SHORT_NAME",
"hide_from_user" => "yes",
"type" => "string",
"value" => "iPXE",
"cfgsec" => "general"
),
// End General Options:
// Begin Console Options:
"HDR_CONSOLE_OPTIONS"
=> array (
"flag" => "HDR_CONSOLE_OPTIONS",
"type" => "header",
"label" => "Console Options"
),
"CONSOLE_PCBIOS"
=> array (
"flag" => "CONSOLE_PCBIOS",
"type" => "on/off",
"value" => "on",
"cfgsec" => "console"
),
"CONSOLE_SERIAL"
=> array (
"flag" => "CONSOLE_SERIAL",
"type" => "on/off",
"value" => "off",
"cfgsec" => "console"
),
"BANNER_TIMEOUT"
=> array (
"flag" => "BANNER_TIMEOUT",
"type" => "integer",
"value" => "20",
"cfgsec" => "general"
),
"KEYBOARD_MAP"
=> array (
"flag" => "KEYBOARD_MAP",
"type" => "choice",
"options" => array("al","az","bg","by","cf","cz","de","dk","es","et","fi","fr",
"gr","hu","il","it","lt","mk","mt","nl","no","pl","pt","ro","ru","sg","sr",
"th","ua","uk","us","wo"),
"value" => "us",
"cfgsec" => "console"
),
"LOG_LEVEL"
=> array (
"flag" => "LOG_LEVEL",
"type" => "choice",
"options" => array("LOG_NONE","LOG_EMERG","LOG_ALERT","LOG_CRIT","LOG_ERR",
"LOG_WARNING","LOG_NOTICE","LOG_INFO","LOG_DEBUG","LOG_ALL"),
"value" => "LOG_NONE",
"cfgsec" => "console"
),
// End Console Options
// Begin Network Protocol Options:
"HDR_NETWORK_PROTOCOL_OPTIONS"
=> array (
"flag" => "HDR_NETWORK_PROTOCOL_OPTIONS",
"hide_from_user" => "yes", // Hide even the header
"type" => "header",
"label" => "Network Protocol Options"
),
"NET_PROTO_IPV4"
=> array (
"flag" => "NET_PROTO_IPV4",
"type" => "on/off",
"value" => "on",
"hide_from_user" => "yes",
"cfgsec" => "general"
),
// End Network Protocol Options
// Begin Serial Port configuration
"HDR_SERIAL_PORT_OPTIONS"
=> array (
"flag" => "HDR_SERIAL_PORT_OPTIONS",
"type" => "header",
"label" => "Serial Port Options"
),
"COMCONSOLE"
=> array (
"flag" => "COMCONSOLE",
"type" => "integer-hex", // e.g. 0x378
"value" => "0x3F8",
"cfgsec" => "serial"
),
"COMPRESERVE"
=> array (
"flag" => "COMPRESERVE",
"type" => "on/off",
"value" => "off",
"cfgsec" => "serial"
),
"COMSPEED"
=> array (
"flag" => "COMSPEED",
"type" => "integer",
"value" => "115200",
"cfgsec" => "serial"
),
"COMDATA"
=> array (
"flag" => "COMDATA",
"type" => "integer",
"value" => "8",
"cfgsec" => "serial"
),
"COMPARITY"
=> array (
"flag" => "COMPARITY",
"type" => "integer",
"value" => "0",
"cfgsec" => "serial"
),
"COMSTOP"
=> array (
"flag" => "COMSTOP",
"type" => "integer",
"value" => "1",
"cfgsec" => "serial"
),
// End Serial Options
// Begin Download Protocols
"HDR_DOWNLOAD_PROTOCOLS"
=> array (
"flag" => "HDR_DOWNLOAD_PROTOCOLS",
"type" => "header",
"label" => "Download Protocols"
),
"DOWNLOAD_PROTO_TFTP"
=> array (
"flag" => "DOWNLOAD_PROTO_TFTP",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"DOWNLOAD_PROTO_HTTP"
=> array (
"flag" => "DOWNLOAD_PROTO_HTTP",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"DOWNLOAD_PROTO_HTTPS"
=> array (
"flag" => "DOWNLOAD_PROTO_HTTPS",
"type" => "on/off",
"value" => "off",
"cfgsec" => "general"
),
"DOWNLOAD_PROTO_FTP"
=> array (
"flag" => "DOWNLOAD_PROTO_FTP",
"type" => "on/off",
"value" => "off",
"cfgsec" => "general"
),
// End Download Protocols
// Begin SAN boot protocols
"HDR_SANBOOT_PROTOCOLS"
=> array (
"flag" => "HDR_SANBOOT_PROTOCOLS",
"type" => "header",
"label" => "SAN Boot Protocols"
),
"SANBOOT_PROTO_ISCSI"
=> array (
"flag" => "SANBOOT_PROTO_ISCSI",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"SANBOOT_PROTO_AOE"
=> array (
"flag" => "SANBOOT_PROTO_AOE",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
// End SAN boot protocols
// Begin Name resolution modules
"HDR_NAME_RESOLUTION_MODULES"
=> array (
"flag" => "HDR_NAME_RESOLUTION_MODULES",
"type" => "header",
"label" => "Name Resolution Modules"
),
"DNS_RESOLVER"
=> array (
"flag" => "DNS_RESOLVER",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"NMB_RESOLVER"
=> array (
"flag" => "NMB_RESOLVER",
"type" => "on/off",
"value" => "off",
"hide_from_user" => "yes",
"cfgsec" => "general"
),
// End Name resolution modules
// Begin Image types
"HDR_IMAGE_TYPES"
=> array (
"flag" => "HDR_IMAGE_TYPES",
"type" => "header",
"label" => "Image Types",
),
"IMAGE_ELF"
=> array (
"flag" => "IMAGE_ELF",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IMAGE_NBI"
=> array (
"flag" => "IMAGE_NBI",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IMAGE_MULTIBOOT"
=> array (
"flag" => "IMAGE_MULTIBOOT",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IMAGE_PXE"
=> array (
"flag" => "IMAGE_PXE",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IMAGE_SCRIPT"
=> array (
"flag" => "IMAGE_SCRIPT",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IMAGE_BZIMAGE"
=> array (
"flag" => "IMAGE_BZIMAGE",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IMAGE_COMBOOT"
=> array (
"flag" => "IMAGE_COMBOOT",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
// End Image types
// Begin Command-line commands to include
"HDR_COMMAND_LINE_OPTIONS"
=> array (
"flag" => "HDR_COMMAND_LINE_OPTIONS",
"type" => "header",
"label" => "Command Line Options",
),
"AUTOBOOT_CMD"
=> array (
"flag" => "AUTOBOOT_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"NVO_CMD"
=> array (
"flag" => "NVO_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"CONFIG_CMD"
=> array (
"flag" => "CONFIG_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IFMGMT_CMD"
=> array (
"flag" => "IFMGMT_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IWMGMT_CMD"
=> array (
"flag" => "IWMGMT_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"ROUTE_CMD"
=> array (
"flag" => "ROUTE_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"IMAGE_CMD"
=> array (
"flag" => "IMAGE_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"DHCP_CMD"
=> array (
"flag" => "DHCP_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"SANBOOT_CMD"
=> array (
"flag" => "SANBOOT_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"LOGIN_CMD"
=> array (
"flag" => "LOGIN_CMD",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"TIME_CMD"
=> array (
"flag" => "TIME_CMD",
"type" => "on/off",
"value" => "off",
"cfgsec" => "general"
),
"DIGEST_CMD"
=> array (
"flag" => "DIGEST_CMD",
"type" => "on/off",
"value" => "off",
"cfgsec" => "general"
),
// End Command-line commands to include
// Begin Wireless options
"HDR_WIRELESS_OPTIONS"
=> array (
"flag" => "HDR_WIRELESS_OPTIONS",
"type" => "header",
"label" => "Wireless Interface Options",
),
"CRYPTO_80211_WEP"
=> array (
"flag" => "CRYPTO_80211_WEP",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"CRYPTO_80211_WPA"
=> array (
"flag" => "CRYPTO_80211_WPA",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
"CRYPTO_80211_WPA2"
=> array (
"flag" => "CRYPTO_80211_WPA2",
"type" => "on/off",
"value" => "on",
"cfgsec" => "general"
),
// End Wireless options
// Obscure options required to compile
"NETDEV_DISCARD_RATE"
=> array (
"flag" => "NETDEV_DISCARD_RATE",
"type" => "integer",
"value" => "0",
"cfgsec" => "general",
"hide_from_user" => true
)
// End Obscure options
);
// For emacs:
// Local variables:
// c-basic-offset: 4
// c-indent-level: 4
// tab-width: 4
// End:
?>

View File

@ -1,51 +0,0 @@
<?php // -*- Mode: PHP; -*-
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Directory containing iPXE source code tree
$src_dir = "../../src";
// Compute iPXE version based on source tree
exec ( "make -C '$src_dir' version 2>&1", $make_output, $status );
$version = ( $status == 0 && count ( $make_output ) > 1 )
? trim ( $make_output[count ( $make_output ) - 2] )
: "";
// Email address of person responsible for this website
$webmaster_email = "webmaster@example.com";
// Files that header and footer text
$top_inc = "top.php";
$bottom_inc = "bottom.php";
// Descriptive strings
$header_title = "ROM-o-matic for iPXE $version";
$html_tagline = "ROM-o-matic dynamically generates iPXE images";
$html_title = "ROM-o-matic for iPXE $version";
$description = "a dynamic iPXE image generator";
// For emacs:
// Local variables:
// c-basic-offset: 4
// c-indent-level: 4
// tab-width: 4
// End:
?>

View File

@ -1,47 +0,0 @@
<?php // -*- Mode: PHP; -*-
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Get utility functions and set globals
require_once "utils.php";
// Begin html output
include_once $top_inc;
?>
<form action="build.php" method=POST>
<input type="hidden" name="version" value = "<?php echo $version ?>">
<h3>To create an image:</h3>
<ol>
<?php require ( "directions.php" ); ?>
<li>
Generate and download an image:
<input type="submit" name="A" value="Get Image">
<br><br>
</li>
<li>
(optional) Customize image configuration options:
<input type="submit" name="A" value="Customize">
<br><br>
</li>
</ol>
</form>
<?php include_once $bottom_inc ?>

View File

@ -1,684 +0,0 @@
<?php // -*- Mode: PHP; -*-
/**
* Copyright (C) 2009 Marty Connor <mdc@etherboot.org>.
* Copyright (C) 2009 Entity Cyber, Inc.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Include table of user-configurable iPXE options
require_once "flag-table.php";
// Include user-shadowable globals
require_once "globals.php";
// Allow user to shadow globals
if ( is_file ( 'local-config.php' ) ) {
include_once "local-config.php";
}
////
// General utility functions
////
/**
* Remove undesirable characters from a given string
*
* Certain characters have the potential to be used for
* malicious purposes by web-based attackers. This routine
* filters out such characters.
*
* @param string $s supplied string
*
* @return string returned string with unwanted characters
* removed
*/
function cleanstring ( $s )
{
$len = strlen ( $s );
if ( $len > 80 ) {
$s = substr ( $s, 0, 80 );
}
$s = trim ( $s );
$pos = 0;
$result = "";
while ( $pos < $len ) {
$ltr = ord ( ucfirst ( $s[$pos] ) );
if ( ( $ltr >= ord ( "A" ) ) && ( $ltr <= ord ( "Z" ) ) ||
( $ltr >= ord ( "0" ) ) && ( $ltr <= ord ( "9" ) ) ||
( $ltr == ord ( "." ) ) && ( strlen ( $result ) > 0 ) ||
( $ltr == ord ( "_" ) ) ||
( $ltr == ord ( "+" ) ) ||
( $ltr == ord ( ":" ) ) ||
( $ltr == ord ( "/" ) ) ||
( $ltr == ord ( "-" ) ) ) {
$result .= $s[$pos];
}
$pos++;
}
return $result;
}
/**
* Return URL of the currently running script, minus the filename
*
* @return string the URL of the currently running script, minus the filename
*/
function curDirURL ()
{
$dir = dirname ( $_SERVER['PHP_SELF'] );
if ( $dir == "." || $dir == "/" ) {
$dir = "";
}
$isHTTPS = ( isset ( $_SERVER["HTTPS"] ) && $_SERVER["HTTPS"] == "on" );
$port = ( isset($_SERVER["SERVER_PORT"] ) &&
( ( !$isHTTPS && $_SERVER["SERVER_PORT"] != "80" ) ||
( $isHTTPS && $_SERVER["SERVER_PORT"] != "443" ) ) );
$port = ( $port ) ? ':' . $_SERVER["SERVER_PORT"] : '';
$dest = ( $isHTTPS ? 'https://' : 'http://' ) .
$_SERVER["SERVER_NAME"] . $dir . "/";
return $dest;
}
/**
* Extract NIC families and associated ROM PCI IDs from the src/bin/NIC file.
*
* $src_dir must contain the path of the iPXE src directory for this build
*
* @return array[0] array $new_nics
* @return array[1] array $roms
*/
function parse_nic_file ()
{
global $src_dir;
$fd = fopen ( "$src_dir/bin/NIC", "r" );
if ( ! $fd ) {
die ( "Missing src/bin/NIC file. 'make bin/NIC'" );
}
$nics = array ();
$roms = array ();
$nic = "";
while ( !feof ( $fd ) ) {
$line = trim ( fgets ( $fd, 200 ) );
$first_eight_chars = substr ( $line, 0, 8 );
settype ( $first_eight_chars, "string" );
if ( strpos ( $first_eight_chars, "family" ) === 0 ) {
// get pathname of NIC driver
#list ( $dummy, $nic ) = split( "[ \t]+", $line );
list ( $dummy, $nic ) = explode("\t", $line);
settype ( $nic, "string" );
// extract filename name of driver from pathname
$nic = substr ( $nic, strrpos ( $nic, "/" ) + 1,
strlen ( $nic ) - strrpos ( $nic, "/" ) + 1 );
$nics[$nic] = $nic;
// For each ISA NIC, there can only be one ROM variant
$roms[$nic] = $nic;
}
// If the first 8 digits of the line are hex digits
// add this rom to the current nic family.
if ( ( strlen ( $first_eight_chars ) == 8 )
&& ( ctype_xdigit ( $first_eight_chars ) )
&& ( $nic != "" ) ) {
$roms[$first_eight_chars] = $nic;
}
}
fclose ( $fd );
// put most NICs in nice alpha order for menu
ksort ( $nics );
// add special cases to the top
$new_nics = array ( "all-drivers" => "ipxe",
"undionly" => "undionly",
"undi" => "undi",
);
foreach ( $nics as $key => $value ) {
// skip the undi driver
if ( $key != "undi" ) {
$new_nics[$key] = $value;
}
}
return array ( $new_nics, $roms );
}
////
// HTML form utility functions
////
/**
* Return html code to create hidden form input fields
*
* @param string $flag name of form variable to set
* @param string $value value to give form variable
*
* @return string html code for given hidden form input field
*/
function hidden ( $flag, $value )
{
$value = htmlentities ( $value );
return "<input type=\"hidden\" value=\"$value\" name=\"$flag\"></input>";
}
/**
* Return html code to create checkbox form input fields
*
* @param string $flag name of form variable to set
* @param string $value "on" means box should be checked
*
* @return string html code for given hidden form input field
*/
function checkbox ( $flag, $value )
{
return "<input type=\"checkbox\" value=\"on\" name=\"$flag\"" .
($value == "on" ? " checked>" : ">" );
}
/**
* Return html code to create text form input fields
*
* @param string $flag name of form variable to set
* @param string $value initial contents of field
* @param string $size size in characters of text box
*
* @return string html code for given text input field
*/
function textbox ( $flag, $value, $size )
{
$value = htmlentities ( $value );
return "<input type=\"text\" size=\"$size\" value=\"$value\" name=\"$flag\">";
}
/**
* Return html code to create textarea form fields
*
* @param string $flag name of form variable to set
* @param string $value initial contents of textarea
* @param string $rows height of text area in rows
* @param string $cols width of text area in columns
*
* @return string html code for given textarea input field
*/
function textarea ( $flag, $value, $rows, $cols )
{
$value = htmlentities ( $value );
return "<textarea name=\"$flag\" rows=\"$rows\" cols=\"$cols\">"
. $value . "</textarea>";
}
/**
* Return html code to create select (menu) form fields
*
* Use array of strings as menu choices
*
* @param string $flag name of form variable to set
* @param array $options array of strings representing choices
* @param string $value value of choice to select in menu
*
* @return string html code for given select (menu) input field
*/
function menubox ( $name, $options, $value )
{
$s="<select name=\"$name\">";
foreach ( $options as $ignore => $option ) {
if ( !$value ) $value = $option;
$s .= "<option" . ( $option == $value ? " selected>" : ">" ) .
htmlentities ( $option ) . "</option>";
}
return $s . "</select>";
}
/**
* Return html code to create select (menu) form fields
*
* Use indices of array of strings as menu choices rather than
* the values pointed to by the indicies.
*
* @param string $flag name of form variable to set
* @param array $options array of strings representing choices
* @param string $value value of choice to select in menu
*
* @return string html code for given select (menu) input field
*/
function keys_menubox ( $name, $options, $value )
{
$s="<select name=\"$name\">";
foreach ( $options as $option => $ignore ) {
if ( !$value ) $value = $option;
$s .= "<option" . ( $option == $value ? " selected>" : ">" ) .
htmlentities ( $option ) . "</option>";
}
return $s . "</select>";
}
////
// Flag (compile option) handling functions
////
/**
* Return default compile options (flags)
*
* Initial compile options are in a global called $flag_table.
* Create and return an array containing the ones we want.
*
* @return array default compile options (flags)
*/
function default_flags ()
{
global $flag_table;
$flags = array ();
foreach ( $flag_table as $key => $props ) {
$flag = $props["flag"];
$type = $props["type"];
// Fields like headers have no "value" property
if ( isset ( $props["value"] ) ) {
$flags[$flag] = $props["value"];
}
}
return $flags;
}
/**
* Return combination of default and user compile options (flags)
*
* Initial compile options are in a global called $flag_table.
* Compile options may have been changed via form input. We return
* an array with either the default value of each option or a user
* supplied value from form input.
*
* @return array combined default and user supplied compile options (flags)
*/
function get_flags ()
{
global $flag_table;
$flags = default_flags ();
if ( ! isset ( $_POST["use_flags"] ) )
return $flags;
foreach ( $flag_table as $key => $props ) {
$flag = $props["flag"];
$type = $props["type"];
if ( isset ( $_POST["$flag"] ) ) {
$flags[$flag] = $_POST["$flag"];
if ( $type == "integer-hex" ) {
if ( strtolower ( substr ( $flags[$flag], 0, 2 ) ) != "0x" ) {
$flags[$flag] = "0x" . $flags[$flag];
}
}
} else if ( $type == "on/off" ) {
// Unchecked checkboxes don't pass any POST value
// so we must check for them specially. At this
// point we know that there is no $_POST value set
// for this option. If it is a checkbox, this means
// it is unchecked, so record that in $flags so we
// can later generate an #undef for this option.
$flags[$flag] = "off";
}
}
return $flags;
}
/**
* Output given value in appropriate format for iPXE config file
*
* iPXE config/*.h files use C pre-processor syntax. Output the given
* compile option in a format appropriate to its type
*
* @param string $key index into $flag_table for given compile option
* @param string $value value we wish to set compile option to
*
* @return string code to set compile option to given value
*/
function pprint_flag ( $key, $value )
{
global $flag_table;
// Determine type of given compile option (flag)
$type = $flag_table[$key]["type"];
$s = "";
if ( $type == "on/off" && $value == "on" ) {
$s = "#define $key";
} else if ( $type == "on/off" && $value != "on" ) {
$s = "#undef $key";
} else if ( $type == "string" ) {
$s = ( "#define $key \"" . cleanstring ( $value ) . "\"" );
} else if ($type == "qstring" ) {
$s = ( "#define $key \\\"" . cleanstring ( $value ) . "\\\"" );
} else {
$s = "#define $key " . cleanstring ( $value );
}
return $s;
}
/**
* Output html code to display all compile options as a table
*
* @param array $flags array of compile options
*
* @return void
*/
function echo_flags ( $flags )
{
global $flag_table;
echo "<table>\n";
foreach ( $flag_table as $key => $props ) {
// Hide parameters from users that should not be changed.
$hide_from_user = isset ( $props["hide_from_user"] ) ? $props["hide_from_user"] : "no";
$flag = $props["flag"];
$type = $props["type"];
$value = isset ( $flags[$flag] ) ? $flags[$flag] : '';
if ( $hide_from_user == "yes" ) {
// Hidden flags cannot not be set by the user. We use hidden form
// fields to keep them at their default values.
if ( $type != "header" ) {
echo hidden ( $flag, $value );
}
} else {
// Flag (iPXE compile option) should be displayed to user
if ( $type == "header" ) {
$label = $props["label"];
echo "<td colspan=2><hr><h3>$label</h3><hr></td>";
} else if ($type == "on/off" ) {
echo "<td>", checkbox ( $flag, $value ), "</td><td><strong>$flag</strong></td>";
} else { // don't display checkbox for non-on/off flags
echo "<td>&nbsp;</td><td><strong>$flag: </strong>";
if ($type == "choice" ) {
$options = $props["options"];
echo menubox($flag, $options, $value);
} else {
echo textbox($flag, $value, ($type == "integer" ||
$type == "integer-hex"
? 7 : 25));
}
echo "</td>";
}
echo "</tr>\n";
if ( $type != "header" ) {
echo "<tr><td>&nbsp;</td>";
echo "<td>\n";
if ( is_file ( "doc/$flag.html" ) ) {
include_once "doc/$flag.html";
}
echo "\n</td></tr>\n";
}
}
}
echo "</table>";
}
/**
* Return an array of configuration sections used in all compile options
*
* $flag_table, the global list of compile options contains a 'cfgsec'
* property for each flag we are interested in. We return a list of
* all the unique cfgsec options we find in $flag_table.
*
* @return array an array of strings representing all unique cfgsec values
* found in $flag_table
*/
function get_flag_cfgsecs ()
{
global $flag_table;
$cfgsecs = array ();
foreach ( $flag_table as $key => $props ) {
if ( isset ( $props['cfgsec'] ) ) {
$cfgsec = $props["cfgsec"];
$cfgsecs[$cfgsec] = $cfgsec;
}
}
return $cfgsecs;
}
////
// File and directory handling functions
////
/**
* Create a copy of a given source directory to a given destination
*
* Since we are going to modify the source directory, we create a copy
* of the directory with a unique name in the given destination directory.
* We supply a prefix for the tempnam call to prepend to the random filename
* it generates.
*
* @param string $src source directory
* @param string $dst destination directory
* @param string $prefix string to append to directory created
*
* @return string absolute path to destination directory
*/
function mktempcopy ( $src, $dst, $prefix )
{
if ( $src[0] != "/" ) {
$src = dirname ( $_SERVER['SCRIPT_FILENAME'] ) . "/" . $src;
}
// Create a file in the given destination directory with a unique name
$dir = tempnam ( $dst, $prefix );
// Delete the file just created, since it would interfere with the copy we
// are about to do. We only care that the dir name we copy to is unique.
unlink ( $dir );
exec ( "/bin/cp -a '$src' '$dir' 2>&1", $cpytxt, $status );
if ( $status != 0 ) {
die ( "src directory copy failed!" );
}
return $dir;
}
/**
* Write iPXE config files based on value of given flags
*
* iPXE compile options are stored in src/config/*.h .
* We write out a config file for each set of options.
*
* @param string $config_dir directory to write .h files to
* @param array $flags array of compile options for this build
*
* @return void
*/
function write_ipxe_config_files ( $config_dir, $flags )
{
global $flag_table;
$cfgsecs = get_flag_cfgsecs ();
foreach ( $cfgsecs as $cfgsec ) {
$fname = $config_dir . "/" . $cfgsec . ".h";
$fp = fopen ( $fname, "wb" );
if ( $fp <= 0 ) {
die ( "Unable to open $fname file for output!" );
}
$ifdef_secname = "CONFIG_" . strtoupper ( $cfgsec ) . "_H";
fwrite ( $fp, "#ifndef ${ifdef_secname}\n" );
fwrite ( $fp, "#define ${ifdef_secname}\n" );
fwrite ( $fp, "#include <config/defaults.h>\n" );
foreach ( $flags as $key => $value ) {
// When the flag matches this section name, write it out
if ( $flag_table[$key]["cfgsec"] == $cfgsec ) {
fwrite ( $fp, pprint_flag ( $key, $value ) . "\n" );
}
}
fwrite ( $fp, "#endif /* ${ifdef_secname} */\n" );
fclose ( $fp );
}
}
/**
* Output a string to a file
*
* Output a given string to a given pathname. The file will be created if
* necessary, and the string will replace the file's contents in all cases.
*
* @param string $fname pathname of file to output string to
* @param string $ftext text to output to file
*
* @return void
*/
function write_file_from_string ( $fname, $ftext )
{
$fp = fopen ( $fname, "wb" );
if ( ! $fp ) {
die ( "Unable to open $fname file for output!" );
}
fwrite ( $fp, $ftext );
fclose ( $fp );
}
/**
* Delete a file or recursively delete a directory tree
*
* @param string $file_or_dir_name name of file or directory to delete
* @return bool Returns TRUE on success, FALSE on failure
*/
function rm_file_or_dir ( $file_or_dir_name )
{
if ( ! file_exists ( $file_or_dir_name ) ) {
return false;
}
if ( is_file ( $file_or_dir_name ) || is_link ( $file_or_dir_name ) ) {
return unlink ( $file_or_dir_name );
}
$dir = dir ( $file_or_dir_name );
while ( ( $dir_entry = $dir->read () ) !== false ) {
if ( $dir_entry == '.' || $dir_entry == '..') {
continue;
}
rm_file_or_dir ( $file_or_dir_name . '/' . $dir_entry );
}
$dir->close();
return rmdir ( $file_or_dir_name );
}
////
// Debugging functions
////
/**
* Emit html code to display given array of compile options (flags)
*
* @param array $flags array of compile options for this build
*
* @return void
*/
function show_flags ( $flags )
{
echo ( "\$flags contains " . count ( $flags ) . " elements:" . "<br>" );
foreach ( $flags as $key => $flag ) {
echo ( "\$flags[" . $key . "]=" . "\"$flag\"" . "<br>" );
}
}
/**
* Emit HTML code to display default array of compile options (flags)
*
* $flag_table contains default compile options and properties. This
* routine outputs HTML code to display all properties of $flag_table.
*
* @return void
*/
function dump_flag_table ()
{
global $flag_table;
echo ( "\$flag_table contains " . count ( $flag_table ) . " elements:" . "<br>" );
foreach ( $flag_table as $key => $props ) {
print ( "flag_table[" . $key . "] = " . "<br>" );
foreach ( $props as $key2 => $props2 ) {
print ( "&nbsp;&nbsp;&nbsp;" . $key2 . " = " . $props2 . "<br>" );
}
}
}
// Parse src/bin/NIC file
list ( $nics, $roms ) = parse_nic_file ();
// For emacs:
// Local variables:
// c-basic-offset: 4
// c-indent-level: 4
// tab-width: 4
// End:
?>

2
src/.gitignore vendored
View File

@ -1,4 +1,4 @@
.toolcheck
.echocheck
TAGS*
bin*
bin-*

View File

@ -77,6 +77,7 @@ SRCDIRS += drivers/net/efi
SRCDIRS += drivers/net/tg3
SRCDIRS += drivers/net/bnxt
SRCDIRS += drivers/net/sfc
SRCDIRS += drivers/net/marvell
SRCDIRS += drivers/block
SRCDIRS += drivers/nvs
SRCDIRS += drivers/bitbash

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 );

View File

@ -84,8 +84,8 @@ int cpuid_supported ( uint32_t function ) {
return rc;
/* Find highest supported function number within this family */
cpuid ( ( function & CPUID_EXTENDED ), 0, &max_function, &discard_b,
&discard_c, &discard_d );
cpuid ( ( function & ( CPUID_EXTENDED | CPUID_HYPERVISOR ) ), 0,
&max_function, &discard_b, &discard_c, &discard_d );
/* Fail if maximum function number is meaningless (e.g. if we
* are attempting to call an extended function on a CPU which

View File

@ -38,7 +38,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* Bit 31 Extended function
* Bits 30-24 (bit 22 = 1) Subfunction number
* (bit 22 = 0) Number of consecutive functions to call, minus one
* Bit 30 (bit 22 = 0) Hypervisor function
* Bits 29-24 (bit 22 = 0) Number of consecutive functions to call, minus one
* Bit 23 Return result as little-endian (used for strings)
* Bit 22 Interpret bits 30-24 as a subfunction number
* Bits 21-18 Unused
@ -98,7 +99,7 @@ enum cpuid_flags {
* @v tag Setting tag
* @ret function Starting function number
*/
#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
#define CPUID_FUNCTION( tag ) ( (tag) & 0xc00000ffUL )
/**
* Extract subfunction number from CPUID setting tag
@ -108,6 +109,14 @@ enum cpuid_flags {
*/
#define CPUID_SUBFUNCTION( tag ) ( ( (tag) >> 24 ) & 0x7f )
/**
* Extract number of consecutive functions from CPUID setting tag
*
* @v tag Setting tag
* @ret num_functions Number of consecutive functions
*/
#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0x3f ) + 1 )
/**
* Extract register array from CPUID setting tag
*
@ -165,12 +174,13 @@ static int cpuid_settings_fetch ( struct settings *settings,
/* Call each function in turn */
function = CPUID_FUNCTION ( setting->tag );
subfunction = CPUID_SUBFUNCTION ( setting->tag );
if ( setting->tag & CPUID_USE_SUBFUNCTION ) {
function &= ~CPUID_HYPERVISOR;
subfunction = CPUID_SUBFUNCTION ( setting->tag );
num_functions = 1;
} else {
num_functions = ( subfunction + 1 );
subfunction = 0;
num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
}
for ( ; num_functions-- ; function++ ) {

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

@ -45,6 +45,7 @@ void pcidirect_prepare ( struct pci_device *pci, int where ) {
PCIDIRECT_CONFIG_ADDRESS );
}
PROVIDE_PCIAPI_INLINE ( direct, pci_can_probe );
PROVIDE_PCIAPI_INLINE ( direct, pci_discover );
PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_byte );
PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_word );

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

@ -33,6 +33,9 @@ struct x86_features {
/** CPUID extended function */
#define CPUID_EXTENDED 0x80000000UL
/** CPUID hypervisor function */
#define CPUID_HYPERVISOR 0x40000000UL
/** Get vendor ID and largest standard function */
#define CPUID_VENDOR_ID 0x00000000UL

View File

@ -32,6 +32,16 @@ extern int pcibios_read ( struct pci_device *pci, uint32_t command,
extern int pcibios_write ( struct pci_device *pci, uint32_t command,
uint32_t value );
/**
* Check if PCI bus probing is allowed
*
* @ret ok Bus probing is allowed
*/
static inline __always_inline int
PCIAPI_INLINE ( pcbios, pci_can_probe ) ( void ) {
return 1;
}
/**
* Read byte from PCI configuration space via PCI BIOS
*

View File

@ -15,4 +15,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define PCIAPI_PREFIX_cloud __cloud_
#endif
/**
* Check if PCI bus probing is allowed
*
* @ret ok Bus probing is allowed
*/
static inline __always_inline int
PCIAPI_INLINE ( cloud, pci_can_probe ) ( void ) {
return 1;
}
#endif /* _IPXE_PCICLOUD_H */

View File

@ -25,6 +25,16 @@ struct pci_device;
extern void pcidirect_prepare ( struct pci_device *pci, int where );
/**
* Check if PCI bus probing is allowed
*
* @ret ok Bus probing is allowed
*/
static inline __always_inline int
PCIAPI_INLINE ( direct, pci_can_probe ) ( void ) {
return 1;
}
/**
* Find next PCI bus:dev.fn address range in system
*

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

@ -250,8 +250,10 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
/* CODE_DEFAULT: restore default .code32/.code64 directive */
#ifdef __x86_64__
#define CODE_DEFAULT ".code64"
#define STACK_DEFAULT "q"
#else
#define CODE_DEFAULT ".code32"
#define STACK_DEFAULT "l"
#endif
/* LINE_SYMBOL: declare a symbol for the current source code line */
@ -268,7 +270,7 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
/* REAL_CODE: declare a fragment of code that executes in real mode */
#define REAL_CODE( asm_code_str ) \
"push $1f\n\t" \
"push" STACK_DEFAULT " $1f\n\t" \
"call real_call\n\t" \
TEXT16_CODE ( "\n1:\n\t" \
asm_code_str \
@ -277,7 +279,7 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
/* PHYS_CODE: declare a fragment of code that executes in flat physical mode */
#define PHYS_CODE( asm_code_str ) \
"push $1f\n\t" \
"push" STACK_DEFAULT " $1f\n\t" \
"call phys_call\n\t" \
".section \".text.phys\", \"ax\", @progbits\n\t"\
"\n" LINE_SYMBOL "\n\t" \
@ -472,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

@ -44,11 +44,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @v smbios SMBIOS entry point descriptor structure to fill in
* @ret rc Return status code
*/
static int bios_find_smbios ( struct smbios *smbios ) {
static int bios_find_smbios2 ( struct smbios *smbios ) {
struct smbios_entry entry;
int rc;
/* Scan through BIOS segment to find SMBIOS entry point */
/* Scan through BIOS segment to find SMBIOS 32-bit entry point */
if ( ( rc = find_smbios_entry ( real_to_user ( BIOS_SEG, 0 ), 0x10000,
&entry ) ) != 0 )
return rc;
@ -62,4 +62,55 @@ static int bios_find_smbios ( struct smbios *smbios ) {
return 0;
}
/**
* Find SMBIOS
*
* @v smbios SMBIOS entry point descriptor structure to fill in
* @ret rc Return status code
*/
static int bios_find_smbios3 ( struct smbios *smbios ) {
struct smbios3_entry entry;
int rc;
/* Scan through BIOS segment to find SMBIOS 64-bit entry point */
if ( ( rc = find_smbios3_entry ( real_to_user ( BIOS_SEG, 0 ), 0x10000,
&entry ) ) != 0 )
return rc;
/* Check that address is accessible */
if ( entry.smbios_address > ~( ( physaddr_t ) 0 ) ) {
DBG ( "SMBIOS3 at %08llx is inaccessible\n",
( ( unsigned long long ) entry.smbios_address ) );
return -ENOTSUP;
}
/* Fill in entry point descriptor structure */
smbios->address = phys_to_user ( entry.smbios_address );
smbios->len = entry.smbios_len;
smbios->count = 0;
smbios->version = SMBIOS_VERSION ( entry.major, entry.minor );
return 0;
}
/**
* Find SMBIOS
*
* @v smbios SMBIOS entry point descriptor structure to fill in
* @ret rc Return status code
*/
static int bios_find_smbios ( struct smbios *smbios ) {
int rc;
/* Use 32-bit table if present */
if ( ( rc = bios_find_smbios2 ( smbios ) ) == 0 )
return 0;
/* Otherwise, use 64-bit table if present and accessible */
if ( ( rc = bios_find_smbios3 ( smbios ) ) == 0 )
return 0;
return rc;
}
PROVIDE_SMBIOS ( pcbios, find_smbios, bios_find_smbios );

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

@ -120,6 +120,7 @@ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){
return ( status >> 8 );
}
PROVIDE_PCIAPI_INLINE ( pcbios, pci_can_probe );
PROVIDE_PCIAPI ( pcbios, pci_discover, pcibios_discover );
PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_byte );
PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_word );

View File

@ -148,6 +148,7 @@ static void * pcicloud_ioremap ( struct pci_device *pci,
return pcicloud->pci_ioremap ( pci, bus_addr, len );
}
PROVIDE_PCIAPI_INLINE ( cloud, pci_can_probe );
PROVIDE_PCIAPI ( cloud, pci_discover, pcicloud_discover );
PROVIDE_PCIAPI ( cloud, pci_read_config_byte, pcicloud_read_config_byte );
PROVIDE_PCIAPI ( cloud, pci_read_config_word, pcicloud_read_config_word );
@ -165,24 +166,27 @@ static void pcicloud_init ( void ) {
static struct pci_api *apis[] = {
&ecam_api, &pcibios_api, &pcidirect_api
};
struct pci_range range;
struct pci_device pci;
uint32_t busdevfn;
unsigned int i;
int rc;
/* Select first API that successfully discovers an address range */
/* Select first API that successfully discovers a PCI device */
for ( i = 0 ; i < ( sizeof ( apis ) / sizeof ( apis[0] ) ) ; i++ ) {
pcicloud = apis[i];
pcicloud_discover ( 0, &range );
if ( range.count != 0 ) {
DBGC ( pcicloud, "PCICLOUD selected %s API\n",
pcicloud->name );
break;
busdevfn = 0;
if ( ( rc = pci_find_next ( &pci, &busdevfn ) ) == 0 ) {
DBGC ( pcicloud, "PCICLOUD selected %s API (found "
PCI_FMT ")\n", pcicloud->name,
PCI_ARGS ( &pci ) );
return;
}
}
/* The PCI direct API can never fail discovery since the range
* is hardcoded.
*/
assert ( range.count != 0 );
/* Fall back to using final attempted API if no devices found */
pcicloud = apis[ i - 1 ];
DBGC ( pcicloud, "PCICLOUD selected %s API (nothing detected)\n",
pcicloud->name );
}
/** Cloud VM PCI configuration space access initialisation function */

View File

@ -375,9 +375,10 @@ int pxe_start_nbp ( void ) {
* Notify BIOS of existence of network device
*
* @v netdev Network device
* @v priv Private data
* @ret rc Return status code
*/
static int pxe_notify ( struct net_device *netdev ) {
static int pxe_notify ( struct net_device *netdev, void *priv __unused ) {
/* Do nothing if we already have a network device */
if ( pxe_netdev )

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

@ -207,65 +207,35 @@ struct init_fn guestinfo_init_fn __init_fn ( INIT_NORMAL ) = {
* Create per-netdevice GuestInfo settings
*
* @v netdev Network device
* @v priv Private data
* @ret rc Return status code
*/
static int guestinfo_net_probe ( struct net_device *netdev ) {
struct settings *settings;
static int guestinfo_net_probe ( struct net_device *netdev, void *priv ) {
struct settings *settings = priv;
int rc;
/* Do nothing unless we have a GuestInfo channel available */
if ( guestinfo_channel < 0 )
return 0;
/* Allocate and initialise settings block */
settings = zalloc ( sizeof ( *settings ) );
if ( ! settings ) {
rc = -ENOMEM;
goto err_alloc;
}
settings_init ( settings, &guestinfo_settings_operations, NULL, NULL );
/* Register settings */
/* Initialise and register settings */
settings_init ( settings, &guestinfo_settings_operations,
&netdev->refcnt, NULL );
if ( ( rc = register_settings ( settings, netdev_settings ( netdev ),
"vmware" ) ) != 0 ) {
DBGC ( settings, "GuestInfo %p could not register for %s: %s\n",
settings, netdev->name, strerror ( rc ) );
goto err_register;
return rc;
}
DBGC ( settings, "GuestInfo %p registered for %s\n",
settings, netdev->name );
return 0;
err_register:
free ( settings );
err_alloc:
return rc;
}
/**
* Remove per-netdevice GuestInfo settings
*
* @v netdev Network device
*/
static void guestinfo_net_remove ( struct net_device *netdev ) {
struct settings *parent = netdev_settings ( netdev );
struct settings *settings;
list_for_each_entry ( settings, &parent->children, siblings ) {
if ( settings->op == &guestinfo_settings_operations ) {
DBGC ( settings, "GuestInfo %p unregistered for %s\n",
settings, netdev->name );
unregister_settings ( settings );
free ( settings );
return;
}
}
}
/** GuestInfo per-netdevice driver */
struct net_driver guestinfo_net_driver __net_driver = {
.name = "GuestInfo",
.priv_len = sizeof ( struct settings ),
.probe = guestinfo_net_probe,
.remove = guestinfo_net_remove,
};

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:

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