diff --git a/Changelog.md b/Changelog.md
index 741ab07d2a1..802714c6060 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -6,6 +6,8 @@ OpenCore Changelog
- Added Arrow Lake CPU detection
- Fixed Raptor Lake CPU detection
- Supported booting with TuneD in Fedora 41 in OpenLinuxBoot
+- Added OpenNetworkBoot driver to support HTTP(S) and PXE boot
+- Supported DMG loading and verification (e.g. macOS Recovery) over HTTP(S) boot
#### v1.0.2
- Fixed error in macrecovery when running headless, thx @mkorje
diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex
index 707439e05b3..5824f56fd37 100755
--- a/Docs/Configuration.tex
+++ b/Docs/Configuration.tex
@@ -4119,6 +4119,7 @@ \subsection{Debug Properties}\label{miscdebugprops}
\item \texttt{HDA} --- AudioDxe
\item \texttt{KKT} --- KeyTester
\item \texttt{LNX} --- OpenLinuxBoot
+ \item \texttt{NTBT} --- OpenNetworkBoot
\item \texttt{MMDD} --- MmapDump
\item \texttt{OCPAVP} --- PavpProvision
\item \texttt{OCRST} --- ResetSystem
@@ -6579,6 +6580,9 @@ \subsection{Drivers}\label{uefidrivers}
& \hyperref[uefilinux]{OpenCore plugin} implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}
to allow direct detection and booting of Linux distributions from OpenCore, without
chainloading via GRUB. \\
+\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenNetworkBoot}}\textbf{*}
+& \hyperref[uefipxe]{OpenCore plugin} implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}
+ to show available PXE and HTTP(S) boot options on the OpenCore boot menu. \\
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenNtfsDxe}}\textbf{*}
& New Technologies File System (NTFS) read-only driver.
NTFS is the primary file system for Microsoft Windows versions that are based on Windows NT. \\
@@ -7092,9 +7096,141 @@ \subsubsection{Additional information}
therefore \texttt{efibootmgr} rather than \texttt{bootctl} must be used for any low-level Linux command line interaction
with the boot menu.
+\subsection{OpenNetworkBoot}\label{uefipxe}
+
+OpenNetworkBoot is an OpenCore plugin implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}.
+It enables PXE and HTTP(S) Boot options in the OpenCore menu if these
+are supported by the underlying firmware, or if the required network boot drivers
+have been loaded using OpenCore.
+
+It has additional support for loading \texttt{.dmg} files and their associated
+\texttt{.chunklist} file over HTTP(S) Boot, allowing macOS recovery to be
+started over HTTP(S) Boot: if either extension is seen in the HTTP(S) Boot URI
+then the other file of the pair is automatically loaded as well, and both are
+passed to OpenCore to verify and boot from the DMG file.
+
+PXE Boot is already supported on most firmware, so in most cases PXE Boot entries
+should appear as soon as the driver is loaded. Using the additional network boot
+drivers provided with OpenCore, when needed, HTTP(S) Boot should be available on
+most firmware even if not natively supported.
+
+Detailed information about the available network boot drivers and how to configure
+PXE and HTTP(S) Boot is provided on
+\href{https://github.com/acidanthera/OpenCorePkg/blob/master/Platform/OpenNetworkBoot/README.md}{this page}.
+
+The following configuration options may be specified in the \texttt{Arguments} section for this driver:
+
+\begin{itemize}
+ \item \texttt{-4} - Boolean flag, enabled if present. \medskip
+
+ If specified enable IPv4 for PXE and HTTP(S) Boot. Disable IPV6
+ unless the \texttt{-6} flag is also present. If neither flag is
+ present, both are enabled by default. \medskip
+
+ \item \texttt{-6} - Boolean flag, enabled if present. \medskip
+
+ If specified enable IPv6 for PXE and HTTP(S) Boot. Disable IPV4
+ unless the \texttt{-4} flag is also present. If neither flag is
+ present, both are enabled by default. \medskip
+
+ \item \texttt{-{}-aux} - Boolean flag, enabled if present. \medskip
+
+ If specified the driver will generate auxiliary boot entries. \medskip
+
+ \item \texttt{-{}-delete-all-certs[:\{OWNER\_GUID\}]} - Default: not set. \medskip
+
+ If specified, delete all certificates present for \texttt{OWNER\_GUID}.
+ \texttt{OWNER\_GUID} is optional, and will default to all zeros if not specified. \medskip
+
+ \item \texttt{-{}-delete-cert[:\{OWNER\_GUID\}]="\{cert-text\}"} - Default: not set. \medskip
+
+ If specified, delete the given certificate(s) for HTTPS Boot. The certificate(s) can be specified
+ as a multi-line PEM value between double quotes.
+ \texttt{OWNER\_GUID} is optional, and will default to all zeros if not specified.
+ A single PEM file can contain one or more certicates.
+ Multiple instances of this option can be used to delete multiple different
+ PEM files, if required.
+
+ \item \texttt{-{}-enroll-cert[:\{OWNER\_GUID\}]="\{cert-text\}"} - Default: not set. \medskip
+
+ If specified, enroll the given certificate(s) for HTTPS Boot. The certificate(s) can be specified
+ as a multi-line PEM value between double quotes.
+ \texttt{OWNER\_GUID} is optional, and will default to all zeros if not specified.
+ A single PEM file can contain one or more certicates.
+ Multiple instances of this option can be used to enroll multiple different
+ PEM files, if required. \medskip
+
+ \item \texttt{-{}-http} - Boolean flag, enabled if present. \medskip
+
+ If specified enable HTTP(S) Boot. Disable PXE Boot unless
+ the \texttt{-{}-pxe} flag is also present. If neither flag is
+ present, both are enabled by default. \medskip
+
+ \item \texttt{-{}-https} - Boolean flag, enabled if present. \medskip
+
+ If enabled, allow only \texttt{https://} URIs for HTTP(S) Boot.
+ Additionally has the same behaviour as the \texttt{-{}-http} flag. \medskip
+
+ \item \texttt{-{}-pxe} - Boolean flag, enabled if present. \medskip
+
+ If specified enable PXE Boot, and disable HTTP(S) Boot unless
+ the \texttt{-{}-http} or \texttt{-{}-https} flags are present.
+ If none of these flags are present, both PXE and HTTP(S) Boot are
+ enabled by default. \medskip
+
+ \item \texttt{-{}-uri} - String value, no default. \medskip
+
+ If present, specify the URI to use for HTTP(S) Boot. If not present then
+ DHCP boot options must be enabled on the network in order for HTTP(S)
+ Boot to know what to boot.
+
+\end{itemize} \medskip
+
+\subsubsection{OpenNetworkBoot Certificate Management}
+
+Certificates are enrolled to NVRAM storage, therefore once
+a certificate has been enrolled, it will remain enrolled even if the \texttt{-{}-enroll-cert} config
+option is removed. \texttt{-{}-delete-cert} or \texttt{-{}-delete-all-certs}
+should be used to remove enrolled certificates.
+
+Checking for certificate presence by the \texttt{-{}-enroll-cert}
+and \texttt{-{}-delete-cert} options uses the simple algorithm
+of matching by exact file contents, not by file meaning. The intended
+usage is to leave an \texttt{-{}-enroll-cert} option present in the config
+file until it is time to delete it, e.g. after another more up-to-date
+\texttt{-{}-enroll-cert} option has been added and tested. At this point
+the user can change \texttt{-{}-enroll-cert} to \texttt{-{}-delete-cert}
+for the old certificate. \medskip
+
+Certificate options are processed one at a time, in
+order, and each will potentially make changes to the certificate NVRAM storage.
+However each option will not change the NVRAM store if it is already correct
+for the option at that point in time (e.g. will not enroll a certificate if it is
+already enrolled).
+Avoid combinations such as \texttt{-{}-delete-all-certs} followed by
+\texttt{-{}-enroll-cert}, as this will modify the NVRAM certificate
+storage twice on every boot. However a combination such as
+\texttt{-{}-delete-cert="\{certA-text\}"} followed by \texttt{-{}-enroll-cert="\{certB-text\}"}
+(with \texttt{certA-text} and \texttt{certB-text} different) is safe,
+because certA will only be deleted if it is present
+and certB will only be added if it is not present, therefore no
+NVRAM changes will be made on the second and subsequent boots
+with these options.
+
+In some cases (such as OVMF with https:// boot support) the
+\texttt{OpenNetworkBoot} certificate configuration options manage the same
+certificates as those seen in the firmware UI. In other cases of vendor customised
+HTTPS Boot firmware, the certificates managed by this driver will be
+separate from those managed by firmware.
+
+When using the debug version of this driver, the OpenCore debug log includes \texttt{NTBT:} entries
+that show which certificates are enrolled and removed by these options, and which
+certificates are present after all certificate configuration options have been processed.
+
\subsection{Other Boot Entry Protocol drivers}
-In addition to the \hyperref[uefilinux]{OpenLinuxBoot} plugin, the following \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}
+In addition to the \hyperref[uefilinux]{OpenLinuxBoot} and \hyperref[uefipxe]{OpenNetworkBoot} plugins,
+the following \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}
plugins are made available to add optional, configurable boot entries to the OpenCore boot picker.
\subsubsection{ResetNvramEntry}\label{uefiresetnvram}
diff --git a/Docs/Flavours.md b/Docs/Flavours.md
index 7ab4913596b..85407dc3b60 100644
--- a/Docs/Flavours.md
+++ b/Docs/Flavours.md
@@ -21,7 +21,7 @@ Icon pack authors are encouraged to provide only those icons for which there is
In the case of macOS only, a flavour based on the detected OS version is applied automatically (as shown below), and the user does not normally need to override this.
-For icon pack authors, the **Apple** icon is recommended, **AppleRecovery** and **AppleTM** are suggested, all others are entirely optional.
+For icon pack authors, the **Apple** icon is recommended, **AppleRecv** and **AppleTM** are suggested, all others are entirely optional.
- **Apple12:Apple** - Monterey (`Apple12.icns`)
- **Apple11:Apple** - Big Sur (`Apple11.icns`)
@@ -155,18 +155,31 @@ If providing `NVRAMTool.icns`, it should be themed so that it could be applied t
- **ResetNVRAM:NVRAMTool** - Reset NVRAM tool (`ResetNVRAM.icns`)
- This is the recommended flavour, used for the entry created by the `ResetNvramEntry.efi` driver.
- As another example of how flavours work: **ResetNVRAM:NVRAMTool** will look for `ResetNVRAM.icns`, then `NVRAMTool.icns` (and then, by OC default behaviour, `Tool.icns` then `HardDrive.icns`)
- - **Note**: Including **ResetNVRAM** anywhere in a user flavour triggers picker audio-assist and builtin label support for "Reset NVRAM"
+ - **Note**: Including **ResetNVRAM** anywhere in a flavour triggers picker audio-assist and builtin label support for "Reset NVRAM"
- **ToggleSIP:NVRAMTool** - Icon themed for Toggle SIP tool (`ToggleSIP.icns`)
- **ToggleSIP_Enabled:ToggleSIP:NVRAMTool** - Icon themed for Toggle SIP tool when SIP is enabled (system is protected)
- **ToggleSIP_Disabled:ToggleSIP:NVRAMTool** - Icon themed for Toggle SIP tool when SIP is disabled (system is unprotected)
- - **Note**: Including **ToggleSIP_Enabled** or **ToggleSIP_Disabled** anywhere in a user flavour triggers picker audio-assist and builtin label support for the two states of the Toggle SIP menu entry
+ - **Note**: Including **ToggleSIP_Enabled** or **ToggleSIP_Disabled** anywhere in a flavour triggers picker audio-assist and builtin label support for the two states of the Toggle SIP menu entry
+
+### Network Boot
+
+`OpenNetworkBoot.efi` uses the following flavours:
+
+ - **HttpBoot4:HttpBoot:NetworkBoot** - IPv4 HTTP(S) Boot
+ - **HttpBoot6:HttpBoot:NetworkBoot** - IPv6 HTTP(S) Boot
+ - **PxeBoot4:PxeBoot:NetworkBoot** - IPv4 PXE Boot
+ - **PxeBoot6:PxeBoot:NetworkBoot** - IPv6 PXE Boot
+
+If none of these icons are available, network boot is treated like an external OS, so the fallbacks are **Other** followed by **HardDrive**.
+
+ - **Note**: Including **NetworkBoot** anywhere in a flavour triggers picker audio-assist and builtin label support for "Network Boot"
### Other Tools
A list of other known tools which are common enough that some icon pack artists may wish to provide a standard icon for them:
- - **FirmwareSettings** - A boot menu entry for accessing firmware settings (`FirmwareSettings.icns`)
- - **Note**: Including **FirmwareSettings** anywhere in a user flavour triggers picker audio-assist and builtin label support for "Firmware Settings"
+ - **FirmwareSettings** - A boot menu entry for accessing firmware settings, such as generated by `FirmwareSettingsEntry.efi` (`FirmwareSettings.icns`)
+ - **Note**: Including **FirmwareSettings** anywhere in a flavour triggers picker audio-assist and builtin label support for "Firmware Settings"
- **MemTest** - A system memory testing tool such as that available from [memtest86.com](https://www.memtest86.com/) (`MemTest.icns`)
## Bootloaders
@@ -198,6 +211,7 @@ Provided by OcBinaryData. Used automatically by OC in some circumstances, if pro
- **ExtAppleTM** - Apple Time Machine (on external drive) (fallback: **ExtHardDrive**)
- **Shell** - Shell tool (fallback: **Tool**)
- **Tool** - Generic tool (fallback: **HardDrive**)
+ - **Other** - Other OS (fallback: **HardDrive**)
- **Windows** - Microsoft Windows (fallback: **HardDrive**)
### Additional Optional
diff --git a/Docs/Sample.plist b/Docs/Sample.plist
index 7ebd9181235..9cc7ffb5e38 100644
--- a/Docs/Sample.plist
+++ b/Docs/Sample.plist
@@ -1697,6 +1697,30 @@
Path
Ext4Dxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ RngDxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Hash2DxeCrypto.efi
+
Arguments
@@ -1781,6 +1805,66 @@
Path
Udp4Dxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Mtftp4Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Dhcp6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Ip6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Udp6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Mtftp6Dxe.efi
+
Arguments
@@ -1793,6 +1877,18 @@
Path
TcpDxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ UefiPxeBcDxe.efi
+
Arguments
@@ -1841,6 +1937,30 @@
Path
HttpBootDxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ TlsDxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ RamDiskDxe.efi
+
Arguments
diff --git a/Docs/SampleCustom.plist b/Docs/SampleCustom.plist
index d8841be15c8..63d1d0c00bb 100644
--- a/Docs/SampleCustom.plist
+++ b/Docs/SampleCustom.plist
@@ -2065,6 +2065,30 @@
Path
Ext4Dxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ RngDxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Hash2DxeCrypto.efi
+
Arguments
@@ -2149,6 +2173,66 @@
Path
Udp4Dxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Mtftp4Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Dhcp6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Ip6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Udp6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Mtftp6Dxe.efi
+
Arguments
@@ -2161,6 +2245,18 @@
Path
TcpDxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ UefiPxeBcDxe.efi
+
Arguments
@@ -2209,6 +2305,30 @@
Path
HttpBootDxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ TlsDxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ RamDiskDxe.efi
+
Arguments
diff --git a/Include/Acidanthera/Library/OcAppleDiskImageLib.h b/Include/Acidanthera/Library/OcAppleDiskImageLib.h
index c3174b3748e..78e24e1df0d 100644
--- a/Include/Acidanthera/Library/OcAppleDiskImageLib.h
+++ b/Include/Acidanthera/Library/OcAppleDiskImageLib.h
@@ -32,6 +32,17 @@ typedef struct {
APPLE_DISK_IMAGE_BLOCK_DATA **Blocks;
} OC_APPLE_DISK_IMAGE_CONTEXT;
+//
+// Disk image preload context, for network boot.
+//
+typedef struct {
+ OC_APPLE_DISK_IMAGE_CONTEXT *DmgContext;
+ EFI_FILE_PROTOCOL *DmgFile;
+ UINT32 DmgFileSize;
+ VOID *ChunklistBuffer;
+ UINT32 ChunklistFileSize;
+} OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT;
+
BOOLEAN
OcAppleDiskImageInitializeContext (
OUT OC_APPLE_DISK_IMAGE_CONTEXT *Context,
diff --git a/Include/Acidanthera/Library/OcBootManagementLib.h b/Include/Acidanthera/Library/OcBootManagementLib.h
index e1f717d7654..3023d1e1917 100644
--- a/Include/Acidanthera/Library/OcBootManagementLib.h
+++ b/Include/Acidanthera/Library/OcBootManagementLib.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -78,6 +79,10 @@ typedef struct OC_HOTKEY_CONTEXT_ OC_HOTKEY_CONTEXT;
#define OC_FLAVOUR_TOGGLE_SIP_ENABLED "ToggleSIP_Enabled:ToggleSIP:NVRAMTool"
#define OC_FLAVOUR_TOGGLE_SIP_DISABLED "ToggleSIP_Disabled:ToggleSIP:NVRAMTool"
#define OC_FLAVOUR_FIRMWARE_SETTINGS "FirmwareSettings"
+#define OC_FLAVOUR_HTTP_BOOT4 "HttpBoot4:HttpBoot:NetworkBoot"
+#define OC_FLAVOUR_HTTP_BOOT6 "HttpBoot6:HttpBoot:NetworkBoot"
+#define OC_FLAVOUR_PXE_BOOT4 "PxeBoot4:PxeBoot:NetworkBoot"
+#define OC_FLAVOUR_PXE_BOOT6 "PxeBoot6:PxeBoot:NetworkBoot"
#define OC_FLAVOUR_APPLE_OS "Apple"
#define OC_FLAVOUR_APPLE_RECOVERY "AppleRecv:Apple"
#define OC_FLAVOUR_APPLE_FW "AppleRecv:Apple"
@@ -92,6 +97,7 @@ typedef struct OC_HOTKEY_CONTEXT_ OC_HOTKEY_CONTEXT;
#define OC_FLAVOUR_ID_TOGGLE_SIP_ENABLED "ToggleSIP_Enabled"
#define OC_FLAVOUR_ID_TOGGLE_SIP_DISABLED "ToggleSIP_Disabled"
#define OC_FLAVOUR_ID_FIRMWARE_SETTINGS "FirmwareSettings"
+#define OC_FLAVOUR_ID_NETWORK_BOOT "NetworkBoot"
/**
Paths allowed to be accessible by the interfaces.
@@ -190,6 +196,139 @@ typedef enum OC_PICKER_MODE_ {
#define OC_KERN_CAPABILITY_K32_U32_U64 (OC_KERN_CAPABILITY_K32_U32 | OC_KERN_CAPABILITY_K32_U64)
#define OC_KERN_CAPABILITY_ALL (OC_KERN_CAPABILITY_K32_U32 | OC_KERN_CAPABILITY_K32_K64_U64)
+/**
+ Perform filtering based on file system basis.
+ Ignores all filesystems by default.
+ Remove this bit to allow any file system.
+**/
+#define OC_SCAN_FILE_SYSTEM_LOCK BIT0
+
+/**
+ Perform filtering based on device basis.
+ Ignores all devices by default.
+ Remove this bit to allow any device type.
+**/
+#define OC_SCAN_DEVICE_LOCK BIT1
+
+/**
+ Allow scanning APFS filesystems.
+**/
+#define OC_SCAN_ALLOW_FS_APFS BIT8
+
+/**
+ Allow scanning HFS filesystems.
+**/
+#define OC_SCAN_ALLOW_FS_HFS BIT9
+
+/**
+ Allow scanning ESP filesystems.
+**/
+#define OC_SCAN_ALLOW_FS_ESP BIT10
+
+/**
+ Allow scanning NTFS filesystems.
+**/
+#define OC_SCAN_ALLOW_FS_NTFS BIT11
+
+/**
+ Allow scanning Linux Root filesystems.
+ https://systemd.io/DISCOVERABLE_PARTITIONS/
+**/
+#define OC_SCAN_ALLOW_FS_LINUX_ROOT BIT12
+
+/**
+ Allow scanning Linux Data filesystems.
+ https://systemd.io/DISCOVERABLE_PARTITIONS/
+**/
+#define OC_SCAN_ALLOW_FS_LINUX_DATA BIT13
+
+/**
+ Allow scanning XBOOTLDR filesystems.
+**/
+#define OC_SCAN_ALLOW_FS_XBOOTLDR BIT14
+
+/**
+ Allow scanning SATA devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_SATA BIT16
+
+/**
+ Allow scanning SAS and Mac NVMe devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_SASEX BIT17
+
+/**
+ Allow scanning SCSI devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_SCSI BIT18
+
+/**
+ Allow scanning NVMe devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_NVME BIT19
+
+/**
+ Allow scanning ATAPI devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_ATAPI BIT20
+
+/**
+ Allow scanning USB devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_USB BIT21
+
+/**
+ Allow scanning FireWire devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_FIREWIRE BIT22
+
+/**
+ Allow scanning SD card devices.
+**/
+#define OC_SCAN_ALLOW_DEVICE_SDCARD BIT23
+
+/**
+ Allow scanning PCI devices (e.g. virtio).
+**/
+#define OC_SCAN_ALLOW_DEVICE_PCI BIT24
+
+/**
+ All device bits used by OC_SCAN_DEVICE_LOCK.
+**/
+#define OC_SCAN_DEVICE_BITS (\
+ OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \
+ OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \
+ OC_SCAN_ALLOW_DEVICE_ATAPI | OC_SCAN_ALLOW_DEVICE_USB | \
+ OC_SCAN_ALLOW_DEVICE_FIREWIRE | OC_SCAN_ALLOW_DEVICE_SDCARD | \
+ OC_SCAN_ALLOW_DEVICE_PCI)
+
+/**
+ All file system bits used by OC_SCAN_FILE_SYSTEM_LOCK.
+**/
+#define OC_SCAN_FILE_SYSTEM_BITS (\
+ OC_SCAN_ALLOW_FS_APFS | OC_SCAN_ALLOW_FS_HFS | OC_SCAN_ALLOW_FS_ESP | \
+ OC_SCAN_ALLOW_FS_NTFS | OC_SCAN_ALLOW_FS_LINUX_ROOT | \
+ OC_SCAN_ALLOW_FS_LINUX_DATA | OC_SCAN_ALLOW_FS_XBOOTLDR )
+
+/**
+ By default allow booting from APFS from internal drives.
+**/
+#define OC_SCAN_DEFAULT_POLICY (\
+ OC_SCAN_FILE_SYSTEM_LOCK | OC_SCAN_DEVICE_LOCK | \
+ OC_SCAN_ALLOW_FS_APFS | \
+ OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \
+ OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \
+ OC_SCAN_ALLOW_DEVICE_PCI)
+
+/**
+ OcLoadBootEntry DMG loading policy rules.
+**/
+typedef enum {
+ OcDmgLoadingDisabled,
+ OcDmgLoadingAnyImage,
+ OcDmgLoadingAppleSigned,
+} OC_DMG_LOADING_SUPPORT;
+
/**
Action to perform as part of executing a system boot entry.
**/
@@ -219,11 +358,44 @@ EFI_STATUS
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
);
+/**
+ Forward declaration of OC_BOOT_ENTRY structure.
+**/
+typedef struct OC_BOOT_ENTRY_ OC_BOOT_ENTRY;
+
+/**
+ Exposed custom entry load interface.
+ Returns allocated file buffer from pool on success.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *OC_CUSTOM_READ)(
+ IN OC_STORAGE_CONTEXT *Storage,
+ IN OC_BOOT_ENTRY *ChosenEntry,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
+ OUT EFI_HANDLE *StorageHandle,
+ OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ OUT VOID **CustomFreeContext
+ );
+
+/**
+ Exposed custom entry interface to free any custom items after load.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *OC_CUSTOM_FREE)(
+ IN VOID *CustomFreeContext
+ );
+
/**
Discovered boot entry.
Note, inner resources must be freed with FreeBootEntry.
**/
-typedef struct OC_BOOT_ENTRY_ {
+struct OC_BOOT_ENTRY_ {
//
// Link in entry list in OC_BOOT_FILESYSTEM.
//
@@ -246,6 +418,14 @@ typedef struct OC_BOOT_ENTRY_ {
//
OC_BOOT_UNMANAGED_GET_FINAL_DP UnmanagedBootGetFinalDevicePath;
//
+ // Custom entry image read routine, optional for non-custom entries.
+ //
+ OC_CUSTOM_READ CustomRead;
+ //
+ // Custom entry routine to free custom items. Optional.
+ //
+ OC_CUSTOM_FREE CustomFree;
+ //
// Id under which to save entry as default.
//
CHAR16 *Id;
@@ -332,7 +512,7 @@ typedef struct OC_BOOT_ENTRY_ {
// Audio base type for system action. Boot Entry Protocol only.
//
CHAR8 *AudioBaseType;
-} OC_BOOT_ENTRY;
+};
/**
Parsed load option or shell variable.
@@ -417,139 +597,6 @@ typedef struct OC_BOOT_CONTEXT_ {
OC_PICKER_CONTEXT *PickerContext;
} OC_BOOT_CONTEXT;
-/**
- Perform filtering based on file system basis.
- Ignores all filesystems by default.
- Remove this bit to allow any file system.
-**/
-#define OC_SCAN_FILE_SYSTEM_LOCK BIT0
-
-/**
- Perform filtering based on device basis.
- Ignores all devices by default.
- Remove this bit to allow any device type.
-**/
-#define OC_SCAN_DEVICE_LOCK BIT1
-
-/**
- Allow scanning APFS filesystems.
-**/
-#define OC_SCAN_ALLOW_FS_APFS BIT8
-
-/**
- Allow scanning HFS filesystems.
-**/
-#define OC_SCAN_ALLOW_FS_HFS BIT9
-
-/**
- Allow scanning ESP filesystems.
-**/
-#define OC_SCAN_ALLOW_FS_ESP BIT10
-
-/**
- Allow scanning NTFS filesystems.
-**/
-#define OC_SCAN_ALLOW_FS_NTFS BIT11
-
-/**
- Allow scanning Linux Root filesystems.
- https://systemd.io/DISCOVERABLE_PARTITIONS/
-**/
-#define OC_SCAN_ALLOW_FS_LINUX_ROOT BIT12
-
-/**
- Allow scanning Linux Data filesystems.
- https://systemd.io/DISCOVERABLE_PARTITIONS/
-**/
-#define OC_SCAN_ALLOW_FS_LINUX_DATA BIT13
-
-/**
- Allow scanning XBOOTLDR filesystems.
-**/
-#define OC_SCAN_ALLOW_FS_XBOOTLDR BIT14
-
-/**
- Allow scanning SATA devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_SATA BIT16
-
-/**
- Allow scanning SAS and Mac NVMe devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_SASEX BIT17
-
-/**
- Allow scanning SCSI devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_SCSI BIT18
-
-/**
- Allow scanning NVMe devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_NVME BIT19
-
-/**
- Allow scanning ATAPI devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_ATAPI BIT20
-
-/**
- Allow scanning USB devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_USB BIT21
-
-/**
- Allow scanning FireWire devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_FIREWIRE BIT22
-
-/**
- Allow scanning SD card devices.
-**/
-#define OC_SCAN_ALLOW_DEVICE_SDCARD BIT23
-
-/**
- Allow scanning PCI devices (e.g. virtio).
-**/
-#define OC_SCAN_ALLOW_DEVICE_PCI BIT24
-
-/**
- All device bits used by OC_SCAN_DEVICE_LOCK.
-**/
-#define OC_SCAN_DEVICE_BITS (\
- OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \
- OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \
- OC_SCAN_ALLOW_DEVICE_ATAPI | OC_SCAN_ALLOW_DEVICE_USB | \
- OC_SCAN_ALLOW_DEVICE_FIREWIRE | OC_SCAN_ALLOW_DEVICE_SDCARD | \
- OC_SCAN_ALLOW_DEVICE_PCI)
-
-/**
- All file system bits used by OC_SCAN_FILE_SYSTEM_LOCK.
-**/
-#define OC_SCAN_FILE_SYSTEM_BITS (\
- OC_SCAN_ALLOW_FS_APFS | OC_SCAN_ALLOW_FS_HFS | OC_SCAN_ALLOW_FS_ESP | \
- OC_SCAN_ALLOW_FS_NTFS | OC_SCAN_ALLOW_FS_LINUX_ROOT | \
- OC_SCAN_ALLOW_FS_LINUX_DATA | OC_SCAN_ALLOW_FS_XBOOTLDR )
-
-/**
- By default allow booting from APFS from internal drives.
-**/
-#define OC_SCAN_DEFAULT_POLICY (\
- OC_SCAN_FILE_SYSTEM_LOCK | OC_SCAN_DEVICE_LOCK | \
- OC_SCAN_ALLOW_FS_APFS | \
- OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \
- OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \
- OC_SCAN_ALLOW_DEVICE_PCI)
-
-/**
- OcLoadBootEntry DMG loading policy rules.
-**/
-typedef enum {
- OcDmgLoadingDisabled,
- OcDmgLoadingAnyImage,
- OcDmgLoadingAppleSigned,
-} OC_DMG_LOADING_SUPPORT;
-
/**
Exposed start interface with chosen boot entry but otherwise equivalent
to EFI_BOOT_SERVICES StartImage.
@@ -564,22 +611,6 @@ EFI_STATUS
IN BOOLEAN LaunchInText
);
-/**
- Exposed custom entry load interface.
- Returns allocated file buffer from pool on success.
-**/
-typedef
-EFI_STATUS
-(EFIAPI *OC_CUSTOM_READ)(
- IN OC_STORAGE_CONTEXT *Storage,
- IN OC_BOOT_ENTRY *ChosenEntry,
- OUT VOID **Data,
- OUT UINT32 *DataSize,
- OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
- OUT EFI_HANDLE *StorageHandle,
- OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath
- );
-
/**
Custom picker entry.
Note that OC_BOOT_ENTRY_PROTOCOL_REVISION needs incrementing
@@ -650,9 +681,17 @@ typedef struct {
//
OC_BOOT_UNMANAGED_GET_FINAL_DP UnmanagedBootGetFinalDevicePath;
//
- // Unmanaged boot Device Path. Boot Entry Protocol unmanaged boot entries only.
+ // Absolute Device Path. May be used instead of text Path above for Boot Entry Protocol entries. Optional.
+ //
+ EFI_DEVICE_PATH_PROTOCOL *UnmanagedDevicePath;
+ //
+ // Custom entry image read routine, optional for non-custom entries.
+ //
+ OC_CUSTOM_READ CustomRead;
+ //
+ // Custom entry routine to free custom items. Optional.
//
- EFI_DEVICE_PATH_PROTOCOL *UnmanagedBootDevicePath;
+ OC_CUSTOM_FREE CustomFree;
//
// Whether this entry should be labeled as external to the system. Boot Entry Protocol only. Optional.
//
@@ -925,7 +964,7 @@ struct OC_PICKER_CONTEXT_ {
//
BOOLEAN CustomBootGuid;
//
- // Custom entry reading routine, optional for no custom entries.
+ // Custom entry image read routine, optional for non-custom entries.
//
OC_CUSTOM_READ CustomRead;
//
@@ -1537,9 +1576,10 @@ typedef struct OC_BOOT_ARGUMENTS_ {
} OC_BOOT_ARGUMENTS;
//
-// Sanity check max. size for LoadOptions.
+// Sanity check max. size for LoadOptions. We need to pass PEM certificates
+// to some drivers (e.g. OpenNetworkBoot), so this has to be quite large.
//
-#define MAX_LOAD_OPTIONS_SIZE SIZE_4KB
+#define MAX_LOAD_OPTIONS_SIZE SIZE_16KB
/**
Are load options apparently valid (Unicode string or cleanly non-present)?
diff --git a/Include/Acidanthera/Protocol/OcAudio.h b/Include/Acidanthera/Protocol/OcAudio.h
index a06e59cfdcb..8aa8f4c5a41 100644
--- a/Include/Acidanthera/Protocol/OcAudio.h
+++ b/Include/Acidanthera/Protocol/OcAudio.h
@@ -56,6 +56,7 @@ typedef struct OC_AUDIO_PROTOCOL_ OC_AUDIO_PROTOCOL;
#define OC_VOICE_OVER_AUDIO_FILE_MAC_OS_RECOVERY "macOS_Recovery"
#define OC_VOICE_OVER_AUDIO_FILE_MAC_OS_TIME_MACHINE "macOS_TimeMachine"
#define OC_VOICE_OVER_AUDIO_FILE_MAC_OS_UPDATE_FW "macOS_UpdateFw"
+#define OC_VOICE_OVER_AUDIO_FILE_NETWORK_BOOT "NetworkBoot"
#define OC_VOICE_OVER_AUDIO_FILE_OTHER_OS "OtherOS"
#define OC_VOICE_OVER_AUDIO_FILE_PASSWORD_ACCEPTED "PasswordAccepted"
#define OC_VOICE_OVER_AUDIO_FILE_PASSWORD_INCORRECT "PasswordIncorrect"
diff --git a/Include/Acidanthera/Protocol/OcBootEntry.h b/Include/Acidanthera/Protocol/OcBootEntry.h
index 9ac8326c89a..2a3e261a70a 100644
--- a/Include/Acidanthera/Protocol/OcBootEntry.h
+++ b/Include/Acidanthera/Protocol/OcBootEntry.h
@@ -28,7 +28,7 @@
WARNING: This protocol is currently undergoing active design.
**/
-#define OC_BOOT_ENTRY_PROTOCOL_REVISION 5
+#define OC_BOOT_ENTRY_PROTOCOL_REVISION 6
/**
Forward declaration of OC_BOOT_ENTRY_PROTOCOL structure.
diff --git a/Library/OcBootManagementLib/BootAudio.c b/Library/OcBootManagementLib/BootAudio.c
index bdc4b4ad2a0..f539feb5939 100644
--- a/Library/OcBootManagementLib/BootAudio.c
+++ b/Library/OcBootManagementLib/BootAudio.c
@@ -214,7 +214,11 @@ OcPlayAudioEntry (
} else if (Entry->Type == OC_BOOT_WINDOWS) {
OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_WINDOWS, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE);
} else if (Entry->Type == OC_BOOT_EXTERNAL_OS) {
- OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_EXTERNAL_OS, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE);
+ if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_NETWORK_BOOT) != NULL) {
+ OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_NETWORK_BOOT, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE);
+ } else {
+ OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_EXTERNAL_OS, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE);
+ }
} else if (Entry->Type == OC_BOOT_SYSTEM) {
OcPlayAudioFile (Context, Entry->AudioBasePath, Entry->AudioBaseType, FALSE);
} else if (Entry->Type == OC_BOOT_EXTERNAL_TOOL) {
diff --git a/Library/OcBootManagementLib/BootEntryManagement.c b/Library/OcBootManagementLib/BootEntryManagement.c
index 4837ab74dc0..26c02174ee6 100644
--- a/Library/OcBootManagementLib/BootEntryManagement.c
+++ b/Library/OcBootManagementLib/BootEntryManagement.c
@@ -676,6 +676,8 @@ InternalAddBootEntryFromCustomEntry (
}
BootEntry->IsExternal = FileSystem->External;
+ BootEntry->CustomRead = CustomEntry->CustomRead;
+ BootEntry->CustomFree = CustomEntry->CustomFree;
if (CustomEntry->Id != NULL) {
BootEntry->Id = AsciiStrCopyToUnicode (CustomEntry->Id, 0);
@@ -692,7 +694,7 @@ InternalAddBootEntryFromCustomEntry (
return EFI_OUT_OF_RESOURCES;
}
- if (!CustomEntry->UnmanagedBootAction && !CustomEntry->SystemAction) {
+ if (!CustomEntry->UnmanagedBootAction && !CustomEntry->SystemAction && !CustomEntry->UnmanagedDevicePath) {
ASSERT (CustomEntry->Path != NULL);
PathName = AsciiStrCopyToUnicode (CustomEntry->Path, 0);
if (PathName == NULL) {
@@ -727,7 +729,7 @@ InternalAddBootEntryFromCustomEntry (
BootEntry->AudioBasePath = CustomEntry->AudioBasePath;
BootEntry->AudioBaseType = CustomEntry->AudioBaseType;
BootEntry->IsExternal = CustomEntry->External;
- BootEntry->DevicePath = DuplicateDevicePath (CustomEntry->UnmanagedBootDevicePath);
+ BootEntry->DevicePath = DuplicateDevicePath (CustomEntry->UnmanagedDevicePath);
if (BootEntry->DevicePath == NULL) {
FreeBootEntry (BootEntry);
@@ -739,7 +741,10 @@ InternalAddBootEntryFromCustomEntry (
BootEntry->AudioBasePath = CustomEntry->AudioBasePath;
BootEntry->AudioBaseType = CustomEntry->AudioBaseType;
} else if (CustomEntry->Tool) {
- BootEntry->Type = OC_BOOT_EXTERNAL_TOOL;
+ ASSERT (CustomEntry->CustomRead == NULL && CustomEntry->CustomFree == NULL);
+ BootEntry->Type = OC_BOOT_EXTERNAL_TOOL;
+ BootEntry->CustomRead = BootContext->PickerContext->CustomRead;
+ BootEntry->CustomFree = NULL;
UnicodeUefiSlashes (PathName);
BootEntry->PathName = PathName;
} else {
@@ -750,13 +755,19 @@ InternalAddBootEntryFromCustomEntry (
// for user entry path is absolute device path.
//
if (IsBootEntryProtocol) {
- UnicodeUefiSlashes (PathName);
- BootEntry->DevicePath = FileDevicePath (FileSystem->Handle, PathName);
+ if (CustomEntry->UnmanagedDevicePath) {
+ BootEntry->DevicePath = DuplicateDevicePath (CustomEntry->UnmanagedDevicePath);
+ } else {
+ UnicodeUefiSlashes (PathName);
+ BootEntry->DevicePath = FileDevicePath (FileSystem->Handle, PathName);
+ FreePool (PathName);
+ }
} else {
+ ASSERT (CustomEntry->UnmanagedDevicePath == NULL);
BootEntry->DevicePath = ConvertTextToDevicePath (PathName);
+ FreePool (PathName);
}
- FreePool (PathName);
if (BootEntry->DevicePath == NULL) {
FreeBootEntry (BootEntry);
return EFI_OUT_OF_RESOURCES;
@@ -770,22 +781,24 @@ InternalAddBootEntryFromCustomEntry (
)
);
if (FilePath == NULL) {
- DEBUG ((
- DEBUG_WARN,
- "OCB: Invalid device path, not adding entry %a\n",
- CustomEntry->Name
- ));
- FreeBootEntry (BootEntry);
- return EFI_UNSUPPORTED;
- }
-
- BootEntry->PathName = AllocateCopyPool (
- OcFileDevicePathNameSize (FilePath),
- FilePath->PathName
- );
- if (BootEntry->PathName == NULL) {
- FreeBootEntry (BootEntry);
- return EFI_OUT_OF_RESOURCES;
+ if (BootEntry->CustomRead == NULL) {
+ DEBUG ((
+ DEBUG_WARN,
+ "OCB: Invalid device path, not adding entry %a\n",
+ CustomEntry->Name
+ ));
+ FreeBootEntry (BootEntry);
+ return EFI_UNSUPPORTED;
+ }
+ } else {
+ BootEntry->PathName = AllocateCopyPool (
+ OcFileDevicePathNameSize (FilePath),
+ FilePath->PathName
+ );
+ if (BootEntry->PathName == NULL) {
+ FreeBootEntry (BootEntry);
+ return EFI_OUT_OF_RESOURCES;
+ }
}
//
@@ -843,7 +856,7 @@ InternalAddBootEntryFromCustomEntry (
BootEntry->ExposeDevicePath = CustomEntry->RealPath;
BootEntry->FullNvramAccess = CustomEntry->FullNvramAccess;
- if ((BootEntry->UnmanagedBootAction != NULL) || (BootEntry->SystemAction != NULL)) {
+ if ((BootEntry->UnmanagedBootAction != NULL) || (BootEntry->SystemAction != NULL) || (CustomEntry->CustomRead != NULL)) {
ASSERT (CustomEntry->Arguments == NULL);
} else {
ASSERT (CustomEntry->Arguments != NULL);
@@ -1464,6 +1477,7 @@ AddBootEntryFromBootOption (
);
} while (NumPatchedNodes > 0);
+ Status = EFI_NOT_FOUND;
if ((ExpandedDevicePath == NULL) && (CustomFileSystem != NULL)) {
//
// If non-standard device path, attempt to pre-construct a user config
@@ -1484,12 +1498,12 @@ AddBootEntryFromBootOption (
*CustomIndex = Index;
}
- InternalAddBootEntryFromCustomEntry (
- BootContext,
- CustomFileSystem,
- &BootContext->PickerContext->CustomEntries[Index],
- FALSE
- );
+ Status = InternalAddBootEntryFromCustomEntry (
+ BootContext,
+ CustomFileSystem,
+ &BootContext->PickerContext->CustomEntries[Index],
+ FALSE
+ );
break;
}
}
@@ -1503,6 +1517,36 @@ AddBootEntryFromBootOption (
EntryProtocolDevPath = InternalGetOcEntryProtocolDevPath (DevicePath);
+ if (EntryProtocolDevPath != NULL) {
+ //
+ // Zero GUID can be non-file-based entry (e.g. from network boot),
+ // or file-based entry on OVMF mounted drives where GPT GUIDs are
+ // not available. Try non-file-based first.
+ //
+ if (CompareGuid (&gEfiPartTypeUnusedGuid, &EntryProtocolDevPath->Partuuid)) {
+ Status = OcAddEntriesFromBootEntryProtocol (
+ BootContext,
+ CustomFileSystem,
+ EntryProtocolHandles,
+ EntryProtocolHandleCount,
+ EntryProtocolDevPath->EntryName.PathName,
+ TRUE,
+ FALSE
+ );
+ if (!EFI_ERROR (Status)) {
+ if (EntryProtocolPartuuid != NULL) {
+ CopyGuid (EntryProtocolPartuuid, &gEfiPartTypeUnusedGuid);
+ }
+
+ if (EntryProtocolId != NULL) {
+ *EntryProtocolId = AllocateCopyPool (StrSize (EntryProtocolDevPath->EntryName.PathName), EntryProtocolDevPath->EntryName.PathName);
+ }
+
+ EntryProtocolDevPath = NULL;
+ }
+ }
+ }
+
if (EntryProtocolDevPath != NULL) {
//
// Search for ID on matching device only.
@@ -1574,7 +1618,7 @@ AddBootEntryFromBootOption (
DevicePath = ExpandedDevicePath;
if (DevicePath == NULL) {
- return EFI_NOT_FOUND;
+ return Status;
}
} else if (NumPatchedNodes == -1) {
//
@@ -2511,6 +2555,7 @@ OcLoadBootEntry (
EFI_STATUS Status;
EFI_HANDLE EntryHandle;
INTERNAL_DMG_LOAD_CONTEXT DmgLoadContext;
+ VOID *CustomFreeContext;
if ((BootEntry->Type & OC_BOOT_UNMANAGED) != 0) {
ASSERT (BootEntry->UnmanagedBootAction != NULL);
@@ -2527,7 +2572,8 @@ OcLoadBootEntry (
BootEntry,
ParentHandle,
&EntryHandle,
- &DmgLoadContext
+ &DmgLoadContext,
+ &CustomFreeContext
);
if (!EFI_ERROR (Status)) {
//
@@ -2546,14 +2592,26 @@ OcLoadBootEntry (
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "OCB: StartImage failed - %r\n", Status));
//
- // Unload dmg if any.
- //
- InternalUnloadDmg (&DmgLoadContext);
- //
// Unload image.
+ // Note: This is not needed on success, since this has already been done
+ // and image handle is now invalid, if image was an application and it
+ // exited successfully:
+ // https://github.com/tianocore/edk2/blob/a3aab12c34dba35d1fd592f4939cb70617668f7e/MdeModulePkg/Core/Dxe/Image/Image.c#L1789-L1793
//
gBS->UnloadImage (EntryHandle);
}
+
+ //
+ // Unload dmg if any.
+ //
+ InternalUnloadDmg (&DmgLoadContext);
+ //
+ // Unload any entry protocol custom items.
+ // For instance HTTP Boot natively supported RAM disk, on loading .iso or .img.
+ //
+ if (BootEntry->CustomFree != NULL) {
+ BootEntry->CustomFree (CustomFreeContext);
+ }
} else {
DEBUG ((DEBUG_WARN, "OCB: LoadImage failed - %r\n", Status));
}
diff --git a/Library/OcBootManagementLib/BootEntryProtocol.c b/Library/OcBootManagementLib/BootEntryProtocol.c
index 6bcde243c36..6d463f81589 100644
--- a/Library/OcBootManagementLib/BootEntryProtocol.c
+++ b/Library/OcBootManagementLib/BootEntryProtocol.c
@@ -248,6 +248,10 @@ OcAddEntriesFromBootEntryProtocol (
{
BEP_ADD_ENTRIES_CONTEXT AddEntriesContext;
+ //
+ // May be CustomFileSystem, but not NULL.
+ //
+ ASSERT (FileSystem != NULL);
ASSERT (!CreateDefault || (DefaultEntryId != NULL));
AddEntriesContext.ReturnStatus = EFI_NOT_FOUND;
diff --git a/Library/OcBootManagementLib/BootManagementInternal.h b/Library/OcBootManagementLib/BootManagementInternal.h
index 1a3ac19387a..0e156c2725d 100644
--- a/Library/OcBootManagementLib/BootManagementInternal.h
+++ b/Library/OcBootManagementLib/BootManagementInternal.h
@@ -133,8 +133,9 @@ InternalCheckScanPolicy (
EFI_DEVICE_PATH_PROTOCOL *
InternalLoadDmg (
- IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
- IN OC_DMG_LOADING_SUPPORT DmgLoading
+ IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext
);
VOID
@@ -177,7 +178,8 @@ InternalLoadBootEntry (
IN OC_BOOT_ENTRY *BootEntry,
IN EFI_HANDLE ParentHandle,
OUT EFI_HANDLE *EntryHandle,
- OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext
+ OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext,
+ OUT VOID **CustomFreeContext
);
UINT16 *
diff --git a/Library/OcBootManagementLib/DefaultEntryChoice.c b/Library/OcBootManagementLib/DefaultEntryChoice.c
index d45c9b4c248..6d38bd2fce1 100644
--- a/Library/OcBootManagementLib/DefaultEntryChoice.c
+++ b/Library/OcBootManagementLib/DefaultEntryChoice.c
@@ -1529,19 +1529,21 @@ InternalLoadBootEntry (
IN OC_BOOT_ENTRY *BootEntry,
IN EFI_HANDLE ParentHandle,
OUT EFI_HANDLE *EntryHandle,
- OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext
+ OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext,
+ OUT VOID **CustomFreeContext
)
{
- EFI_STATUS Status;
- EFI_STATUS OptionalStatus;
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- EFI_HANDLE StorageHandle;
- EFI_DEVICE_PATH_PROTOCOL *StoragePath;
- CHAR16 *UnicodeDevicePath;
- EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
- VOID *EntryData;
- UINT32 EntryDataSize;
- CONST CHAR8 *Args;
+ EFI_STATUS Status;
+ EFI_STATUS OptionalStatus;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE StorageHandle;
+ EFI_DEVICE_PATH_PROTOCOL *StoragePath;
+ CHAR16 *UnicodeDevicePath;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ VOID *EntryData;
+ UINT32 EntryDataSize;
+ CONST CHAR8 *Args;
+ OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT DmgPreloadContext;
ASSERT (BootEntry != NULL);
//
@@ -1558,38 +1560,57 @@ InternalLoadBootEntry (
ZeroMem (DmgLoadContext, sizeof (*DmgLoadContext));
- EntryData = NULL;
- EntryDataSize = 0;
- StorageHandle = NULL;
- StoragePath = NULL;
+ EntryData = NULL;
+ EntryDataSize = 0;
+ StorageHandle = NULL;
+ StoragePath = NULL;
+ *CustomFreeContext = NULL;
+ ZeroMem (&DmgPreloadContext, sizeof (DmgPreloadContext));
- if (BootEntry->IsFolder) {
+ //
+ // CustomRead must be set for external tools, but may also be set for boot
+ // entry protocol entries.
+ //
+ ASSERT (BootEntry->Type != OC_BOOT_EXTERNAL_TOOL || BootEntry->CustomRead != NULL);
+
+ if (BootEntry->CustomRead != NULL) {
+ Status = BootEntry->CustomRead (
+ Context->StorageContext,
+ BootEntry,
+ &EntryData,
+ &EntryDataSize,
+ &DevicePath,
+ &StorageHandle,
+ &StoragePath,
+ Context->DmgLoading,
+ &DmgPreloadContext,
+ CustomFreeContext
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "OCB: Custom read failed - %r\n", Status));
+ return Status;
+ }
+ }
+
+ if ( (DmgPreloadContext.DmgFile != NULL)
+ || (DmgPreloadContext.DmgContext != NULL)
+ || BootEntry->IsFolder)
+ {
if (Context->DmgLoading == OcDmgLoadingDisabled) {
return EFI_SECURITY_VIOLATION;
}
DmgLoadContext->DevicePath = BootEntry->DevicePath;
- DevicePath = InternalLoadDmg (DmgLoadContext, Context->DmgLoading);
+ DevicePath = InternalLoadDmg (
+ DmgLoadContext,
+ Context->DmgLoading,
+ &DmgPreloadContext
+ );
if (DevicePath == NULL) {
return EFI_UNSUPPORTED;
}
- } else if (BootEntry->Type == OC_BOOT_EXTERNAL_TOOL) {
- ASSERT (Context->CustomRead != NULL);
-
- Status = Context->CustomRead (
- Context->StorageContext,
- BootEntry,
- &EntryData,
- &EntryDataSize,
- &DevicePath,
- &StorageHandle,
- &StoragePath
- );
-
- if (EFI_ERROR (Status)) {
- return Status;
- }
- } else {
+ } else if (BootEntry->CustomRead == NULL) {
DevicePath = BootEntry->DevicePath;
}
@@ -1691,6 +1712,9 @@ InternalLoadBootEntry (
}
} else {
InternalUnloadDmg (DmgLoadContext);
+ if (BootEntry->CustomFree != NULL) {
+ BootEntry->CustomFree (*CustomFreeContext);
+ }
}
return Status;
diff --git a/Library/OcBootManagementLib/DmgBootSupport.c b/Library/OcBootManagementLib/DmgBootSupport.c
index e0a717de6aa..e2b31ff6caa 100644
--- a/Library/OcBootManagementLib/DmgBootSupport.c
+++ b/Library/OcBootManagementLib/DmgBootSupport.c
@@ -317,8 +317,9 @@ InternalFindDmgChunklist (
EFI_DEVICE_PATH_PROTOCOL *
InternalLoadDmg (
- IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
- IN OC_DMG_LOADING_SUPPORT DmgLoading
+ IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext
)
{
EFI_DEVICE_PATH_PROTOCOL *DevPath;
@@ -342,130 +343,154 @@ InternalLoadDmg (
ASSERT (Context != NULL);
- DevPath = Context->DevicePath;
- Status = OcOpenFileByDevicePath (
- &DevPath,
- &DmgDir,
- EFI_FILE_MODE_READ,
- EFI_FILE_DIRECTORY
- );
- if (EFI_ERROR (Status)) {
- DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE);
- DEBUG ((DEBUG_INFO, "OCB: Failed to open DMG directory %s\n", DevPathText));
- if (DevPathText != NULL) {
- FreePool (DevPathText);
- }
-
- return NULL;
- }
+ if (DmgPreloadContext->DmgContext != NULL) {
+ Context->DmgContext = DmgPreloadContext->DmgContext;
+ DmgFileSize = DmgPreloadContext->DmgFileSize;
+ } else {
+ if (DmgPreloadContext->DmgFile != NULL) {
+ DmgFile = DmgPreloadContext->DmgFile;
+ DmgFileSize = DmgPreloadContext->DmgFileSize;
+ } else {
+ DevPath = Context->DevicePath;
+ Status = OcOpenFileByDevicePath (
+ &DevPath,
+ &DmgDir,
+ EFI_FILE_MODE_READ,
+ EFI_FILE_DIRECTORY
+ );
+ if (EFI_ERROR (Status)) {
+ DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE);
+ DEBUG ((DEBUG_INFO, "OCB: Failed to open DMG directory %s\n", DevPathText));
+ if (DevPathText != NULL) {
+ FreePool (DevPathText);
+ }
- DmgFileInfo = InternalFindFirstDmgFileName (DmgDir, &DmgFileNameLen);
- if (DmgFileInfo == NULL) {
- DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE);
- DEBUG ((DEBUG_INFO, "OCB: Unable to find any DMG at %s\n"));
- if (DevPathText != NULL) {
- FreePool (DevPathText);
- }
+ return NULL;
+ }
- DmgDir->Close (DmgDir);
- return NULL;
- }
+ DmgFileInfo = InternalFindFirstDmgFileName (DmgDir, &DmgFileNameLen);
+ if (DmgFileInfo == NULL) {
+ DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE);
+ DEBUG ((DEBUG_INFO, "OCB: Unable to find any DMG at %s\n"));
+ if (DevPathText != NULL) {
+ FreePool (DevPathText);
+ }
- Status = OcSafeFileOpen (
- DmgDir,
- &DmgFile,
- DmgFileInfo->FileName,
- EFI_FILE_MODE_READ,
- 0
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((
- DEBUG_INFO,
- "OCB: Failed to open DMG file %s - %r\n",
- DmgFileInfo->FileName,
- Status
- ));
+ DmgDir->Close (DmgDir);
+ return NULL;
+ }
- FreePool (DmgFileInfo);
- DmgDir->Close (DmgDir);
- return NULL;
- }
+ Status = OcSafeFileOpen (
+ DmgDir,
+ &DmgFile,
+ DmgFileInfo->FileName,
+ EFI_FILE_MODE_READ,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "OCB: Failed to open DMG file %s - %r\n",
+ DmgFileInfo->FileName,
+ Status
+ ));
+
+ FreePool (DmgFileInfo);
+ DmgDir->Close (DmgDir);
+ return NULL;
+ }
- Status = OcGetFileSize (DmgFile, &DmgFileSize);
- if (EFI_ERROR (Status)) {
- DEBUG ((
- DEBUG_INFO,
- "OCB: Failed to retrieve DMG file size - %r\n",
- Status
- ));
+ Status = OcGetFileSize (DmgFile, &DmgFileSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "OCB: Failed to retrieve DMG file size - %r\n",
+ Status
+ ));
+
+ FreePool (DmgFileInfo);
+ DmgDir->Close (DmgDir);
+ DmgFile->Close (DmgFile);
+ return NULL;
+ }
+ }
- FreePool (DmgFileInfo);
- DmgDir->Close (DmgDir);
- DmgFile->Close (DmgFile);
- return NULL;
- }
+ Context->DmgContext = AllocatePool (sizeof (*Context->DmgContext));
+ if (Context->DmgContext == NULL) {
+ DEBUG ((DEBUG_INFO, "OCB: Failed to allocate DMG context\n"));
+ return NULL;
+ }
- Context->DmgContext = AllocatePool (sizeof (*Context->DmgContext));
- if (Context->DmgContext == NULL) {
- DEBUG ((DEBUG_INFO, "OCB: Failed to allocate DMG context\n"));
- return NULL;
- }
+ Result = OcAppleDiskImageInitializeFromFile (Context->DmgContext, DmgFile);
- Result = OcAppleDiskImageInitializeFromFile (Context->DmgContext, DmgFile);
+ DmgFile->Close (DmgFile);
- DmgFile->Close (DmgFile);
+ if (!Result) {
+ DEBUG ((DEBUG_INFO, "OCB: Failed to initialise DMG from file\n"));
- if (!Result) {
- DEBUG ((DEBUG_INFO, "OCB: Failed to initialise DMG from file\n"));
+ if (DmgPreloadContext->DmgFile == NULL) {
+ FreePool (DmgFileInfo);
+ DmgDir->Close (DmgDir);
+ }
- FreePool (DmgFileInfo);
- FreePool (Context->DmgContext);
- DmgDir->Close (DmgDir);
- return NULL;
+ FreePool (Context->DmgContext);
+ return NULL;
+ }
}
ChunklistBuffer = NULL;
ChunklistFileSize = 0;
+ if ( (DmgPreloadContext->DmgFile != NULL)
+ || (DmgPreloadContext->DmgContext != NULL))
+ {
+ if (DmgPreloadContext->ChunklistBuffer != NULL) {
+ ChunklistBuffer = DmgPreloadContext->ChunklistBuffer;
+ ChunklistFileSize = DmgPreloadContext->ChunklistFileSize;
+ }
+ } else {
+ ChunklistFileInfo = InternalFindDmgChunklist (
+ DmgDir,
+ DmgFileInfo->FileName,
+ DmgFileNameLen
+ );
+ if (ChunklistFileInfo != NULL) {
+ Status = OcSafeFileOpen (
+ DmgDir,
+ &ChunklistFile,
+ ChunklistFileInfo->FileName,
+ EFI_FILE_MODE_READ,
+ 0
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = OcGetFileSize (ChunklistFile, &ChunklistFileSize);
+ if (Status == EFI_SUCCESS) {
+ ChunklistBuffer = AllocatePool (ChunklistFileSize);
- ChunklistFileInfo = InternalFindDmgChunklist (
- DmgDir,
- DmgFileInfo->FileName,
- DmgFileNameLen
- );
- if (ChunklistFileInfo != NULL) {
- Status = OcSafeFileOpen (
- DmgDir,
- &ChunklistFile,
- ChunklistFileInfo->FileName,
- EFI_FILE_MODE_READ,
- 0
- );
- if (!EFI_ERROR (Status)) {
- Status = OcGetFileSize (ChunklistFile, &ChunklistFileSize);
- if (Status == EFI_SUCCESS) {
- ChunklistBuffer = AllocatePool (ChunklistFileSize);
-
- if (ChunklistBuffer == NULL) {
- ChunklistFileSize = 0;
- } else {
- Status = OcGetFileData (ChunklistFile, 0, ChunklistFileSize, ChunklistBuffer);
- if (EFI_ERROR (Status)) {
- FreePool (ChunklistBuffer);
- ChunklistBuffer = NULL;
+ if (ChunklistBuffer == NULL) {
ChunklistFileSize = 0;
+ } else {
+ Status = OcGetFileData (ChunklistFile, 0, ChunklistFileSize, ChunklistBuffer);
+ if (EFI_ERROR (Status)) {
+ FreePool (ChunklistBuffer);
+ ChunklistBuffer = NULL;
+ ChunklistFileSize = 0;
+ }
}
}
+
+ ChunklistFile->Close (ChunklistFile);
}
- ChunklistFile->Close (ChunklistFile);
+ FreePool (ChunklistFileInfo);
}
-
- FreePool (ChunklistFileInfo);
}
- FreePool (DmgFileInfo);
-
- DmgDir->Close (DmgDir);
+ if ( (DmgPreloadContext->DmgFile == NULL)
+ && (DmgPreloadContext->DmgContext == NULL))
+ {
+ FreePool (DmgFileInfo);
+ DmgDir->Close (DmgDir);
+ }
DevPath = InternalGetDiskImageBootFile (
Context,
diff --git a/Library/OcBootManagementLib/OcBootManagementLib.c b/Library/OcBootManagementLib/OcBootManagementLib.c
index b1d778226fa..8f9a242e87f 100644
--- a/Library/OcBootManagementLib/OcBootManagementLib.c
+++ b/Library/OcBootManagementLib/OcBootManagementLib.c
@@ -508,7 +508,6 @@ OcRunBootPicker (
);
OcRestoreNvramProtection (FwRuntime);
- RestoreMode ();
//
// Do not wait on successful return code.
@@ -524,6 +523,11 @@ OcRunBootPicker (
OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_EXECUTION_SUCCESSFUL, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE);
}
+ //
+ // Restore mode after any delay.
+ //
+ RestoreMode ();
+
//
// Ensure that we flush all pressed keys after the application.
// This resolves the problem of application-pressed keys being used to control the menu.
diff --git a/Library/OcMainLib/OpenCoreMisc.c b/Library/OcMainLib/OpenCoreMisc.c
index 070bd8bed75..999f44d98de 100644
--- a/Library/OcMainLib/OpenCoreMisc.c
+++ b/Library/OcMainLib/OpenCoreMisc.c
@@ -25,6 +25,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include
#include
#include
+#include
#include
#include
#include
@@ -232,6 +233,21 @@ ProduceDebugReport (
DEBUG ((DEBUG_INFO, "OC: GOPInfo dumping - %r\n", Status));
+ Status = OcSafeFileOpen (
+ SysReport,
+ &SubReport,
+ L"Drivers",
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
+ EFI_FILE_DIRECTORY
+ );
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "OC: Dumping DriverImageNames for report...\n"));
+ Status = OcDriverInfoDump (SubReport);
+ SubReport->Close (SubReport);
+ }
+
+ DEBUG ((DEBUG_INFO, "OC: DriverImageNames dumping - %r\n", Status));
+
SysReport->Close (SysReport);
Fs->Close (Fs);
@@ -242,13 +258,16 @@ STATIC
EFI_STATUS
EFIAPI
OcToolLoadEntry (
- IN OC_STORAGE_CONTEXT *Storage,
- IN OC_BOOT_ENTRY *ChosenEntry,
- OUT VOID **Data,
- OUT UINT32 *DataSize,
- OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
- OUT EFI_HANDLE *StorageHandle,
- OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath
+ IN OC_STORAGE_CONTEXT *Storage,
+ IN OC_BOOT_ENTRY *ChosenEntry,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
+ OUT EFI_HANDLE *StorageHandle,
+ OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ OUT VOID **CustomFreeContext
)
{
EFI_STATUS Status;
diff --git a/Library/OcPngLib/lodepng.c b/Library/OcPngLib/lodepng.c
index 9090f30ffc6..16aa8a55ba2 100644
--- a/Library/OcPngLib/lodepng.c
+++ b/Library/OcPngLib/lodepng.c
@@ -121,9 +121,6 @@ to something as fast. */
#ifdef EFIAPI
-// Floating point operations are used here, this must be defined to prevent linker error
-const int32_t _fltused = 0;
-
#define LODEPNG_MAX_ALLOC ((size_t)256*1024*1024)
void* lodepng_malloc(size_t size) {
diff --git a/OpenCorePkg.dsc b/OpenCorePkg.dsc
index d5d157a6357..1861d4923f3 100755
--- a/OpenCorePkg.dsc
+++ b/OpenCorePkg.dsc
@@ -30,8 +30,8 @@
DEFINE NETWORK_ENABLE = TRUE
DEFINE NETWORK_SNP_ENABLE = TRUE
DEFINE NETWORK_IP4_ENABLE = TRUE
- DEFINE NETWORK_IP6_ENABLE = FALSE
- DEFINE NETWORK_TLS_ENABLE = FALSE
+ DEFINE NETWORK_IP6_ENABLE = TRUE
+ DEFINE NETWORK_TLS_ENABLE = TRUE
DEFINE NETWORK_HTTP_ENABLE = TRUE
DEFINE NETWORK_HTTP_BOOT_ENABLE = TRUE
DEFINE NETWORK_ALLOW_HTTP_CONNECTIONS = TRUE
@@ -160,7 +160,7 @@
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
UefiDriverEntryPoint|OpenCorePkg/Library/OcDriverEntryPoint/UefiDriverEntryPoint.inf
- UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+ UefiHiiServicesLib|OpenCorePkg/Library/OcHiiServicesLib/OcHiiServicesLib.inf
UefiImageExtraActionLib|MdePkg/Library/BaseUefiImageExtraActionLibNull/BaseUefiImageExtraActionLibNull.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
@@ -169,8 +169,21 @@
VariableFlashInfoLib|MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
ResetSystemLib|OpenCorePkg/Library/OcResetSystemLib/OcResetSystemLib.inf
+ !if $(NETWORK_TLS_ENABLE) == TRUE
+ BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+ # FileExplorerLib is for TlsAuthConfigDxe only (not used by us, but enabled by NETWORK_TLS_ENABLE)
+ FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+ IntrinsicLib|MdePkg/Library/IntrinsicLib/IntrinsicLib.inf
+ OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf
+ RngLib|MdeModulePkg/Library/BaseRngLibTimerLib/BaseRngLibTimerLib.inf
+ SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+ TlsLib|CryptoPkg/Library/TlsLib/TlsLib.inf
+ !endif
+
!include NetworkPkg/NetworkLibs.dsc.inc
+ HttpLib|NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf
+
!include Ext4Pkg/Ext4Defines.dsc.inc
!include Ext4Pkg/Ext4Libs.dsc.inc
@@ -244,7 +257,6 @@
OpenCorePkg/Library/OcBlitLib/OcBlitLib.inf
OpenCorePkg/Library/OcBootManagementLib/OcBootManagementLib.inf
OpenCorePkg/Library/OcBootServicesTableLib/OcBootServicesTableLib.inf
- OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf
OpenCorePkg/Library/OcCompressionLib/OcCompressionLib.inf
OpenCorePkg/Library/OcConfigurationLib/OcConfigurationLib.inf
OpenCorePkg/Library/OcConsoleControlEntryModeLib/OcConsoleControlEntryModeGenericLib.inf
@@ -301,6 +313,7 @@
OpenCorePkg/Platform/OpenCanopy/OpenCanopy.inf
OpenCorePkg/Platform/OpenLegacyBoot/OpenLegacyBoot.inf
OpenCorePkg/Platform/OpenLinuxBoot/OpenLinuxBoot.inf
+ OpenCorePkg/Platform/OpenNetworkBoot/OpenNetworkBoot.inf
OpenCorePkg/Platform/OpenNtfsDxe/OpenNtfsDxe.inf
OpenCorePkg/Platform/OpenPartitionDxe/PartitionDxe.inf
OpenCorePkg/Platform/OpenRuntime/OpenRuntime.inf
@@ -370,13 +383,23 @@
# Ext4 driver
Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
+ # RNG and HASH2 protocols are required by various network boot drivers since edk2-stable202405
+ # REF: https://github.com/acidanthera/bugtracker/issues/2421
+ SecurityPkg/RandomNumberGenerator/RngDxe/RngDxe.inf
+ SecurityPkg/Hash2DxeCrypto/Hash2DxeCrypto.inf
+
#
# Network Support
#
!include NetworkPkg/NetworkComponents.dsc.inc
+ #
+ # Ramdisk support (driver required for network boot native .iso/.img support)
+ #
+ MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
+
[LibraryClasses]
- NULL|OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf
+ NULL|MdePkg/Library/IntrinsicLib/IntrinsicLib.inf
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|0
diff --git a/OpenDuetPkg.dsc b/OpenDuetPkg.dsc
index 3704c47fdb7..f8ab35e26c6 100644
--- a/OpenDuetPkg.dsc
+++ b/OpenDuetPkg.dsc
@@ -245,10 +245,8 @@
OpenCorePkg/Legacy/BootPlatform/LegacyRegion2Dxe/LegacyRegion2Dxe.inf
OpenCorePkg/Legacy/BootPlatform/BiosVideo/BiosVideo.inf
- OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf
-
[LibraryClasses]
- NULL|OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf
+ NULL|MdePkg/Library/IntrinsicLib/IntrinsicLib.inf
[PcdsFeatureFlag]
gEfiMdeModulePkgTokenSpaceGuid.PcdSupportHiiImageProtocol|FALSE
diff --git a/OpenDuetPkgDefines.fdf.inc b/OpenDuetPkgDefines.fdf.inc
index 4a688b389a1..40e2f7cff9c 100644
--- a/OpenDuetPkgDefines.fdf.inc
+++ b/OpenDuetPkgDefines.fdf.inc
@@ -22,7 +22,7 @@
!if ($(TARGET) == DEBUG)
NumBlocks = 0xe #GenFv image size 0xe0000
!else
- NumBlocks = 0x24 #GenFv image size 0x240000
+ NumBlocks = 0x25 #GenFv image size 0x250000
!endif
!endif
FvAlignment = 16 #FV alignment and FV attributes setting.
diff --git a/Platform/OpenCanopy/GuiApp.c b/Platform/OpenCanopy/GuiApp.c
index acf6131201d..6081f5920cc 100644
--- a/Platform/OpenCanopy/GuiApp.c
+++ b/Platform/OpenCanopy/GuiApp.c
@@ -47,7 +47,8 @@ CONST CHAR8 *
[LABEL_SHELL] = "Shell",
[LABEL_SIP_IS_ENABLED] = "SIPEnabled",
[LABEL_SIP_IS_DISABLED] = "SIPDisabled",
- [LABEL_FIRMWARE_SETTINGS] = "FirmwareSettings"
+ [LABEL_FIRMWARE_SETTINGS] = "FirmwareSettings",
+ [LABEL_NETWORK_BOOT] = "NetworkBoot"
};
STATIC
diff --git a/Platform/OpenCanopy/GuiApp.h b/Platform/OpenCanopy/GuiApp.h
index fc9bd0852fa..efce8957589 100644
--- a/Platform/OpenCanopy/GuiApp.h
+++ b/Platform/OpenCanopy/GuiApp.h
@@ -67,6 +67,7 @@ typedef enum {
LABEL_SIP_IS_ENABLED,
LABEL_SIP_IS_DISABLED,
LABEL_FIRMWARE_SETTINGS,
+ LABEL_NETWORK_BOOT,
LABEL_NUM_TOTAL
} LABEL_TARGET;
diff --git a/Platform/OpenCanopy/Views/BootPicker.c b/Platform/OpenCanopy/Views/BootPicker.c
index ba714682cf0..cf0b92c26b3 100644
--- a/Platform/OpenCanopy/Views/BootPicker.c
+++ b/Platform/OpenCanopy/Views/BootPicker.c
@@ -1470,7 +1470,12 @@ BootPickerEntriesSet (
Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_WINDOWS]);
break;
case OC_BOOT_EXTERNAL_OS:
- Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_OTHER]);
+ if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_NETWORK_BOOT) != NULL) {
+ Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_NETWORK_BOOT]);
+ } else {
+ Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_OTHER]);
+ }
+
break;
//
// Use flavour-based labels for system entries (e.g. from boot entry protocol).
diff --git a/Platform/OpenLegacyBoot/OpenLegacyBoot.c b/Platform/OpenLegacyBoot/OpenLegacyBoot.c
index f18577f434d..51879712998 100644
--- a/Platform/OpenLegacyBoot/OpenLegacyBoot.c
+++ b/Platform/OpenLegacyBoot/OpenLegacyBoot.c
@@ -426,7 +426,7 @@ OcGetLegacyBootEntries (
PickerEntry->External = IsExternal;
PickerEntry->UnmanagedBootAction = UnmanagedBootActionDoLegacyBoot;
PickerEntry->UnmanagedBootGetFinalDevicePath = UnmanagedBootGetFinalDevicePath;
- PickerEntry->UnmanagedBootDevicePath = BlockDevicePath;
+ PickerEntry->UnmanagedDevicePath = BlockDevicePath;
if ((PickerEntry->Name == NULL) || (PickerEntry->Flavour == NULL)) {
OcFlexArrayFree (&FlexPickerEntries);
diff --git a/Platform/OpenNetworkBoot/BmBoot.c b/Platform/OpenNetworkBoot/BmBoot.c
index ba66ca2bcad..69919829be2 100644
--- a/Platform/OpenNetworkBoot/BmBoot.c
+++ b/Platform/OpenNetworkBoot/BmBoot.c
@@ -1,14 +1,15 @@
/** @file
- Library functions which relates with booting.
+ Library functions which relate to booting.
Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.
(C) Copyright 2015-2021 Hewlett Packard Enterprise Development LP
+Copyright (C) 2024, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
-#include "InternalBm.h"
+#include "NetworkBootInternal.h"
EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;
@@ -23,6 +24,7 @@ EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
+STATIC
EFI_DEVICE_PATH_PROTOCOL *
BmExpandMediaDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
@@ -145,6 +147,7 @@ BmExpandMediaDevicePath (
@retval TRUE Left and Right are the same.
@retval FALSE Left and Right are the different.
**/
+STATIC
BOOLEAN
BmMatchHttpBootDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *Left,
@@ -190,6 +193,7 @@ BmMatchHttpBootDevicePath (
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
+STATIC
EFI_DEVICE_PATH_PROTOCOL *
BmExpandNetworkFileSystem (
IN EFI_HANDLE LoadFileHandle,
@@ -297,6 +301,7 @@ BmGetRamDiskDevicePath (
@retval RAM Disk buffer.
**/
+STATIC
VOID *
BmGetRamDiskMemoryInfo (
IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath,
@@ -370,10 +375,13 @@ BmDestroyRamDisk (
@return The full device path pointing to the load option buffer.
**/
+STATIC
EFI_DEVICE_PATH_PROTOCOL *
BmExpandLoadFile (
IN EFI_HANDLE LoadFileHandle,
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize
)
{
EFI_STATUS Status;
@@ -383,6 +391,12 @@ BmExpandLoadFile (
UINTN BufferSize;
EFI_DEVICE_PATH_PROTOCOL *FullPath;
+ ASSERT (Data != NULL);
+ ASSERT (DataSize != NULL);
+
+ *Data = NULL;
+ *DataSize = 0;
+
Status = gBS->OpenProtocol (
LoadFileHandle,
&gEfiLoadFileProtocolGuid,
@@ -400,11 +414,38 @@ BmExpandLoadFile (
return NULL;
}
+ //
+ // In call tree of original BmGetLoadOptionBuffer, handling this case
+ // is deferred to subsequent call to GetFileBufferByFilePath.
+ //
if (Status == EFI_BUFFER_TOO_SMALL) {
//
- // The load option buffer is directly returned by LoadFile.
+ // Limited to UINT32 by DataSize in OC_CUSTOM_READ, which is limited
+ // by Size in OcGetFileSize.
+ //
+ if (BufferSize > MAX_UINT32) {
+ return NULL;
+ }
+
+ FileBuffer = AllocatePool (BufferSize);
+ if (FileBuffer == NULL) {
+ return NULL;
+ }
+
+ //
+ // Call LoadFile with the correct buffer size.
//
- return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
+ FullPath = DevicePathFromHandle (LoadFileHandle);
+ Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
+ if (EFI_ERROR (Status)) {
+ FreePool (FileBuffer);
+ return NULL;
+ }
+
+ *DataSize = (UINT32)BufferSize;
+ *Data = FileBuffer;
+
+ return DuplicateDevicePath (FullPath);
}
//
@@ -487,7 +528,10 @@ BmExpandLoadFile (
**/
EFI_DEVICE_PATH_PROTOCOL *
BmExpandLoadFiles (
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ IN BOOLEAN ValidateHttp
)
{
EFI_STATUS Status;
@@ -496,6 +540,7 @@ BmExpandLoadFiles (
UINTN HandleCount;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *Node;
+ EFI_EVENT NotifyEvent;
//
// Get file buffer from load file instance.
@@ -542,5 +587,25 @@ BmExpandLoadFiles (
return NULL;
}
- return BmExpandLoadFile (Handle, FilePath);
+ if (ValidateHttp) {
+ NotifyEvent = MonitorHttpBootCallback (Handle);
+ if (NotifyEvent == NULL) {
+ return NULL;
+ }
+ }
+
+ Node = BmExpandLoadFile (Handle, FilePath, Data, DataSize);
+
+ if (ValidateHttp) {
+ gBS->CloseEvent (NotifyEvent);
+
+ if ((Node != NULL) && !UriWasValidated ()) {
+ Print (L"\n"); ///< Sort out cramped spacing
+ DEBUG ((DEBUG_ERROR, "NTBT: LoadFile returned value but URI was never validated\n"));
+ FreePool (Node);
+ return NULL;
+ }
+ }
+
+ return Node;
}
diff --git a/Platform/OpenNetworkBoot/BmBootDescription.c b/Platform/OpenNetworkBoot/BmBootDescription.c
index 4b8c8f472e8..d4a054127b4 100644
--- a/Platform/OpenNetworkBoot/BmBootDescription.c
+++ b/Platform/OpenNetworkBoot/BmBootDescription.c
@@ -1,13 +1,14 @@
/** @file
- Library functions which relate with boot option description.
+ Library functions which relate to boot option description.
Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
+Copyright (C) 2024, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
-#include "InternalBm.h"
+#include "NetworkBootInternal.h"
/**
Return the description for network boot device.
@@ -141,18 +142,18 @@ BmGetNetworkDescription (
//
// Build description like below:
- // "PXEv6 (MAC:112233445566 VLAN1)"
- // "HTTPv4 (MAC:112233445566)"
+ // "PXE Boot IPv6 (MAC:11-22-33-44-55-66 VLAN1)"
+ // "HTTP Boot IPv4 (MAC:11-22-33-44-55-66)"
//
- DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");
+ DescriptionSize = sizeof (L"HTTP Boot IPv6 (MAC:11-22-33-44-55-66 VLAN65535)");
Description = AllocatePool (DescriptionSize);
ASSERT (Description != NULL);
UnicodeSPrint (
Description,
DescriptionSize,
(Vlan == NULL) ?
- L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :
- L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",
+ L"%s Boot IPv%d (MAC:%02x-%02x-%02x-%02x-%02x-%02x)" :
+ L"%s Boot IPv%d (MAC:%02x-%02x-%02x-%02x-%02x-%02x VLAN%d)",
(Uri == NULL) ? L"PXE" : L"HTTP",
((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,
Mac->MacAddress.Addr[0],
diff --git a/Platform/OpenNetworkBoot/HttpBootCallback.c b/Platform/OpenNetworkBoot/HttpBootCallback.c
new file mode 100644
index 00000000000..145f60e8323
--- /dev/null
+++ b/Platform/OpenNetworkBoot/HttpBootCallback.c
@@ -0,0 +1,286 @@
+/** @file
+ Callback handler for HTTP Boot.
+
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause
+**/
+
+#include "NetworkBootInternal.h"
+
+#include
+#include
+#include
+
+#define HTTP_CONTENT_TYPE_APP_EFI "application/efi"
+
+OC_DMG_LOADING_SUPPORT gDmgLoading;
+
+STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback;
+STATIC BOOLEAN mUriValidated;
+
+//
+// Abort if we are loading a .dmg and these are banned, or if underlying drivers have
+// allowed http:// in URL but user setting for OpenNetworkBoot does not allow it.
+// If PcdAllowHttpConnections was not set (via NETWORK_ALLOW_HTTP_CONNECTIONS compilation
+// flag) then both HttpDxe and HttpBootDxe will enforce https:// before we get to here.
+//
+EFI_STATUS
+ValidateDmgAndHttps (
+ CHAR16 *Uri,
+ BOOLEAN ShowLog,
+ BOOLEAN *HasDmgExtension
+ )
+{
+ CHAR8 *Match;
+ CHAR8 *Uri8;
+ UINTN UriSize;
+
+ if (gRequireHttpsUri && !HasHttpsUri (Uri)) {
+ //
+ // Do not return ACCESS_DENIED as this will attempt to add authentication to the request.
+ //
+ if (ShowLog) {
+ DEBUG ((DEBUG_INFO, "NTBT: Invalid URI https:// is required\n"));
+ }
+
+ return EFI_UNSUPPORTED;
+ }
+
+ UriSize = StrSize (Uri);
+ Uri8 = AllocatePool (UriSize);
+ if (Uri8 == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStrS (Uri, Uri8, UriSize);
+
+ Match = ".dmg";
+ *HasDmgExtension = UriFileHasExtension (Uri8, Match);
+ if (!*HasDmgExtension) {
+ Match = ".chunklist";
+ *HasDmgExtension = UriFileHasExtension (Uri8, Match);
+ }
+
+ FreePool (Uri8);
+
+ if (gDmgLoading == OcDmgLoadingDisabled) {
+ if (*HasDmgExtension) {
+ if (ShowLog) {
+ DEBUG ((DEBUG_INFO, "NTBT: %a file is requested while DMG loading is disabled\n", Match));
+ }
+
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+OpenNetworkBootHttpBootCallback (
+ IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This,
+ IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType,
+ IN BOOLEAN Received,
+ IN UINT32 DataLength,
+ IN VOID *Data OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_MESSAGE *HttpMessage;
+ EFI_HTTP_HEADER *Header;
+ STATIC BOOLEAN HasDmgExtension = FALSE;
+
+ switch (DataType) {
+ //
+ // Abort if http:// is specified but only https:// is allowed.
+ // Since the URI can come from DHCP boot options this is important for security.
+ // This will fail to be enforced if this callback doesn't get registered, or
+ // isn't used by a given implementation of HTTP Boot (it is used by ours, ofc).
+ // Therefore if a file is returned from HTTP Boot, we check that this
+ // was really hit, and won't load it otherwise. (Which is less ideal than
+ // not even fetching the file, as will happen when this callback is hit.)
+ //
+ // Also abort early if .dmg or .chunklist is found when DmgLoading is disabled.
+ // This is a convenience, we could allow these to load and they would be
+ // rejected eventually anyway.
+ //
+ case HttpBootHttpRequest:
+ if (Data != NULL) {
+ HttpMessage = (EFI_HTTP_MESSAGE *)Data;
+ if (HttpMessage->Data.Request->Url != NULL) {
+ //
+ // Print log messages once on initial access with HTTP HEAD, don't
+ // log on subsequent GET which is an attempt to get file size by
+ // pre-loading entire file for case of chunked encoding (where file
+ // size is not known until it has been transferred).
+ //
+ Status = ValidateDmgAndHttps (
+ HttpMessage->Data.Request->Url,
+ HttpMessage->Data.Request->Method == HttpMethodHead,
+ &HasDmgExtension
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ mUriValidated = TRUE;
+ }
+ }
+
+ break;
+
+ //
+ // Provide fake MIME type of 'application/efi' for .dmg and .chunklist.
+ // This is also a convenience of sorts, in that as long as the user
+ // sets 'application/efi' MIME type for these files on their web server,
+ // they would work anyway.
+ //
+ case HttpBootHttpResponse:
+ if ((Data != NULL) && HasDmgExtension) {
+ //
+ // We do not need to keep modifying Content-Type for subsequent packets.
+ //
+ HasDmgExtension = FALSE;
+
+ HttpMessage = (EFI_HTTP_MESSAGE *)Data;
+ Header = HttpFindHeader (HttpMessage->HeaderCount, HttpMessage->Headers, HTTP_HEADER_CONTENT_TYPE);
+
+ if (Header == NULL) {
+ Header = HttpMessage->Headers;
+ ++HttpMessage->HeaderCount;
+ HttpMessage->Headers = AllocatePool (HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0]));
+ if (HttpMessage->Headers == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (HttpMessage->Headers, Header, HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0]));
+ Header = &HttpMessage->Headers[HttpMessage->HeaderCount - 1];
+ Header->FieldValue = NULL;
+ Header->FieldName = AllocateCopyPool (L_STR_SIZE (HTTP_HEADER_CONTENT_TYPE), HTTP_HEADER_CONTENT_TYPE);
+ if (Header->FieldName == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ ASSERT (Header->FieldValue != NULL);
+ if (AsciiStrCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) != 0) {
+ FreePool (Header->FieldValue);
+ Header->FieldValue = NULL;
+ }
+ }
+
+ if (Header->FieldValue == NULL) {
+ Header->FieldValue = AllocateCopyPool (L_STR_SIZE (HTTP_CONTENT_TYPE_APP_EFI), HTTP_CONTENT_TYPE_APP_EFI);
+ if (Header->FieldValue == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return mOriginalHttpBootCallback (
+ This,
+ DataType,
+ Received,
+ DataLength,
+ Data
+ );
+}
+
+STATIC
+VOID
+EFIAPI
+NotifyInstallHttpBootCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE LoadFileHandle;
+ EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;
+
+ LoadFileHandle = Context;
+
+ Status = gBS->HandleProtocol (
+ LoadFileHandle,
+ &gEfiHttpBootCallbackProtocolGuid,
+ (VOID **)&HttpBootCallback
+ );
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Due to a bug in EDK 2 HttpBootUninstallCallback, they do not uninstall
+ // this protocol when they try to. This is okay as long as there is only
+ // one consumer of the protocol, because our hooked version stays installed
+ // and gets reused (found as the already installed protocol) on second
+ // and subsequent tries in HttpBootInstallCallback.
+ // REF: https://edk2.groups.io/g/devel/message/117469
+ // TODO: Add edk2 bugzilla issue.
+ // TODO: To safely allow for more than one consumer while allowing for
+ // their bug, we would need to store multiple original callbacks, one
+ // per http boot callback protocol address. (Otherwise using consumer
+ // A then consumer B, so that we are already installed on both handles,
+ // then consumer A again, will use the original callback for B.)
+ //
+ mOriginalHttpBootCallback = HttpBootCallback->Callback;
+ HttpBootCallback->Callback = OpenNetworkBootHttpBootCallback;
+ }
+}
+
+BOOLEAN
+UriWasValidated (
+ VOID
+ )
+{
+ return mUriValidated;
+}
+
+EFI_EVENT
+MonitorHttpBootCallback (
+ EFI_HANDLE LoadFileHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+ VOID *Registration;
+
+ //
+ // Everything in our callback except https validation is convenient but optional.
+ // So we can make our driver fully usable with some (hypothetical?) http boot
+ // implementation which never hits our callback, as long as we treat the URI as
+ // already validated (even if the callback is never hit) when https validation
+ // is not turned on.
+ //
+ mUriValidated = !gRequireHttpsUri;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ NotifyInstallHttpBootCallback,
+ LoadFileHandle,
+ &Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Status = gBS->RegisterProtocolNotify (
+ &gEfiHttpBootCallbackProtocolGuid,
+ Event,
+ &Registration
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (Event);
+ return NULL;
+ }
+
+ return Event;
+}
diff --git a/Platform/OpenNetworkBoot/HttpBootCustomRead.c b/Platform/OpenNetworkBoot/HttpBootCustomRead.c
new file mode 100644
index 00000000000..fa6875da1be
--- /dev/null
+++ b/Platform/OpenNetworkBoot/HttpBootCustomRead.c
@@ -0,0 +1,298 @@
+/** @file
+ Top level LoadFile protocol handler for HTTP Boot.
+
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause
+**/
+
+#include "NetworkBootInternal.h"
+
+#include
+#include
+
+typedef struct {
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
+} CUSTOM_FREE_CONTEXT;
+
+STATIC
+EFI_STATUS
+SetDmgPreloadDmgFile (
+ IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ IN OUT VOID **Data,
+ IN OUT UINT32 *DataSize
+ )
+{
+ EFI_STATUS Status;
+
+ Status = CreateVirtualFileFileNameCopy (L"__HTTPBoot__.dmg", *Data, *DataSize, NULL, &DmgPreloadContext->DmgFile);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Data);
+ } else {
+ DmgPreloadContext->DmgFileSize = *DataSize;
+ }
+
+ *Data = NULL;
+ *DataSize = 0;
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+SetDmgPreloadChunklist (
+ IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ IN OUT VOID **Data,
+ IN OUT UINT32 *DataSize
+ )
+{
+ DmgPreloadContext->ChunklistBuffer = *Data;
+ DmgPreloadContext->ChunklistFileSize = *DataSize;
+
+ *Data = NULL;
+ *DataSize = 0;
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+FreeDmgPreloadContext (
+ IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext
+ )
+{
+ if (DmgPreloadContext->ChunklistBuffer != NULL) {
+ FreePool (DmgPreloadContext->ChunklistBuffer);
+ }
+
+ DmgPreloadContext->ChunklistFileSize = 0;
+ if (DmgPreloadContext->DmgFile != NULL) {
+ DmgPreloadContext->DmgFile->Close (DmgPreloadContext->DmgFile);
+ DmgPreloadContext->DmgFile = NULL;
+ }
+
+ DmgPreloadContext->DmgFileSize = 0;
+}
+
+//
+// Equivalent to lines in original BmBoot.c which free RAM disk unconditionally
+// when image loaded from RAM disk exits:
+// https://github.com/tianocore/edk2/blob/a6648418c1600f0a81f2914d9dd14de1adbfe598/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c#L2061-L2071
+//
+EFI_STATUS
+EFIAPI
+HttpBootCustomFree (
+ IN VOID *Context
+ )
+{
+ CUSTOM_FREE_CONTEXT *CustomFreeContext;
+
+ if (Context != NULL) {
+ CustomFreeContext = Context;
+ if (CustomFreeContext->RamDiskDevicePath != NULL) {
+ BmDestroyRamDisk (CustomFreeContext->RamDiskDevicePath);
+ FreePool (CustomFreeContext->RamDiskDevicePath);
+ }
+
+ FreePool (CustomFreeContext);
+ }
+
+ return EFI_SUCCESS;
+}
+
+//
+// Within BmExpandLoadFiles:
+// - Only DevicePath will be set if we're returning a boot file on an HTTP
+// Boot native ram disk (from .iso or .img). In this case the first and
+// second calls to LoadFile occur inside this method.
+// - The boot file is then loaded from the RAM disk (via the returned
+// device path) in a subsequent call to gBS->LoadImage made by the
+// caller.
+// - The above applies in the orginal and our modified method.
+// - Our method is modified from EDK-II original so that the second LoadFile
+// call will also be made inside the method, and Data and DataSize will be
+// filled in, if a single .efi file is loaded.
+// - In the EDK-II original, final loading of .efi files is delayed to the
+// subsequent call to GetFileBufferByFilePath in BmGetLoadOptionBuffer.
+// - Note that in the HttpBootDxe LoadFile implementation (which will be
+// used by the original and our modified code), if HTTP chunked transfer
+// encoding is used then the entire file is downloaded (chunked HTTP GET)
+// and cached (in a linked list of fixed-sized download sections, not
+// corresponding in size to the actual HTTP chunks) in order to get its
+// size, before the final buffer for the file can be allocated; then within
+// the second LoadFile call the file is transferred from this cache into
+// the final allocated buffer.
+// - For non-chunked (so, more or less, normal) transfer encoding, the
+// file size is available from a simple HTTP HEAD request, then in the
+// second LoadFile call the file HTTP GET is written directly into
+// allocated buffer.
+// - So chunked transfer encoding should ideally be avoided, especially
+// for large downloads, but is supported here and in the original.
+//
+EFI_STATUS
+EFIAPI
+HttpBootCustomRead (
+ IN OC_STORAGE_CONTEXT *Storage,
+ IN OC_BOOT_ENTRY *ChosenEntry,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
+ OUT EFI_HANDLE *StorageHandle,
+ OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ OUT VOID **Context
+ )
+{
+ EFI_STATUS Status;
+ CUSTOM_FREE_CONTEXT *CustomFreeContext;
+ CHAR8 *OtherUri;
+ BOOLEAN GotDmgFirst;
+ EFI_DEVICE_PATH_PROTOCOL *OtherLoadFile;
+ EFI_DEVICE_PATH_PROTOCOL *OtherDevicePath;
+
+ ASSERT (Context != NULL);
+ *Context = NULL;
+
+ CustomFreeContext = AllocateZeroPool (sizeof (CUSTOM_FREE_CONTEXT));
+ if (CustomFreeContext == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ gDmgLoading = DmgLoading;
+
+ OcConsoleControlSetMode (EfiConsoleControlScreenText);
+
+ //
+ // Load the first (or only) file. This method has been extended to
+ // abort early (avoiding a pointless, long, slow load of a DMG) if DmgLoading
+ // is disabled and the requested file extension is `.dmg` (or `.chunklist`).
+ //
+ *DevicePath = BmExpandLoadFiles (ChosenEntry->DevicePath, Data, DataSize, TRUE);
+
+ if (*DevicePath == NULL) {
+ FreePool (CustomFreeContext);
+ return EFI_NOT_FOUND;
+ }
+
+ Status = EFI_SUCCESS;
+ GotDmgFirst = FALSE;
+ OtherUri = NULL;
+
+ //
+ // Only potentially treat first file as .dmg/.chunklist if it was loaded as
+ // a normal single file. HttpBootDxe Content-Type header handling may force
+ // any file, regardless of extension, to be treated as an .iso or .img and
+ // loaded as a RAM disk.
+ //
+ if (*DataSize != 0) {
+ Status = ExtractOtherUriFromDevicePath (*DevicePath, ".dmg", ".chunklist", &OtherUri, FALSE);
+ if (!EFI_ERROR (Status)) {
+ GotDmgFirst = TRUE;
+ Status = SetDmgPreloadDmgFile (DmgPreloadContext, Data, DataSize);
+ if (EFI_ERROR (Status)) {
+ FreePool (OtherUri);
+ OtherUri = NULL;
+ }
+ } else {
+ Status = ExtractOtherUriFromDevicePath (*DevicePath, ".chunklist", ".dmg", &OtherUri, FALSE);
+ if (!EFI_ERROR (Status)) {
+ Status = SetDmgPreloadChunklist (DmgPreloadContext, Data, DataSize);
+ if (EFI_ERROR (Status)) {
+ FreePool (OtherUri);
+ OtherUri = NULL;
+ }
+ } else if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ OtherUri = NULL;
+ }
+ }
+ }
+
+ //
+ // Is there a potential matched file?
+ //
+ if (OtherUri != NULL) {
+ //
+ // Always require .dmg if .chunklist was fetched first; only fetch (and
+ // require) .chunklist after .dmg when it will be used.
+ //
+ if (!GotDmgFirst || (DmgLoading == OcDmgLoadingAppleSigned)) {
+ Status = HttpBootAddUri (ChosenEntry->DevicePath, OtherUri, OcStringFormatAscii, &OtherLoadFile);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Sort out cramped spacing between the two HTTP Boot calls.
+ // Hopefully should not affect GUI-based firmware.
+ //
+ Print (L"\n");
+
+ //
+ // Load the second file of .dmg/.chunklist pair.
+ //
+ OtherDevicePath = BmExpandLoadFiles (OtherLoadFile, Data, DataSize, TRUE);
+ FreePool (OtherLoadFile);
+ if (OtherDevicePath == NULL) {
+ DEBUG ((DEBUG_INFO, "NTBT: Failed to fetch required matching file %a\r", OtherUri));
+ Status = EFI_NOT_FOUND;
+ } else {
+ if (GotDmgFirst) {
+ FreePool (OtherDevicePath);
+ Status = SetDmgPreloadChunklist (DmgPreloadContext, Data, DataSize);
+ } else {
+ FreePool (*DevicePath);
+ *DevicePath = OtherDevicePath;
+ Status = SetDmgPreloadDmgFile (DmgPreloadContext, Data, DataSize);
+ }
+ }
+ }
+ }
+
+ FreePool (OtherUri);
+ }
+
+ //
+ // Sort out OC debug messages following HTTP Boot progress message on the same line, after completion.
+ //
+ Print (L"\n");
+
+ if (EFI_ERROR (Status)) {
+ FreeDmgPreloadContext (DmgPreloadContext);
+ FreePool (CustomFreeContext);
+ FreePool (*DevicePath);
+ *DevicePath = NULL;
+ return Status;
+ }
+
+ //
+ // It is okay to follow EDK-II code and check for this when it might not be there.
+ //
+ CustomFreeContext->RamDiskDevicePath = BmGetRamDiskDevicePath (*DevicePath);
+ *Context = CustomFreeContext;
+
+ return EFI_SUCCESS;
+}
+
+//
+// There is no possibility of DMGs, chunklists or ISOs with PXE boot.
+//
+EFI_STATUS
+EFIAPI
+PxeBootCustomRead (
+ IN OC_STORAGE_CONTEXT *Storage,
+ IN OC_BOOT_ENTRY *ChosenEntry,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
+ OUT EFI_HANDLE *StorageHandle,
+ OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ OUT VOID **Context
+ )
+{
+ OcConsoleControlSetMode (EfiConsoleControlScreenText);
+
+ *DevicePath = BmExpandLoadFiles (ChosenEntry->DevicePath, Data, DataSize, FALSE);
+
+ return (*DevicePath == NULL ? EFI_NOT_FOUND : EFI_SUCCESS);
+}
diff --git a/Platform/OpenNetworkBoot/NetworkBootInternal.h b/Platform/OpenNetworkBoot/NetworkBootInternal.h
new file mode 100644
index 00000000000..bf51cf17bec
--- /dev/null
+++ b/Platform/OpenNetworkBoot/NetworkBootInternal.h
@@ -0,0 +1,217 @@
+/** @file
+ Copyright (C) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause
+**/
+
+#ifndef LOAD_FILE_INTERNAL_H
+#define LOAD_FILE_INTERNAL_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ Set if we should enforce https only within this driver.
+**/
+extern BOOLEAN gRequireHttpsUri;
+
+/**
+ Current DmgLoading setting, for HTTP BOOT callback validation.
+**/
+extern OC_DMG_LOADING_SUPPORT gDmgLoading;
+
+/**
+ Custom validation for network boot device path.
+
+ @param Path Device path to validate.
+
+ @retval EFI_SUCCESS Device path should be accepted.
+ @retval other Device path should be rejected.
+**/
+typedef
+EFI_STATUS
+(*VALIDATE_BOOT_DEVICE_PATH)(
+ IN VOID *Context,
+ IN EFI_DEVICE_PATH_PROTOCOL *Path
+ );
+
+/*
+ Return pointer to final node in device path, if it as a URI node.
+ Used to return the URI node for an HTTP Boot device path.
+
+ @return Required device path node if available, NULL otherwise.
+*/
+EFI_DEVICE_PATH_PROTOCOL *
+GetUriNode (
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ );
+
+///
+/// BmBootDescription.c
+///
+
+CHAR16 *
+BmGetNetworkDescription (
+ IN EFI_HANDLE Handle
+ );
+
+///
+/// BmBoot.c
+///
+
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFiles (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ IN BOOLEAN ValidateHttp
+ );
+
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetRamDiskDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ );
+
+VOID
+BmDestroyRamDisk (
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
+ );
+
+///
+/// CustomRead.c
+///
+
+EFI_STATUS
+EFIAPI
+HttpBootCustomFree (
+ IN VOID *Context
+ );
+
+EFI_STATUS
+EFIAPI
+HttpBootCustomRead (
+ IN OC_STORAGE_CONTEXT *Storage,
+ IN OC_BOOT_ENTRY *ChosenEntry,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
+ OUT EFI_HANDLE *StorageHandle,
+ OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ OUT VOID **Context
+ );
+
+EFI_STATUS
+EFIAPI
+PxeBootCustomRead (
+ IN OC_STORAGE_CONTEXT *Storage,
+ IN OC_BOOT_ENTRY *ChosenEntry,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
+ OUT EFI_HANDLE *StorageHandle,
+ OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath,
+ IN OC_DMG_LOADING_SUPPORT DmgLoading,
+ OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext,
+ OUT VOID **Context
+ );
+
+///
+/// Uri.c
+///
+
+BOOLEAN
+HasHttpsUri (
+ CHAR16 *Uri
+ );
+
+EFI_STATUS
+ExtractOtherUriFromDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN CHAR8 *FromExt,
+ IN CHAR8 *ToExt,
+ OUT CHAR8 **OtherUri,
+ IN BOOLEAN OnlySearchForFromExt
+ );
+
+BOOLEAN
+UriFileHasExtension (
+ IN CHAR8 *Uri,
+ IN CHAR8 *Ext
+ );
+
+EFI_STATUS
+HttpBootAddUri (
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ VOID *Uri,
+ OC_STRING_FORMAT StringFormat,
+ EFI_DEVICE_PATH_PROTOCOL **UriDevicePath
+ );
+
+EFI_EVENT
+MonitorHttpBootCallback (
+ EFI_HANDLE LoadFileHandle
+ );
+
+BOOLEAN
+UriWasValidated (
+ VOID
+ );
+
+///
+/// TlsAuthConfigImpl.c
+///
+
+EFI_STATUS
+LogInstalledCerts (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid
+ );
+
+EFI_STATUS
+CertIsPresent (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN EFI_GUID *OwnerGuid,
+ IN UINTN X509DataSize,
+ IN VOID *X509Data
+ );
+
+EFI_STATUS
+DeleteCertsForOwner (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN EFI_GUID *OwnerGuid,
+ IN UINTN X509DataSize,
+ IN VOID *X509Data,
+ OUT UINTN *DeletedCount
+ );
+
+EFI_STATUS
+EnrollX509toVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN EFI_GUID *OwnerGuid,
+ IN UINTN X509DataSize,
+ IN VOID *X509Data
+ );
+
+#endif // LOAD_FILE_INTERNAL_H
diff --git a/Platform/OpenNetworkBoot/OpenNetworkBoot.c b/Platform/OpenNetworkBoot/OpenNetworkBoot.c
new file mode 100644
index 00000000000..0b90a83d3b8
--- /dev/null
+++ b/Platform/OpenNetworkBoot/OpenNetworkBoot.c
@@ -0,0 +1,535 @@
+/** @file
+ Boot entry protocol handler for PXE and HTTP Boot.
+
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause
+**/
+
+#include "NetworkBootInternal.h"
+
+#define ENROLL_CERT L"--enroll-cert"
+#define DELETE_CERT L"--delete-cert"
+#define DELETE_ALL_CERTS L"--delete-all-certs"
+
+BOOLEAN gRequireHttpsUri;
+
+STATIC BOOLEAN mAllowPxeBoot;
+STATIC BOOLEAN mAllowHttpBoot;
+STATIC BOOLEAN mAllowIpv4;
+STATIC BOOLEAN mAllowIpv6;
+STATIC BOOLEAN mAuxEntries;
+STATIC CHAR16 *mHttpBootUri;
+
+STATIC CHAR16 PxeBootId[] = L"PXE Boot IPv";
+STATIC CHAR16 HttpBootId[] = L"HTTP Boot IPv";
+
+VOID
+InternalFreePickerEntry (
+ IN OC_PICKER_ENTRY *Entry
+ )
+{
+ ASSERT (Entry != NULL);
+
+ if (Entry == NULL) {
+ return;
+ }
+
+ if (Entry->Id != NULL) {
+ FreePool ((CHAR8 *)Entry->Id);
+ }
+
+ if (Entry->Name != NULL) {
+ FreePool ((CHAR8 *)Entry->Name);
+ }
+
+ if (Entry->Path != NULL) {
+ FreePool ((CHAR8 *)Entry->Path);
+ }
+
+ if (Entry->Arguments != NULL) {
+ FreePool ((CHAR8 *)Entry->Arguments);
+ }
+
+ if (Entry->UnmanagedDevicePath != NULL) {
+ FreePool (Entry->UnmanagedDevicePath);
+ }
+}
+
+STATIC
+VOID
+EFIAPI
+FreeNetworkBootEntries (
+ IN OC_PICKER_ENTRY **Entries,
+ IN UINTN NumEntries
+ )
+{
+ UINTN Index;
+
+ ASSERT (Entries != NULL);
+ ASSERT (*Entries != NULL);
+ if ((Entries == NULL) || (*Entries == NULL)) {
+ return;
+ }
+
+ for (Index = 0; Index < NumEntries; Index++) {
+ InternalFreePickerEntry (&(*Entries)[Index]);
+ }
+
+ FreePool (*Entries);
+ *Entries = NULL;
+}
+
+STATIC
+EFI_STATUS
+InternalAddEntry (
+ OC_FLEX_ARRAY *FlexPickerEntries,
+ CHAR16 *Description,
+ EFI_HANDLE Handle,
+ CHAR16 *HttpBootUri,
+ BOOLEAN IsIPv4,
+ BOOLEAN IsHttpBoot
+ )
+{
+ EFI_STATUS Status;
+ OC_PICKER_ENTRY *PickerEntry;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
+ UINTN IdLen;
+
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "NTBT: Missing device path - %r\n",
+ Status
+ ));
+ return Status;
+ }
+
+ PickerEntry = OcFlexArrayAddItem (FlexPickerEntries);
+ if (PickerEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IdLen = StrLen (Description);
+ PickerEntry->Id = AllocatePool ((IdLen + 1) * sizeof (PickerEntry->Id[0]));
+ if (PickerEntry->Id == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStrS (Description, (CHAR8 *)PickerEntry->Id, IdLen + 1);
+
+ PickerEntry->Name = AllocateCopyPool (IdLen + 1, PickerEntry->Id);
+ if (PickerEntry->Name == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (IsHttpBoot && (HttpBootUri != NULL)) {
+ Status = HttpBootAddUri (DevicePath, HttpBootUri, OcStringFormatUnicode, &NewDevicePath);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ NewDevicePath = DuplicateDevicePath (DevicePath);
+ if (NewDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ PickerEntry->UnmanagedDevicePath = NewDevicePath;
+
+ if (IsHttpBoot) {
+ PickerEntry->CustomRead = HttpBootCustomRead;
+ PickerEntry->CustomFree = HttpBootCustomFree;
+ PickerEntry->Flavour = IsIPv4 ? OC_FLAVOUR_HTTP_BOOT4 : OC_FLAVOUR_HTTP_BOOT6;
+ } else {
+ PickerEntry->CustomRead = PxeBootCustomRead;
+ PickerEntry->Flavour = IsIPv4 ? OC_FLAVOUR_PXE_BOOT4 : OC_FLAVOUR_PXE_BOOT6;
+ }
+
+ PickerEntry->TextMode = TRUE;
+ PickerEntry->Auxiliary = mAuxEntries;
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+GetNetworkBootEntries (
+ IN OUT OC_PICKER_CONTEXT *PickerContext,
+ IN CONST EFI_HANDLE Device OPTIONAL,
+ OUT OC_PICKER_ENTRY **Entries,
+ OUT UINTN *NumEntries
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+ CHAR16 *NetworkDescription;
+ CHAR16 *IdStr;
+ OC_FLEX_ARRAY *FlexPickerEntries;
+ BOOLEAN IsIPv4;
+ BOOLEAN IsHttpBoot;
+
+ //
+ // Here we produce custom entries only, not entries found on filesystems.
+ //
+ if (Device != NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiLoadFileProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "NTBT: Load file protocol - %r\n", Status));
+ return Status;
+ }
+
+ FlexPickerEntries = OcFlexArrayInit (sizeof (OC_PICKER_ENTRY), (OC_FLEX_ARRAY_FREE_ITEM)InternalFreePickerEntry);
+ if (FlexPickerEntries == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < HandleCount; ++Index) {
+ NetworkDescription = BmGetNetworkDescription (HandleBuffer[Index]);
+ if (NetworkDescription == NULL) {
+ DebugPrintDevicePathForHandle (DEBUG_INFO, "NTBT: LoadFile handle not PXE/HTTP boot DP", HandleBuffer[Index]);
+ } else {
+ //
+ // Use fixed format network description which we control as shortcut
+ // to identify PXE/HTTP and IPv4/6.
+ //
+ if ((IdStr = StrStr (NetworkDescription, PxeBootId)) != NULL) {
+ IsIPv4 = IdStr[L_STR_LEN (PxeBootId)] == L'4';
+ ASSERT (IsIPv4 || (IdStr[L_STR_LEN (PxeBootId)] == L'6'));
+ IsHttpBoot = FALSE;
+ } else if ((IdStr = StrStr (NetworkDescription, HttpBootId)) != NULL) {
+ IsIPv4 = IdStr[L_STR_LEN (HttpBootId)] == L'4';
+ ASSERT (IsIPv4 || (IdStr[L_STR_LEN (HttpBootId)] == L'6'));
+ IsHttpBoot = TRUE;
+ }
+
+ if ( (IdStr != NULL)
+ && ((IsIPv4 && mAllowIpv4) || (!IsIPv4 && mAllowIpv6))
+ && ((IsHttpBoot && mAllowHttpBoot) || (!IsHttpBoot && mAllowPxeBoot))
+ )
+ {
+ DEBUG ((DEBUG_INFO, "NTBT: Adding %s\n", NetworkDescription));
+ Status = InternalAddEntry (
+ FlexPickerEntries,
+ NetworkDescription,
+ HandleBuffer[Index],
+ IsHttpBoot ? mHttpBootUri : NULL,
+ IsIPv4,
+ IsHttpBoot
+ );
+ } else {
+ DEBUG ((DEBUG_INFO, "NTBT: Ignoring %s\n", NetworkDescription));
+ }
+
+ FreePool (NetworkDescription);
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ FreePool (HandleBuffer);
+
+ if (EFI_ERROR (Status)) {
+ OcFlexArrayFree (&FlexPickerEntries);
+ return Status;
+ }
+
+ OcFlexArrayFreeContainer (&FlexPickerEntries, (VOID **)Entries, NumEntries);
+
+ if (*NumEntries == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EnrollCerts (
+ OC_FLEX_ARRAY *ParsedLoadOptions
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ OC_PARSED_VAR *Option;
+ EFI_GUID *OwnerGuid;
+ UINTN CertSize;
+ CHAR8 *CertData;
+ BOOLEAN EnrollCert;
+ BOOLEAN DeleteCert;
+ BOOLEAN DeleteAllCerts;
+ UINTN OptionLen;
+ UINTN DeletedCount;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Find certs in options.
+ //
+ for (Index = 0; Index < ParsedLoadOptions->Count; ++Index) {
+ Option = OcFlexArrayItemAt (ParsedLoadOptions, Index);
+
+ EnrollCert = FALSE;
+ DeleteCert = FALSE;
+ DeleteAllCerts = FALSE;
+
+ if (OcUnicodeStartsWith (Option->Unicode.Name, ENROLL_CERT, TRUE)) {
+ EnrollCert = TRUE;
+ OptionLen = L_STR_LEN (ENROLL_CERT);
+ } else if (OcUnicodeStartsWith (Option->Unicode.Name, DELETE_CERT, TRUE)) {
+ DeleteCert = TRUE;
+ OptionLen = L_STR_LEN (DELETE_CERT);
+ } else if (OcUnicodeStartsWith (Option->Unicode.Name, DELETE_ALL_CERTS, TRUE)) {
+ DeleteAllCerts = TRUE;
+ OptionLen = L_STR_LEN (DELETE_ALL_CERTS);
+ }
+
+ if ( (EnrollCert || DeleteCert || DeleteAllCerts)
+ && (Option->Unicode.Name[OptionLen] != CHAR_NULL)
+ && (Option->Unicode.Name[OptionLen] != L':')
+ )
+ {
+ EnrollCert = FALSE;
+ DeleteCert = FALSE;
+ DeleteAllCerts = FALSE;
+ }
+
+ if ((EnrollCert || DeleteCert) && (Option->Unicode.Value == NULL)) {
+ DEBUG ((DEBUG_INFO, "NTBT: Ignoring %s option with no cert value\n", Option->Unicode.Name));
+ EnrollCert = FALSE;
+ DeleteCert = FALSE;
+ }
+
+ if (EnrollCert || DeleteCert || DeleteAllCerts) {
+ OwnerGuid = AllocateZeroPool (sizeof (EFI_GUID));
+ if (OwnerGuid == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ //
+ // Use all zeros GUID if no user value supplied.
+ //
+ if (Option->Unicode.Name[OptionLen] == L':') {
+ Status = StrToGuid (&Option->Unicode.Name[OptionLen + 1], OwnerGuid);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "NTBT: Cannot parse cert owner GUID from %s - %r\n", Option->Unicode.Name, Status));
+ break;
+ }
+ }
+
+ if (DeleteAllCerts) {
+ Status = DeleteCertsForOwner (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ OwnerGuid,
+ 0,
+ NULL,
+ &DeletedCount
+ );
+ DEBUG ((DEBUG_INFO, "NTBT: %s %u deleted - %r\n", Option->Unicode.Name, DeletedCount, Status));
+ } else {
+ //
+ // We do not include the terminating '\0' in the stored certificate,
+ // which matches how stored by e.g. OVMF when loaded from file;
+ // but we must allocate space for '\0' for Unicode to ASCII conversion.
+ //
+ CertSize = StrLen (Option->Unicode.Value);
+ CertData = AllocateZeroPool (CertSize + 1);
+ if (CertData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ UnicodeStrToAsciiStrS (Option->Unicode.Value, CertData, CertSize + 1);
+
+ if (DeleteCert) {
+ Status = DeleteCertsForOwner (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ OwnerGuid,
+ CertSize,
+ CertData,
+ &DeletedCount
+ );
+ DEBUG ((DEBUG_INFO, "NTBT: %s %u deleted - %r\n", Option->Unicode.Name, DeletedCount, Status));
+ } else {
+ Status = CertIsPresent (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ OwnerGuid,
+ CertSize,
+ CertData
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ALREADY_STARTED) {
+ DEBUG ((DEBUG_INFO, "NTBT: %s already present\n", Option->Unicode.Name));
+ Status = EFI_SUCCESS;
+ } else {
+ DEBUG ((DEBUG_INFO, "NTBT: Error checking for cert presence - %r\n", Status));
+ }
+ } else {
+ Status = EnrollX509toVariable (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ OwnerGuid,
+ CertSize,
+ CertData
+ );
+ DEBUG ((DEBUG_INFO, "NTBT: %s - %r\n", Option->Unicode.Name, Status));
+ }
+ }
+
+ FreePool (CertData);
+ }
+
+ FreePool (OwnerGuid);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ }
+
+ return Status;
+}
+
+STATIC
+OC_BOOT_ENTRY_PROTOCOL
+ mNetworkBootEntryProtocol = {
+ OC_BOOT_ENTRY_PROTOCOL_REVISION,
+ GetNetworkBootEntries,
+ FreeNetworkBootEntries,
+ NULL
+};
+
+EFI_STATUS
+EFIAPI
+UefiMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ OC_FLEX_ARRAY *ParsedLoadOptions;
+ CHAR16 *TempUri;
+
+ Status = gBS->HandleProtocol (
+ ImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **)&LoadedImage
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ mAllowIpv4 = FALSE;
+ mAllowIpv6 = FALSE;
+ mAllowPxeBoot = FALSE;
+ mAllowHttpBoot = FALSE;
+ gRequireHttpsUri = FALSE;
+ mHttpBootUri = NULL;
+
+ Status = OcParseLoadOptions (LoadedImage, &ParsedLoadOptions);
+ if (EFI_ERROR (Status)) {
+ if (Status != EFI_NOT_FOUND) {
+ return Status;
+ }
+
+ Status = EFI_SUCCESS;
+ } else {
+ //
+ // e.g. --https --uri=https://imageserver.org/OpenShell.efi
+ //
+ mAllowIpv4 = OcHasParsedVar (ParsedLoadOptions, L"-4", OcStringFormatUnicode);
+ mAllowIpv6 = OcHasParsedVar (ParsedLoadOptions, L"-6", OcStringFormatUnicode);
+ mAllowPxeBoot = OcHasParsedVar (ParsedLoadOptions, L"--pxe", OcStringFormatUnicode);
+ mAllowHttpBoot = OcHasParsedVar (ParsedLoadOptions, L"--http", OcStringFormatUnicode);
+ mAuxEntries = OcHasParsedVar (ParsedLoadOptions, L"--aux", OcStringFormatUnicode);
+ gRequireHttpsUri = OcHasParsedVar (ParsedLoadOptions, L"--https", OcStringFormatUnicode);
+
+ TempUri = NULL;
+ OcParsedVarsGetUnicodeStr (ParsedLoadOptions, L"--uri", &TempUri);
+ if (TempUri != NULL) {
+ mHttpBootUri = AllocateCopyPool (StrSize (TempUri), TempUri);
+ if (mHttpBootUri == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ Status = EnrollCerts (ParsedLoadOptions);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "NTBT: Failed to enroll certs - %r\n", Status));
+ }
+
+ DEBUG_CODE_BEGIN ();
+ LogInstalledCerts (EFI_TLS_CA_CERTIFICATE_VARIABLE, &gEfiTlsCaCertificateGuid);
+ DEBUG_CODE_END ();
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ if (!mAllowIpv4 && !mAllowIpv6) {
+ mAllowIpv4 = TRUE;
+ mAllowIpv6 = TRUE;
+ }
+
+ if (!gRequireHttpsUri && !mAllowHttpBoot && !mAllowPxeBoot) {
+ mAllowHttpBoot = TRUE;
+ mAllowPxeBoot = TRUE;
+ }
+
+ if (gRequireHttpsUri) {
+ mAllowHttpBoot = TRUE;
+ }
+
+ if (mHttpBootUri != NULL) {
+ if (!mAllowHttpBoot) {
+ DEBUG ((DEBUG_INFO, "NTBT: URI specified but HTTP boot is disabled\n"));
+ } else {
+ if (gRequireHttpsUri && !HasHttpsUri (mHttpBootUri)) {
+ DEBUG ((DEBUG_WARN, "NTBT: Invalid URI https:// is required\n"));
+ mAllowHttpBoot = FALSE;
+ }
+ }
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ImageHandle,
+ &gOcBootEntryProtocolGuid,
+ &mNetworkBootEntryProtocol,
+ NULL
+ );
+ }
+
+ if (ParsedLoadOptions != NULL) {
+ OcFlexArrayFree (&ParsedLoadOptions);
+ }
+
+ if (EFI_ERROR (Status) && (mHttpBootUri != NULL)) {
+ FreePool (mHttpBootUri);
+ }
+
+ return Status;
+}
diff --git a/Platform/OpenNetworkBoot/OpenNetworkBoot.inf b/Platform/OpenNetworkBoot/OpenNetworkBoot.inf
new file mode 100644
index 00000000000..b892e86b11f
--- /dev/null
+++ b/Platform/OpenNetworkBoot/OpenNetworkBoot.inf
@@ -0,0 +1,54 @@
+## @file
+# Boot entry protocol handler for PXE and HTTP Boot.
+#
+# Copyright (C) 2024, Mike Beaton. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = OpenNetworkBoot
+ ENTRY_POINT = UefiMain
+ FILE_GUID = CA2BA189-98E8-4126-ABA5-2CE5C9B4C2D8
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+
+[Packages]
+ OpenCorePkg/OpenCorePkg.dec
+ MdePkg/MdePkg.dec
+ NetworkPkg/NetworkPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ DevicePathLib
+ HttpLib
+ OcConsoleLib
+ OcBootManagementLib
+ OcFlexArrayLib
+ OcVirtualFsLib
+ PrintLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Protocols]
+ gEfiHttpBootCallbackProtocolGuid ## CONSUMES
+ gEfiLoadFileProtocolGuid ## CONSUMES
+ gEfiRamDiskProtocolGuid ## SOMETIMES_CONSUMES
+ gOcBootEntryProtocolGuid ## PRODUCES
+
+[Guids]
+ gEfiTlsCaCertificateGuid ## SOMETIMES_CONSUMES ## Variable:L"TlsCaCertificate"
+ gEdkiiHttpTlsCipherListGuid ## SOMETIMES_CONSUMES ## Variable:L"HttpTlsCipherList"
+ gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Check the cert type
+
+[Sources]
+ BmBoot.c
+ BmBootDescription.c
+ HttpBootCallback.c
+ HttpBootCustomRead.c
+ NetworkBootInternal.h
+ OpenNetworkBoot.c
+ TlsAuthConfigImpl.c
+ Uri.c
diff --git a/Platform/OpenNetworkBoot/README.md b/Platform/OpenNetworkBoot/README.md
new file mode 100644
index 00000000000..295c37d1763
--- /dev/null
+++ b/Platform/OpenNetworkBoot/README.md
@@ -0,0 +1,482 @@
+# OpenNetworkBoot
+
+`OpenNetworkBoot` is an OpenCore boot entry protocol driver which provides
+PXE and HTTP boot entries if the underlying firmware supports it, or if
+the required network boot drivers have been loaded using OpenCore. Using the
+additional network boot drivers provided with OpenCore, when needed, HTTP
+boot should be available on most firmware even if not natively supported.
+
+See [OpenCore documentation](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf)
+for information on the optional configuration arguments which are available for this driver.
+
+> **Note**: In this file 'HTTP boot' refers to booting using either
+`http://` or `https://` URIs. The additional steps to configure a certificate for
+`https://` (and to lock `OpenNetworkBoot` to `https://` only, if required)
+are covered below.
+
+## PXE Boot
+
+On almost all firmware, the drivers for PXE boot will already be present;
+adding `OpenNetworkBoot.efi` to the OpenCore drivers should produce PXE
+boot entries.
+
+> **Note**: On some firmware (e.g. HP) the native network boot drivers are not loaded
+if the system boots directly to OpenCore and it is necessary to start
+OpenCore from the firmware boot menu in order to see PXE and HTTP boot entries.
+(Alternatively, it should be possible to load the network boot stack provided with
+OpenCore, see end of this document.)
+
+## HTTP Boot
+
+On most recent firmware either no or only a few additional drivers are needed
+for HTTP boot, as most of the required drivers are already present in firmware.
+
+After adding `OpenNetworkBoot`, if no HTTP boot entries are seen,
+try adding just the driver `HttpBootDxe`. If this does not produce
+network boot entries, try also adding `HttpDxe` and `HttpUtilitiesDxe`.
+If `http://` URIs can be booted but not `https://` try adding `TlsDxe.efi`.
+
+If the above steps do not work, proceed to the next section to identify
+which drivers are required.
+
+> **Note 1**: When using `https://` as opposed to `http://` URIs, one or
+more certificates, as required to validate the connection, must
+be configured on the network boot client. This can be done using
+OpenNetworkBoot's certificate configuration options, as documented in the
+[OpenCore documentation](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf).
+
+> **Note 2**: In some firmware the existing `HttpBootDxe` driver may produce
+options which do not work correctly (e.g. blank screen when selected,
+because they are designed to work with a firmware UI which is not present
+when OpenCore is running).
+If so, in order to get working HTTP Boot options it may be necessary to use
+the `UEFI/Unload` config setting to unload the existing `HttpBootDxe` driver
+before loading the `HttpBootDxe` driver provided with OpenCore.
+
+> **Note 3**: In some firmware the existing `HttpDxe` (and `HttpBootDxe`) drivers
+may be locked down to `https://` URIs (even if the machine
+has no BIOS UI for HTTP Boot; e.g. Dell OptiPlex 3070).
+This means that while the `HttpBootDxe` from OpenCore can
+work with the native `HttpDxe`, it will only boot from `https://` URIs, giving a
+failure message otherwise. If `http://` URIs are required, this limitation can
+be worked around by using the `UEFI/Unload` config setting to unload the existing
+`HttpDxe` driver before loading the `HttpDxe` driver provided with OpenCore.
+
+> **Note 4**: During HTTP Boot '*Error: Could not retrieve NBP file size from HTTP server*'
+is a very generic error message for 'something went wrong'.
+It could be that `http://` URIs are not allowed by `HttpDxe` or `HttpBootDxe`,
+or that a file does not exist at the specified URI on the server, or that the certificates
+(if any) stored in NVRAM could not be used to validate an `https://` URI, or any one of a number
+of other similar problems.
+
+> **Note 5**: During HTTP Boot, an initial error such as 'IP address not found' or 'Server response timeout',
+even if preceded by the above message, may mean that no IP address was issued by DHCP, or
+that the additional NBP (i.e. boot file) info requested over DHCP was not found. Using `dnsmasq` as the DHCP helper
+with the logging options shown below can be helpful to resolve this: any DHCP request which reaches `dnsmasq`
+will show a couple of log lines. If these are not seen during a network boot attempt then there is no chance of
+`dnsmasq` responding, and network connectivity issues need to be resolved. (It is worth noting that unless blocked,
+this DHCP request traffic will normally be broadcast between local networks.) If these log lines are seen but `dnsmasq` does not
+additionally log that it is responding with NBP information, make sure that it is configured with the correct
+subnetwork for the client machine. It can help to boot the client computer into an OS to confirm which subnetwork
+it is on.
+
+## Identifying missing network boot drivers
+
+The `dh` command in UEFI Shell (e.g. `OpenShell` provided with
+OpenCore) is useful for working out which drivers are missing for network
+boot.
+
+`dh -p LoadFile` shows available network boot entries. Handles with a device
+path ending in an IPv4 or IPv6 address should indicate available PXE boot
+options. Handles with a device path ending in `Uri(...)` should indicate
+available HTTP boot options
+
+> **Note 1**: On some systems, there may be additional
+`LoadFile` handles with vendor-specific device paths. These may correspond, for
+instance, to GUI network boot options. These will not produce boot entries when
+using OpenNetworkBoot.
+
+After identifying the handles for network boot entries,
+the other handles just before and after these, in the full
+list of handles displayed by `dh`, should correspond to the currently loaded
+network boot drivers. If there are no LoadFile options, then
+search in the full handle list for strings such as 'tcp', 'tls', 'http'
+(normally the native network boot drivers will appear grouped together).
+Examining the names printed by `dh` for these handles
+and comparing them to the available network boot drivers (see Network Boot Stack
+section) can be used to identify missing drivers.
+
+> **Note 2**: On systems with reasonably fast console text output, the `-b`
+option can be used with `dh` (as with most UEFI Shell commands) to
+display results one page at a time.
+
+> **Note 3**: For systems with slow console output, it may be more
+convenient to pipe the results of `dh` to a file on a convenient file system
+to examine later, within a booted OS. For example `dh > fs0:\dh_dump` or:
+
+```
+> fs0:
+> dh > dh_dump
+```
+
+## Configuring a network boot server
+
+In standard PXE and HTTP boot, the network's normal DHCP server responds to a
+client device's request for an IP address, but a separate DHCP helper service
+(often running on a different machine from the DHCP server) responds to the
+device's DHCP extension request to know which network boot program (NBP) to
+load. It is possible (but less standard, even on enterprise networks;
+and usually more complex) to configure the main DHCP server to respond to both
+requests.
+
+**Note 1**: The NBP for HTTP boot can be configured by specifying the `--uri`
+flag for `OpenNetworkBoot`. When using this option, only an HTTP server (and
+certificate, for HTTPS), needs to be set up, but no DHCP helper service is
+required.
+
+**Note 2**: No equivalent NBP path option is provided for PXE boot, since the most
+standard (and recommended) set up is for the program specifying the
+NBP file and the program serving it via TFTP to be one and the same.
+
+### PXE Boot
+
+In PXE boot, the NBP is loaded via TFTP, which is a slow protocol not suitable
+for large files. Standard PXE boot NBPs typically load any further large files
+which they need using their own network stack and not via TFTP.
+
+`dnsmasq`, WDS, or other options, such as FOGProject, may be used to specify
+PXE boot responses.
+
+#### dnsmasq
+
+`dnsmasq` can be used to both provide the location of the PXE boot NBP file
+and then serve it by TFTP.
+
+A basic `dnsmasq` PXE boot configuration is as follows:
+
+```
+# Disable DNS Server
+port=0
+
+# Run as network boot DHCP proxy
+dhcp-range=192.168.10.0,proxy
+
+# Identify requested arch
+# REF: https://wiki.archlinux.org/title/dnsmasq#PXE_server
+dhcp-match=set:arch_x64,option:client-arch,7
+dhcp-match=set:arch_x64,option:client-arch,9
+dhcp-match=set:arch_ia32,option:client-arch,6
+
+# Specify one or more boot options per architecture
+pxe-service=tag:arch_x64,x86-64_EFI,"Open Shell",OpenShell.efi
+pxe-service=tag:arch_x64,x86-64_EFI,"Boot Helper",BootHelper.efi
+
+# Enable TFTP support
+enable-tftp
+tftp-root=/home/mjsbeaton/tftp
+```
+
+A more advanced configuration might serve different files to different
+machines, depending on their hardware id. (The same point applies to
+HTTP boot.) See `dnsmasq` documentation.
+
+See **HTTP Boot** **dnsmasq** section below for command to quickly start
+`dnsmasq` for testing.
+
+#### WDS
+
+Windows Deployment Services (WDS, which is incuded with Windows Server) can be
+used to provide responses to PXE boot requests, and can be configured to serve
+non-Windows NBP files.
+
+**Note 1**: Certain aspects of WDS are now deprecated:
+https://aka.ms/WDSSupport
+
+**Note 2**: On certain systems, the OpenCore `TextRenderer` setting
+must be set to one of the `System` values in order to see the early output of
+`wdsmgfw.efi` (the NBP served by default by WDS). If this text is not visible,
+this can be worked round by pressing either `F12` or `Enter` in the pause
+after the program has loaded, in order to access the next screen.
+The issue of the early text of this software not appearing in some circumstances
+is not unique to OpenCore: https://serverfault.com/q/683314
+
+### HTTP Boot
+
+#### dnsmasq
+
+Although `dnsmasq` does not provide complete support for HTTP
+boot, as it does for PXE boot, its PXE boot features can be used
+to respond to requests for the location of HTTP boot NBP files.
+
+A basic `dnsmasq` HTTP boot configuration is as follows:
+
+```
+# Disable DNS Server
+port=0
+
+# Run as PXE Boot DHCP proxy for specified network (use default /24 network size)
+dhcp-range=192.168.2.0,proxy
+
+# Trigger PXE Boot support on HTTP Boot client request
+dhcp-pxe-vendor=HTTPClient
+
+# Set triggering tag if correct arch is present in option 60
+dhcp-match=set:arch_x64,option:client-arch,16
+
+# Make PXE Boot support believe it has something to send...
+pxe-service=tag:arch_x64,x86-64_EFI,"Network Boot"
+
+# Specify bootfile-name via PXE Boot setting
+dhcp-boot=tag:arch_x64,https://michaels-air.lan:8443/images/OpenShell.efi
+
+# Force required vendor class in response, even if not requested
+dhcp-option-force=tag:arch_x64,option:vendor-class,HTTPClient
+```
+
+To quickly start `dnsmasq` for testing, without running it as a server,
+the following command can be used:
+
+```
+sudo dnsmasq --no-daemon -C dnsmasq.conf --log-dhcp --log-debug
+```
+
+An HTTP server (such as Apache, nginx, or multiple other options) will be
+required to serve the actual NBP files over `http://` or `https://`.
+
+References:
+ - https://github.com/ipxe/ipxe/discussions/569
+ - https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/msg16278.html
+
+### HTTPS Boot
+
+Note that the certificate for validating `https://` requests should be loaded into
+firmware using the OpenNetworkBoot `--enroll-cert` option.
+
+A normal https site would not serve files using a self-signed certificate
+authority (CA), but since we are only attempting to serve files to HTTP boot
+clients, in this case we can do so.
+
+## Booting ISO and IMG files
+
+Though not often noted in the documentation, the majority of HTTP Boot
+implementations support loading `.iso` and `.img` files, which will be
+automatically mounted as a ramdisk. If the mounted filesystem includes
+`\EFI\BOOT\BOOTx64.efi` (or `\EFI\BOOT\BOOTIA32.efi` for 32-bit) then this
+file will be loaded from the ramdisk and started.
+
+The MIME types corresponding to `.iso` and `.img` files are:
+
+ - `application/vnd.efi-iso`
+ - `application/vnd.efi-img`
+
+The MIME type for `.efi` files is:
+
+ - `application/efi`
+
+If the MIME type is none of the above, then the corresponding file
+extensions (`.iso`, `.img` and `.efi`) are used instead to identify the NBP
+file type.
+
+Additionally, for boot options generated by `OpenNetworkBoot`, `.dmg`
+and `.chunklist` files will be recognised by extension and loaded,
+regardless of MIME type.
+
+> **Note**: Files which cannot be recognised by any of the above MIME types or
+file extensions will _not_ be loaded by HTTP boot drivers.
+
+## Booting DMG files
+
+In order to allow booting macOS Recovery, `OpenNetworkBoot` includes
+additional support for loading `.dmg` files via HTTP boot. If the NBP
+filename is `{filename}.dmg` or `{filename}.chunklist` then the other
+file of this pair will be automatically loaded, in order to allow DMG
+chunklist verification, and both files will be used for OpenCore DMG booting.
+
+### `DmgLoading` configuration setting
+
+The behaviour of `OpenNetworkBoot`'s DMG support depends on the OpenCore
+`DmgLoading` setting as follows:
+
+ - If `DmgLoading` is set to `Signed` then both `.chunklist` and `.dmg` files
+must be available from the HTTP server. Either file can be specified as
+the NBP, and the other matching file will be loaded afterwards, automatically.
+ - If `DmgLoading` is set to `Disabled` and either of these two file extensions
+are found as the NBP, then the HTTP boot process will be aborted. (If we allowed
+these files to load and then passed them to the OpenCore DMG loading process,
+they would be rejected at that point anyway.)
+ - If `DmgLoading` is set to `Any` and the NBP is `{filename}.dmg` then only
+the `.dmg` file will be loaded, as verification via `.chunklist` is not
+carried out with this setting. If the NBP is `{filename}.chunklist` then
+the `.chunklist` followed by the `.dmg` will be loaded, but only the `.dmg`
+will be used.
+
+## OVMF
+
+OVMF can be compiled with the following flags for full network boot support:
+
+`-D NETWORK_HTTP_BOOT_ENABLE -D NETWORK_TLS_ENABLE -D NETWORK_IP6_ENABLE`
+
+Since a May 2024 security update to the network boot stack, Random
+Number Generator (RNG) protocol support is required. If running OVMF
+with an Ivy Bridge or above CPU, then the `RngDxe` driver included in
+OVMF will provide the required support. For CPUs below Ivy Bridge
+the qemu option `-device virtio-rng-pci` must be provided, so that
+the `VirtioRngDxe` driver which is also present in OVMF can provide
+the required RNG support.
+
+If OVMF is compiled without network boot support (`-D NETWORK_ENABLE=0`), then
+network boot support provided with OpenCore may be added by loading the full
+Network Boot Stack (see below).
+
+### OVMF networking
+
+If any network boot clients (e.g. OVMF, VMWare) or server programs
+(e.g. Apache, `dnsmasq`, WDS) are running on VMs, it is normally easiest
+to set these up using bridged network support, which allows the VM to
+appear as a separate device with its own IP address on the network.
+
+To start OVMF with bridged network support the following macOS-specific
+`qemu` options (which require `sudo`) may be used:
+
+```
+-netdev vmnet-bridged,id=mynet0,ifname=en0 \
+-device e1000,netdev=mynet0,id=mynic0
+```
+
+PXE boot may also be tested in OVMF using qemu's built-in TFTP/PXE server,
+available with the qemu user mode network stack, for example using the
+following options:
+
+```
+-netdev user,id=net0,tftp=/Users/user/tftp,bootfile=/OpenShell.efi \
+-device virtio-net-pci,netdev=net0
+```
+
+No equivalent option is available for HTTP boot, so to experiment with this,
+a combination such as bridged networking and `dnsmasq` should be used.
+
+### OVMF HTTPS certificate
+
+When using `https://` as opposed to `http://`, a certificate must be
+configured on the network boot client. Within the OVMF menus this may
+be done using
+`Device Manager/Tls Auth Configuration/Server CA Configuration/Enroll Cert/Enroll Cert Using File`.
+
+> **Tip**: No GUID needs to be provided in the above dialog. All zeroes will be
+used if nothing is specified, which is fine if only a single certificate is going
+to be configured and managed.
+
+### Debugging network boot on OVMF
+
+Building OVMF with the `-D DEBUG_ON_SERIAL_PORT` option and then passing the
+`-serial stdio` option to qemu (and then scrolling back in the output as
+needed, to the lines generated during a failed network boot) can be very
+useful when trying to debug network boot setup.
+
+OVMF can capture packets using
+`-object filter-dump,netdev={net-id},id=filter0,file=/Users/user/ovmf.cap`
+(`{net-id}` should be replaced as appropriate with the `id` value specified in the
+corresponding `-netdev` option).
+
+For additional information on debugging OpenCore using OVMF,
+see https://github.com/acidanthera/OpenCorePkg/blob/master/Debug/README.md.
+
+## Network Boot Stack
+
+The following drivers supplied with OpenCore make up the network boot
+stack. Please follow the procedures given towards the start of this
+document for deciding which drivers to add.
+
+### Prerequisites
+Various network boot drivers depend on the presence of HiiDatabase.
+
+A recent (May 2024) security update to the EDK 2 network stack
+means that various drivers also depend on the RNG and Hash2 protocols.
+
+These protocols can be checked for in UEFI Shell with:
+
+```
+dh -p HIIDatabase
+dh -p Rng
+dh -p Hash2
+```
+
+If not present, the respective drivers should be loaded before
+the network boot stack.
+
+```
+HiiDatabase
+RngDxe
+Hash2DxeCrypto
+```
+
+### RAM Disk
+Required if not already present in firmware, and the user wishes
+to load `.iso` or `.img` files with HTTP Boot.
+
+Can be checked for in UEFI Shell with `dh -p RamDisk`.
+
+```
+RamDiskDxe
+```
+
+### Network Boot Base
+```
+DpcDxe
+SnpDxe
+MnpDxe
+TcpDxe
+```
+
+### IPv4
+```
+ArpDxe
+Dhcp4Dxe
+Ip4Dxe
+Udp4Dxe
+```
+
+### IPv6
+```
+Dhcp6Dxe
+Ip6Dxe
+Udp6Dxe
+```
+
+### HTTP Boot
+```
+DnsDxe
+HttpDxe
+HttpUtilitiesDxe
+HttpBootDxe
+```
+
+### HTTPS (TLS) support for HTTP Boot
+```
+TlsDxe
+```
+
+### PXE Boot
+```
+UefiPxeBcDxe
+Mtftp4Dxe (IPv4 only)
+Mtftp6Dxe (IPv6 only)
+```
+
+### Firmware and option ROMs
+
+In many situations network card firmware will already be present, but this section covers situations where it may not be.
+
+The EDK II and AUDK versions of OVMF both include `VirtioNetDxe` by default, even if built with `NETWORK_ENABLE=0`.
+This is roughly equivalent to saying that OVMF has network card firmware present, even if the EDK II network stack
+is not included. Therefore, note that when using a cut-down or custom build of OVMF, this driver must be present in
+order for the rest of the network stack to work.
+
+Most (U)EFI machines include PXE boot which relies on the machine's network card firmware
+(e.g. option ROM) being present. However, if using a very old (e.g pre-EFI) machine, or one with very
+cut-down firmware, it may be necessary to manually load the network card's (U)EFI firmware in order for the rest
+of the network boot stack to work. This may be loaded using OpenCore's `Drivers` section. The driver should be
+found from the manufacturer's website or elsewhere online; for example:
+
+ - https://winraid.level1techs.com/t/efi-lan-bios-intel-gopdriver-modules/33948
+ - https://www.intel.com/content/www/us/en/download/15755/intel-ethernet-connections-boot-utility-preboot-images-and-efi-drivers.html
diff --git a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c
index 9c0acaeeece..ed2aa313e23 100644
--- a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c
+++ b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c
@@ -1,98 +1,67 @@
/** @file
- The Miscellaneous Routines for TlsAuthConfigDxe driver.
+ Miscellaneous routines for TLS auth config.
-Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
-SPDX-License-Identifier: BSD-2-Clause-Patent
+#include "NetworkBootInternal.h"
-**/
+#define TLS_AUTH_CONFIG_VAR_BASE_ATTR (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)
-#include "TlsAuthConfigImpl.h"
+typedef
+EFI_STATUS
+(*PROCESS_CERT)(
+ IN VOID *Context,
+ IN UINTN CertIndex,
+ IN UINTN CertSize,
+ IN EFI_SIGNATURE_DATA *Cert
+ );
+
+typedef struct {
+ EFI_GUID *OwnerGuid;
+ UINTN X509DataSize;
+ VOID *X509Data;
+} CERT_IS_PRESENT_CONTEXT;
/**
- List all cert in specified database by GUID in the page
- for user to select and delete as needed.
+ Perform action for all signatures in specified database, with
+ possibility of aborting early.
- @param[in] PrivateData Module's private data.
@param[in] VariableName The variable name of the vendor's signature database.
- @param[in] VendorGuid A unique identifier for the vendor.
- @param[in] LabelNumber Label number to insert opcodes.
- @param[in] FormId Form ID of current page.
- @param[in] QuestionIdBase Base question id of the signature list.
-
- @retval EFI_SUCCESS Success to update the signature list page
- @retval EFI_OUT_OF_RESOURCES Unable to allocate required resources.
+ @param[in] VendorGuid A unique identifier for the signature database vendor.
+ @param[in] ProcessCert The method to call for each certificate.
+ @param[in] Context Context for ProcessCert, if required.
+ @retval EFI_SUCCESS Looped over all signatures.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+ @retval Other Other error or return code from from ProcessCert.
**/
+STATIC
EFI_STATUS
-UpdateDeletePage (
- IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private,
- IN CHAR16 *VariableName,
- IN EFI_GUID *VendorGuid,
- IN UINT16 LabelNumber,
- IN EFI_FORM_ID FormId,
- IN EFI_QUESTION_ID QuestionIdBase
+ProcessAllCerts (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN PROCESS_CERT ProcessCert,
+ IN VOID *Context OPTIONAL
)
{
EFI_STATUS Status;
UINT32 Index;
UINTN CertCount;
UINTN GuidIndex;
- VOID *StartOpCodeHandle;
- VOID *EndOpCodeHandle;
- EFI_IFR_GUID_LABEL *StartLabel;
- EFI_IFR_GUID_LABEL *EndLabel;
UINTN DataSize;
UINT8 *Data;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINT32 ItemDataSize;
- CHAR16 *GuidStr;
- EFI_STRING_ID GuidID;
- EFI_STRING_ID Help;
-
- Data = NULL;
- CertList = NULL;
- Cert = NULL;
- GuidStr = NULL;
- StartOpCodeHandle = NULL;
- EndOpCodeHandle = NULL;
-
- //
- // Initialize the container for dynamic opcodes.
- //
- StartOpCodeHandle = HiiAllocateOpCodeHandle ();
- if (StartOpCodeHandle == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
- }
- EndOpCodeHandle = HiiAllocateOpCodeHandle ();
- if (EndOpCodeHandle == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
- }
+ ASSERT (ProcessCert != NULL);
- //
- // Create Hii Extend Label OpCode.
- //
- StartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode (
- StartOpCodeHandle,
- &gEfiIfrTianoGuid,
- NULL,
- sizeof (EFI_IFR_GUID_LABEL)
- );
- StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
- StartLabel->Number = LabelNumber;
-
- EndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode (
- EndOpCodeHandle,
- &gEfiIfrTianoGuid,
- NULL,
- sizeof (EFI_IFR_GUID_LABEL)
- );
- EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
- EndLabel->Number = LABEL_END;
+ Data = NULL;
+ CertList = NULL;
+ Cert = NULL;
//
// Read Variable.
@@ -100,24 +69,22 @@ UpdateDeletePage (
DataSize = 0;
Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data);
if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) {
- goto ON_EXIT;
+ if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
}
Data = (UINT8 *)AllocateZeroPool (DataSize);
if (Data == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
+ return EFI_OUT_OF_RESOURCES;
}
Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data);
if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
-
- GuidStr = AllocateZeroPool (100);
- if (GuidStr == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
+ FreePool (Data);
+ return Status;
}
//
@@ -128,9 +95,7 @@ UpdateDeletePage (
GuidIndex = 0;
while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
- if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
- Help = STRING_TOKEN (STR_CERT_TYPE_PCKS_GUID);
- } else {
+ if (!CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
//
// The signature type is not supported in current implementation.
//
@@ -139,86 +104,165 @@ UpdateDeletePage (
continue;
}
+ Status = EFI_SUCCESS;
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
for (Index = 0; Index < CertCount; Index++) {
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList
+ sizeof (EFI_SIGNATURE_LIST)
+ CertList->SignatureHeaderSize
+ Index * CertList->SignatureSize);
- //
- // Display GUID and help
- //
- GuidToString (&Cert->SignatureOwner, GuidStr, 100);
- GuidID = HiiSetString (Private->RegisteredHandle, 0, GuidStr, NULL);
- HiiCreateCheckBoxOpCode (
- StartOpCodeHandle,
- (EFI_QUESTION_ID)(QuestionIdBase + GuidIndex++),
- 0,
- 0,
- GuidID,
- Help,
- EFI_IFR_FLAG_CALLBACK,
- 0,
- NULL
- );
+
+ Status = ProcessCert (Context, GuidIndex, CertList->SignatureSize, Cert);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ ++GuidIndex;
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
}
ItemDataSize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
}
-ON_EXIT:
- HiiUpdateForm (
- Private->RegisteredHandle,
- &gTlsAuthConfigGuid,
- FormId,
- StartOpCodeHandle,
- EndOpCodeHandle
- );
-
- if (StartOpCodeHandle != NULL) {
- HiiFreeOpCodeHandle (StartOpCodeHandle);
- }
+ FreePool (Data);
- if (EndOpCodeHandle != NULL) {
- HiiFreeOpCodeHandle (EndOpCodeHandle);
- }
+ return Status;
+}
- if (Data != NULL) {
- FreePool (Data);
+/**
+ @retval EFI_SUCCESS Continue processing.
+**/
+STATIC
+EFI_STATUS
+LogCert (
+ IN VOID *Context,
+ IN UINTN CertIndex,
+ IN UINTN CertSize,
+ IN EFI_SIGNATURE_DATA *Cert
+ )
+{
+ DEBUG ((DEBUG_INFO, "NTBT: Cert %u owner %g\n", CertIndex, &Cert->SignatureOwner));
+ return EFI_SUCCESS;
+}
+
+/**
+ Log owner GUID of each installed certificate in signature database.
+
+ @param[in] VariableName The variable name of the signature database.
+ @param[in] VendorGuid A unique identifier for the signature database vendor.
+
+ @retval EFI_SUCCESS Success.
+**/
+EFI_STATUS
+LogInstalledCerts (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid
+ )
+{
+ DEBUG ((DEBUG_INFO, "NTBT: Listing installed certs...\n"));
+ return ProcessAllCerts (
+ VariableName,
+ VendorGuid,
+ LogCert,
+ NULL
+ );
+}
+
+/**
+ @retval EFI_SUCCESS Certificate not found; continue processing.
+ @retval EFI_ALREADY_STARTED Certificate found; stop processing.
+**/
+STATIC
+EFI_STATUS
+CheckCertPresent (
+ IN VOID *VoidContext,
+ IN UINTN CertIndex,
+ IN UINTN CertSize,
+ IN EFI_SIGNATURE_DATA *Cert
+ )
+{
+ CERT_IS_PRESENT_CONTEXT *Context;
+
+ Context = VoidContext;
+
+ if (!CompareGuid (&Cert->SignatureOwner, Context->OwnerGuid)) {
+ return EFI_SUCCESS;
}
- if (GuidStr != NULL) {
- FreePool (GuidStr);
+ if ( (CertSize == sizeof (EFI_SIGNATURE_DATA) - 1 + Context->X509DataSize)
+ && (CompareMem (Cert->SignatureData, Context->X509Data, Context->X509DataSize) == 0)
+ )
+ {
+ return EFI_ALREADY_STARTED;
}
return EFI_SUCCESS;
}
/**
- Delete one entry from cert database.
+ Report whether specified signature is already enrolled for given owner.
+
+ @param[in] VariableName Variable name of CA database.
+ @param[in] VendorGuid Unique identifier for the CA database vendor.
+ @param[in] OwnerGuid Unique identifier for owner of the certificate to be searched for.
+ @param[in] X509DataSize Certificate data size.
+ @param[in] X509Data Certificate data.
+
+ @retval EFI_SUCCESS Certificate is already enrolled.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+**/
+EFI_STATUS
+CertIsPresent (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN EFI_GUID *OwnerGuid,
+ IN UINTN X509DataSize,
+ IN VOID *X509Data
+ )
+{
+ CERT_IS_PRESENT_CONTEXT Context;
+
+ ASSERT (X509Data != NULL);
+
+ Context.OwnerGuid = OwnerGuid;
+ Context.X509DataSize = X509DataSize;
+ Context.X509Data = X509Data;
+
+ return ProcessAllCerts (
+ VariableName,
+ VendorGuid,
+ CheckCertPresent,
+ &Context
+ );
+}
+
+/**
+ Delete specific entry or all entries with owner guid from signature database.
+ (Based on original EDK 2 DeleteCert which removes one cert, identified by index.)
- @param[in] Private Module's private data.
- @param[in] VariableName The variable name of the database.
- @param[in] VendorGuid A unique identifier for the vendor.
- @param[in] LabelNumber Label number to insert opcodes.
- @param[in] FormId Form ID of current page.
- @param[in] QuestionIdBase Base question id of the cert list.
- @param[in] DeleteIndex Cert index to delete.
+ @param[in] VariableName The variable name of the signature database.
+ @param[in] VendorGuid A unique identifier for the signature database vendor.
+ @param[in] OwnerGuid A unique identifier for owner of the certificate(s) to be deleted.
+ @param[in] X509DataSize Optional certificate data size.
+ @param[in] X509Data Optional certificate data. If non-NULL, delete only specific certificate
+ for owner, if present. If NULL, delete all certificates for owner.
+ @param[in] DeletedCount Optional return count of deleted certificates.
@retval EFI_SUCCESS Delete signature successfully.
- @retval EFI_NOT_FOUND Can't find the signature item,
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
**/
EFI_STATUS
-DeleteCert (
- IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private,
- IN CHAR16 *VariableName,
- IN EFI_GUID *VendorGuid,
- IN UINT16 LabelNumber,
- IN EFI_FORM_ID FormId,
- IN EFI_QUESTION_ID QuestionIdBase,
- IN UINTN DeleteIndex
+DeleteCertsForOwner (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN EFI_GUID *OwnerGuid,
+ IN UINTN X509DataSize,
+ IN VOID *X509Data,
+ OUT UINTN *DeletedCount
)
{
EFI_STATUS Status;
@@ -232,9 +276,16 @@ DeleteCert (
EFI_SIGNATURE_DATA *Cert;
UINTN CertCount;
UINT32 Offset;
- BOOLEAN IsItemFound;
+ UINTN LocalDeleteCount;
UINT32 ItemDataSize;
- UINTN GuidIndex;
+
+ ASSERT ((X509Data == NULL) || (X509DataSize != 0));
+
+ if (DeletedCount == NULL) {
+ DeletedCount = &LocalDeleteCount;
+ }
+
+ *DeletedCount = 0;
Data = NULL;
OldData = NULL;
@@ -248,37 +299,39 @@ DeleteCert (
DataSize = 0;
Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL);
if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) {
- goto ON_EXIT;
+ if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
}
- OldData = (UINT8 *)AllocateZeroPool (DataSize);
+ OldData = AllocateZeroPool (DataSize);
if (OldData == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
+ return EFI_OUT_OF_RESOURCES;
}
Status = gRT->GetVariable (VariableName, VendorGuid, &Attr, &DataSize, OldData);
if (EFI_ERROR (Status)) {
- goto ON_EXIT;
+ FreePool (OldData);
+ return Status;
}
//
// Allocate space for new variable.
//
- Data = (UINT8 *)AllocateZeroPool (DataSize);
+ Data = AllocateZeroPool (DataSize);
if (Data == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
+ FreePool (OldData);
+ return EFI_OUT_OF_RESOURCES;
}
//
- // Enumerate all data and erasing the target item.
+ // Enumerate all data, erasing target items.
//
- IsItemFound = FALSE;
ItemDataSize = (UINT32)DataSize;
CertList = (EFI_SIGNATURE_LIST *)OldData;
Offset = 0;
- GuidIndex = 0;
while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
//
@@ -290,12 +343,19 @@ DeleteCert (
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
for (Index = 0; Index < CertCount; Index++) {
- if (GuidIndex == DeleteIndex) {
+ if ( CompareGuid (&Cert->SignatureOwner, OwnerGuid)
+ && ( (X509Data == NULL)
+ || ( (CertList->SignatureSize == (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize))
+ && (CompareMem ((UINT8 *)(Cert->SignatureData), X509Data, X509DataSize) == 0)
+ )
+ )
+ )
+ {
//
// Find it! Skip it!
//
NewCertList->SignatureListSize -= CertList->SignatureSize;
- IsItemFound = TRUE;
+ ++(*DeletedCount);
} else {
//
// This item doesn't match. Copy it to the Data buffer.
@@ -304,7 +364,6 @@ DeleteCert (
Offset += CertList->SignatureSize;
}
- GuidIndex++;
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize);
}
} else {
@@ -319,85 +378,67 @@ DeleteCert (
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
}
- if (!IsItemFound) {
+ if (*DeletedCount != 0) {
//
- // Doesn't find the signature Item!
+ // Delete the EFI_SIGNATURE_LIST header if there is no signature remaining in any list.
//
- Status = EFI_NOT_FOUND;
- goto ON_EXIT;
- }
+ ItemDataSize = Offset;
+ CertList = (EFI_SIGNATURE_LIST *)Data;
+ Offset = 0;
+ ZeroMem (OldData, ItemDataSize);
+ while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ if (CertCount != 0) {
+ CopyMem (OldData + Offset, (UINT8 *)(CertList), CertList->SignatureListSize);
+ Offset += CertList->SignatureListSize;
+ }
- //
- // Delete the EFI_SIGNATURE_LIST header if there is no signature in the list.
- //
- ItemDataSize = Offset;
- CertList = (EFI_SIGNATURE_LIST *)Data;
- Offset = 0;
- ZeroMem (OldData, ItemDataSize);
- while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
- CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
- DEBUG ((DEBUG_INFO, " CertCount = %x\n", CertCount));
- if (CertCount != 0) {
- CopyMem (OldData + Offset, (UINT8 *)(CertList), CertList->SignatureListSize);
- Offset += CertList->SignatureListSize;
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
}
- ItemDataSize -= CertList->SignatureListSize;
- CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
- }
-
- DataSize = Offset;
-
- Status = gRT->SetVariable (
- VariableName,
- VendorGuid,
- Attr,
- DataSize,
- OldData
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "Failed to set variable, Status = %r\n", Status));
- goto ON_EXIT;
- }
+ DataSize = Offset;
-ON_EXIT:
- if (Data != NULL) {
- FreePool (Data);
+ //
+ // Set (or delete if everything was removed) the Variable.
+ //
+ Status = gRT->SetVariable (
+ VariableName,
+ VendorGuid,
+ Attr,
+ DataSize,
+ OldData
+ );
}
- if (OldData != NULL) {
- FreePool (OldData);
- }
+ FreePool (Data);
+ FreePool (OldData);
- return UpdateDeletePage (
- Private,
- VariableName,
- VendorGuid,
- LabelNumber,
- FormId,
- QuestionIdBase
- );
+ return Status;
}
/**
Enroll a new X509 certificate into Variable.
- @param[in] PrivateData The module's private data.
@param[in] VariableName Variable name of CA database.
+ @param[in] VendorGuid Unique identifier for the CA database vendor.
+ @param[in] OwnerGuid Unique identifier for owner of the certificate to be installed.
+ @param[in] X509DataSize Certificate data size.
+ @param[in] X509Data Certificate data.
@retval EFI_SUCCESS New X509 is enrolled successfully.
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
-
**/
EFI_STATUS
EnrollX509toVariable (
- IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private,
- IN CHAR16 *VariableName
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN EFI_GUID *OwnerGuid,
+ IN UINTN X509DataSize,
+ IN VOID *X509Data
)
{
EFI_STATUS Status;
- UINTN X509DataSize;
- VOID *X509Data;
EFI_SIGNATURE_LIST *CACert;
EFI_SIGNATURE_DATA *CACertData;
VOID *Data;
@@ -405,33 +446,29 @@ EnrollX509toVariable (
UINTN SigDataSize;
UINT32 Attr;
- X509DataSize = 0;
- SigDataSize = 0;
- DataSize = 0;
- X509Data = NULL;
- CACert = NULL;
- CACertData = NULL;
- Data = NULL;
- Attr = 0;
-
- Status = ReadFileContent (
- Private->FileContext->FHandle,
- &X509Data,
- &X509DataSize,
- 0
- );
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
+ SigDataSize = 0;
+ DataSize = 0;
+ CACert = NULL;
+ CACertData = NULL;
+ Data = NULL;
+ Attr = 0;
ASSERT (X509Data != NULL);
+ //
+ // Note: As implemented in EDK 2, each signature list can have multiple
+ // instances of signature data (owner guid followed by raw signature data),
+ // but every instance in one list must have the same size.
+ // The signature data is the unprocessed contents of a .pem or .der file.
+ // It is not immediately obvious how the multiple signature feature would
+ // be useful as signature file data does not in general have a fixed size
+ // (not even for .pem files: https://security.stackexchange.com/q/152584).
+ //
SigDataSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize;
Data = AllocateZeroPool (SigDataSize);
if (Data == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
+ return EFI_OUT_OF_RESOURCES;
}
//
@@ -444,7 +481,7 @@ EnrollX509toVariable (
CopyGuid (&CACert->SignatureType, &gEfiCertX509Guid);
CACertData = (EFI_SIGNATURE_DATA *)((UINT8 *)CACert + sizeof (EFI_SIGNATURE_LIST));
- CopyGuid (&CACertData->SignatureOwner, Private->CertGuid);
+ CopyGuid (&CACertData->SignatureOwner, OwnerGuid);
CopyMem ((UINT8 *)(CACertData->SignatureData), X509Data, X509DataSize);
//
@@ -454,7 +491,7 @@ EnrollX509toVariable (
//
Status = gRT->GetVariable (
VariableName,
- &gEfiTlsCaCertificateGuid,
+ VendorGuid,
&Attr,
&DataSize,
NULL
@@ -464,35 +501,19 @@ EnrollX509toVariable (
} else if (Status == EFI_NOT_FOUND) {
Attr = TLS_AUTH_CONFIG_VAR_BASE_ATTR;
} else {
- goto ON_EXIT;
+ FreePool (Data);
+ return Status;
}
Status = gRT->SetVariable (
VariableName,
- &gEfiTlsCaCertificateGuid,
+ VendorGuid,
Attr,
SigDataSize,
Data
);
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
-
-ON_EXIT:
- CleanFileContext (Private);
-
- if (Private->CertGuid != NULL) {
- FreePool (Private->CertGuid);
- Private->CertGuid = NULL;
- }
- if (Data != NULL) {
- FreePool (Data);
- }
-
- if (X509Data != NULL) {
- FreePool (X509Data);
- }
+ FreePool (Data);
return Status;
}
diff --git a/Platform/OpenNetworkBoot/Uri.c b/Platform/OpenNetworkBoot/Uri.c
new file mode 100644
index 00000000000..6a3ce120f81
--- /dev/null
+++ b/Platform/OpenNetworkBoot/Uri.c
@@ -0,0 +1,304 @@
+/** @file
+ URI utilities for HTTP Boot.
+
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause
+**/
+
+#include "NetworkBootInternal.h"
+
+STATIC EFI_DEVICE_PATH_PROTOCOL mEndDevicePath = {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ END_DEVICE_PATH_LENGTH,
+ 0
+ }
+};
+
+BOOLEAN
+HasHttpsUri (
+ CHAR16 *Uri
+ )
+{
+ ASSERT (Uri != NULL);
+
+ return (OcStrniCmp (L"https://", Uri, L_STR_LEN (L"https://")) == 0);
+}
+
+EFI_DEVICE_PATH_PROTOCOL *
+GetUriNode (
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Previous;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ for ( Previous = NULL, Node = DevicePath
+ ; !IsDevicePathEnd (Node)
+ ; Node = NextDevicePathNode (Node))
+ {
+ Previous = Node;
+ }
+
+ if ( (Previous != NULL)
+ && (DevicePathType (Previous) == MESSAGING_DEVICE_PATH)
+ && (DevicePathSubType (Previous) == MSG_URI_DP)
+ )
+ {
+ return Previous;
+ }
+
+ return NULL;
+}
+
+//
+// See HttpBootCheckImageType and HttpUrlGetPath within it for how to get
+// the proper filename from URL: we would need to use HttpParseUrl, then
+// HttpUrlGetPath, then HttpUrlFreeParser.
+//
+// However, here are some examples:
+//
+// - https://myserver.com/images/OpenShell.efi
+// - https://myserver.com/download.php?a=1&b=2&filename=OpenShell.efi
+// - https://myserver.com/images/OpenShell.efi?secure=123
+//
+// Rather than parse the URL properly, we can handle all of the above
+// (one of which would not be handled by proper parsing of the filename
+// part, since the filename is in a parameter; even though this is a fudge,
+// as the parameter must come last to be fixed up) if we check if url ends
+// with the file ext, and replace it if so; otherwise check if the part
+// ending at '?' ends with ext and replace it if so.
+//
+STATIC
+EFI_STATUS
+ExtractOtherUriFromUri (
+ IN CHAR8 *Uri,
+ IN CHAR8 *FromExt,
+ IN CHAR8 *ToExt,
+ OUT CHAR8 **OtherUri,
+ IN BOOLEAN OnlySearchForFromExt
+ )
+{
+ CHAR8 *SearchUri;
+ UINTN UriLen;
+ UINTN OtherUriLen;
+ UINTN FromExtLen;
+ UINTN ToExtLen;
+ INTN SizeChange;
+ CHAR8 *ParamsStart;
+
+ ASSERT (FromExt != NULL);
+
+ if (!OnlySearchForFromExt) {
+ ASSERT (ToExt != NULL);
+ ASSERT (OtherUri != NULL);
+ *OtherUri = NULL;
+ }
+
+ UriLen = AsciiStrLen (Uri);
+ if (UriLen > MAX_INTN - 1) {
+ ///< i.e. UriSize > MAX_INTN
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FromExtLen = AsciiStrLen (FromExt);
+ if (FromExtLen > MAX_INTN - 1) {
+ ///< i.e. FromExtSize > MAX_INTN
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (UriLen < FromExtLen) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (OnlySearchForFromExt) {
+ SearchUri = Uri;
+ } else {
+ ToExtLen = AsciiStrLen (ToExt);
+ if (ToExtLen > MAX_INTN - 1) {
+ ///< i.e. ToExtSize > MAX_INTN
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BaseOverflowSubSN ((INTN)ToExtLen, (INTN)FromExtLen, &SizeChange)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BaseOverflowAddSN ((INTN)UriLen, SizeChange, (INTN *)&OtherUriLen)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OtherUriLen > MAX_INTN - 1) {
+ ///< i.e. OtherUriSize > MAX_INTN
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *OtherUri = AllocatePool (MAX (UriLen, OtherUriLen) + 1);
+ if (*OtherUri == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (*OtherUri, Uri, UriLen + 1);
+ SearchUri = *OtherUri;
+ }
+
+ if (AsciiStrnCmp (&SearchUri[UriLen - FromExtLen], FromExt, FromExtLen) == 0) {
+ if (!OnlySearchForFromExt) {
+ CopyMem (&SearchUri[UriLen - FromExtLen], ToExt, ToExtLen + 1);
+ if (SizeChange < -1) {
+ ZeroMem (&SearchUri[UriLen + SizeChange + 1], -(SizeChange + 1));
+ }
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ ParamsStart = AsciiStrStr (SearchUri, "?");
+ if ( (ParamsStart != NULL)
+ && (AsciiStrnCmp (ParamsStart - FromExtLen, FromExt, FromExtLen) == 0))
+ {
+ if (!OnlySearchForFromExt) {
+ CopyMem (ParamsStart + SizeChange, ParamsStart, &SearchUri[UriLen] - ParamsStart + 1);
+ CopyMem (ParamsStart - FromExtLen, ToExt, ToExtLen);
+ if (SizeChange < -1) {
+ ZeroMem (&SearchUri[UriLen + SizeChange + 1], -(SizeChange + 1));
+ }
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ if (!OnlySearchForFromExt) {
+ FreePool (*OtherUri);
+ *OtherUri = NULL;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+//
+// We have the filename in the returned device path, so we can try to load the
+// matching file (.chunklist or .dmg, whichever one was not fetched), if we
+// we find the other.
+//
+EFI_STATUS
+ExtractOtherUriFromDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN CHAR8 *FromExt,
+ IN CHAR8 *ToExt,
+ OUT CHAR8 **OtherUri,
+ IN BOOLEAN OnlySearchForFromExt
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Previous;
+ CHAR8 *Uri;
+
+ ASSERT (DevicePath != NULL);
+
+ Previous = GetUriNode (DevicePath);
+ if (Previous == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Uri = (CHAR8 *)Previous + sizeof (EFI_DEVICE_PATH_PROTOCOL);
+
+ return ExtractOtherUriFromUri (
+ Uri,
+ FromExt,
+ ToExt,
+ OtherUri,
+ OnlySearchForFromExt
+ );
+}
+
+//
+// Determine whether file at URI has extension. This isn't only checking
+// the file part of the URI, instead it first checks the argument to the last
+// param, if there is one. This is a convenience to allow the 'download.php'
+// example shown at ExtractOtherUriFromUri.
+//
+BOOLEAN
+UriFileHasExtension (
+ IN CHAR8 *Uri,
+ IN CHAR8 *Ext
+ )
+{
+ return (!EFI_ERROR (ExtractOtherUriFromUri (Uri, Ext, NULL, NULL, TRUE)));
+}
+
+EFI_STATUS
+HttpBootAddUri (
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ VOID *Uri,
+ OC_STRING_FORMAT StringFormat,
+ EFI_DEVICE_PATH_PROTOCOL **UriDevicePath
+ )
+{
+ UINTN Length;
+ UINTN UriSize;
+ VOID *OriginalUri;
+ EFI_DEVICE_PATH_PROTOCOL *Previous;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+ EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
+
+ ASSERT (UriDevicePath != NULL);
+ *UriDevicePath = NULL;
+
+ TmpDevicePath = DuplicateDevicePath (DevicePath);
+ if (TmpDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // If last non-end node is a URI node, replace it with an end node.
+ //
+ Previous = GetUriNode (TmpDevicePath);
+ if (Previous == NULL) {
+ FreePool (TmpDevicePath);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (Previous, &mEndDevicePath, sizeof (mEndDevicePath));
+
+ //
+ // Create replacement URI node with the input boot file URI.
+ //
+ if (StringFormat == OcStringFormatUnicode) {
+ OriginalUri = Uri;
+ UriSize = StrSize (Uri);
+ Uri = AllocatePool (UriSize * sizeof (CHAR8));
+ if (Uri == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStrS (OriginalUri, Uri, UriSize);
+ } else {
+ OriginalUri = NULL;
+ UriSize = AsciiStrSize (Uri);
+ }
+
+ Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + UriSize;
+ Node = AllocatePool (Length);
+ if (Node == NULL) {
+ FreePool (TmpDevicePath);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Node->Type = MESSAGING_DEVICE_PATH;
+ Node->SubType = MSG_URI_DP;
+ SetDevicePathNodeLength (Node, Length);
+ CopyMem ((UINT8 *)Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Uri, UriSize);
+ if (OriginalUri != NULL) {
+ FreePool (Uri);
+ }
+
+ *UriDevicePath = AppendDevicePathNode (TmpDevicePath, Node);
+ FreePool (Node);
+ FreePool (TmpDevicePath);
+ if (*UriDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/build_oc.tool b/build_oc.tool
index 374fc943918..14f0be50978 100755
--- a/build_oc.tool
+++ b/build_oc.tool
@@ -186,21 +186,27 @@ package() {
"BiosVideo.efi"
"CrScreenshotDxe.efi"
"Dhcp4Dxe.efi"
+ "Dhcp6Dxe.efi"
"DnsDxe.efi"
"DpcDxe.efi"
"Ext4Dxe.efi"
"FirmwareSettingsEntry.efi"
+ "Hash2DxeCrypto.efi"
"HiiDatabase.efi"
"HttpBootDxe.efi"
"HttpDxe.efi"
"HttpUtilitiesDxe.efi"
"Ip4Dxe.efi"
+ "Ip6Dxe.efi"
"MnpDxe.efi"
+ "Mtftp4Dxe.efi"
+ "Mtftp6Dxe.efi"
"NvmExpressDxe.efi"
"OpenCanopy.efi"
"OpenHfsPlus.efi"
"OpenLegacyBoot.efi"
"OpenLinuxBoot.efi"
+ "OpenNetworkBoot.efi"
"OpenNtfsDxe.efi"
"OpenPartitionDxe.efi"
"OpenRuntime.efi"
@@ -208,11 +214,16 @@ package() {
"OpenVariableRuntimeDxe.efi"
"Ps2KeyboardDxe.efi"
"Ps2MouseDxe.efi"
+ "RamDiskDxe.efi"
"ResetNvramEntry.efi"
+ "RngDxe.efi"
"SnpDxe.efi"
"TcpDxe.efi"
+ "TlsDxe.efi"
"ToggleSipEntry.efi"
"Udp4Dxe.efi"
+ "Udp6Dxe.efi"
+ "UefiPxeBcDxe.efi"
"UsbMouseDxe.efi"
"XhciDxe.efi"
)