diff --git a/Changelog.md b/Changelog.md
index b32a14aead5..1448d1d1538 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -3,6 +3,8 @@ OpenCore Changelog
#### v1.0.3
- Fixed support for `AMD_CPU_EXT_FAMILY_1AH`, thx @Shaneee
- Fixed EHCI handoff logic in OpenDuet, causing older machines to hang at start
+- 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 63738e0c97b..fa9d680b88d 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. \\
@@ -7080,9 +7084,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..57065355d88 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,42 @@
Path
Udp4Dxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Dhcp6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Ip6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Udp6Dxe.efi
+
Arguments
@@ -1841,6 +1901,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..3092006b659 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,42 @@
Path
Udp4Dxe.efi
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Dhcp6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Ip6Dxe.efi
+
+
+ Arguments
+
+ Comment
+
+ Enabled
+
+ LoadEarly
+
+ Path
+ Udp6Dxe.efi
+
Arguments
@@ -2209,6 +2269,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 9c49cec5129..6be22731e51 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
new file mode 100644
index 00000000000..69919829be2
--- /dev/null
+++ b/Platform/OpenNetworkBoot/BmBoot.c
@@ -0,0 +1,611 @@
+/** @file
+ 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 "NetworkBootInternal.h"
+
+EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;
+
+/**
+ Expand the media device path which points to a BlockIo or SimpleFileSystem instance
+ by appending EFI_REMOVABLE_MEDIA_FILE_NAME.
+
+ @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+
+ @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,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ VOID *Buffer;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
+ UINTN Size;
+ UINTN TempSize;
+ EFI_HANDLE *SimpleFileSystemHandles;
+ UINTN NumberSimpleFileSystemHandles;
+ UINTN Index;
+ BOOLEAN GetNext;
+
+ GetNext = (BOOLEAN)(FullPath == NULL);
+ //
+ // Check whether the device is connected
+ //
+ TempDevicePath = DevicePath;
+ Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);
+ if (!EFI_ERROR (Status)) {
+ ASSERT (IsDevicePathEnd (TempDevicePath));
+
+ NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
+ //
+ // For device path pointing to simple file system, it only expands to one full path.
+ //
+ if (GetNext) {
+ return NextFullPath;
+ } else {
+ FreePool (NextFullPath);
+ return NULL;
+ }
+ }
+
+ Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // For device boot option only pointing to the removable device handle,
+ // should make sure all its children handles (its child partion or media handles)
+ // are created and connected.
+ //
+ gBS->ConnectController (Handle, NULL, NULL, TRUE);
+
+ //
+ // Issue a dummy read to the device to check for media change.
+ // When the removable media is changed, any Block IO read/write will
+ // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is
+ // returned. After the Block IO protocol is reinstalled, subsequent
+ // Block IO read/write will success.
+ //
+ Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Buffer = AllocatePool (BlockIo->Media->BlockSize);
+ if (Buffer != NULL) {
+ BlockIo->ReadBlocks (
+ BlockIo,
+ BlockIo->Media->MediaId,
+ 0,
+ BlockIo->Media->BlockSize,
+ Buffer
+ );
+ FreePool (Buffer);
+ }
+
+ //
+ // Detect the the default boot file from removable Media
+ //
+ NextFullPath = NULL;
+ Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleFileSystemProtocolGuid,
+ NULL,
+ &NumberSimpleFileSystemHandles,
+ &SimpleFileSystemHandles
+ );
+ for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
+ //
+ // Get the device path size of SimpleFileSystem handle
+ //
+ TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
+ TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH;
+ //
+ // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
+ //
+ if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {
+ NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
+ if (GetNext) {
+ break;
+ } else {
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+ FreePool (NextFullPath);
+ NextFullPath = NULL;
+ }
+ }
+ }
+
+ if (SimpleFileSystemHandles != NULL) {
+ FreePool (SimpleFileSystemHandles);
+ }
+
+ return NextFullPath;
+}
+
+/**
+ Check whether Left and Right are the same without matching the specific
+ device path data in IP device path and URI device path node.
+
+ @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,
+ IN EFI_DEVICE_PATH_PROTOCOL *Right
+ )
+{
+ for ( ; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right)
+ ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right)
+ )
+ {
+ if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) {
+ if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) {
+ return FALSE;
+ }
+
+ if (DevicePathSubType (Left) == MSG_DNS_DP) {
+ Left = NextDevicePathNode (Left);
+ }
+
+ if (DevicePathSubType (Right) == MSG_DNS_DP) {
+ Right = NextDevicePathNode (Right);
+ }
+
+ if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) &&
+ ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) &&
+ ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP))
+ )
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return (BOOLEAN)(IsDevicePathEnd (Left) && IsDevicePathEnd (Right));
+}
+
+/**
+ Get the file buffer from the file system produced by Load File instance.
+
+ @param LoadFileHandle The handle of LoadFile instance.
+ @param RamDiskHandle Return the RAM Disk handle.
+
+ @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,
+ OUT EFI_HANDLE *RamDiskHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiBlockIoProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ if (EFI_ERROR (Status)) {
+ Handles = NULL;
+ HandleCount = 0;
+ }
+
+ Handle = NULL;
+ for (Index = 0; Index < HandleCount; Index++) {
+ Node = DevicePathFromHandle (Handles[Index]);
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status) &&
+ (Handle == LoadFileHandle) &&
+ (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP))
+ {
+ //
+ // Find the BlockIo instance populated from the LoadFile.
+ //
+ Handle = Handles[Index];
+ break;
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ if (Index == HandleCount) {
+ Handle = NULL;
+ }
+
+ *RamDiskHandle = Handle;
+
+ if (Handle != NULL) {
+ //
+ // Re-use BmExpandMediaDevicePath() to get the full device path of load option.
+ // But assume only one SimpleFileSystem can be found under the BlockIo.
+ //
+ return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ Return the RAM Disk device path created by LoadFile.
+
+ @param FilePath The source file path.
+
+ @return Callee-to-free RAM Disk device path
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetRamDiskDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+ EFI_HANDLE Handle;
+
+ Node = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status) &&
+ (DevicePathType (Node) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)
+ )
+ {
+ //
+ // Construct the device path pointing to RAM Disk
+ //
+ Node = NextDevicePathNode (Node);
+ RamDiskDevicePath = DuplicateDevicePath (FilePath);
+ ASSERT (RamDiskDevicePath != NULL);
+ SetDevicePathEndNode ((VOID *)((UINTN)RamDiskDevicePath + ((UINTN)Node - (UINTN)FilePath)));
+ return RamDiskDevicePath;
+ }
+
+ return NULL;
+}
+
+/**
+ Return the buffer and buffer size occupied by the RAM Disk.
+
+ @param RamDiskDevicePath RAM Disk device path.
+ @param RamDiskSizeInPages Return RAM Disk size in pages.
+
+ @retval RAM Disk buffer.
+**/
+STATIC
+VOID *
+BmGetRamDiskMemoryInfo (
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath,
+ OUT UINTN *RamDiskSizeInPages
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ UINT64 StartingAddr;
+ UINT64 EndingAddr;
+
+ ASSERT (RamDiskDevicePath != NULL);
+
+ *RamDiskSizeInPages = 0;
+
+ //
+ // Get the buffer occupied by RAM Disk.
+ //
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle);
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (
+ (DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP)
+ );
+ StartingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->StartingAddr);
+ EndingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->EndingAddr);
+ *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN)(EndingAddr - StartingAddr + 1));
+ return (VOID *)(UINTN)StartingAddr;
+}
+
+/**
+ Destroy the RAM Disk.
+
+ The destroy operation includes to call RamDisk.Unregister to
+ unregister the RAM DISK from RAM DISK driver, free the memory
+ allocated for the RAM Disk.
+
+ @param RamDiskDevicePath RAM Disk device path.
+**/
+VOID
+BmDestroyRamDisk (
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
+ )
+{
+ EFI_STATUS Status;
+ VOID *RamDiskBuffer;
+ UINTN RamDiskSizeInPages;
+
+ ASSERT (RamDiskDevicePath != NULL);
+
+ RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);
+
+ //
+ // Destroy RAM Disk.
+ //
+ if (mRamDisk == NULL) {
+ Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *)&mRamDisk);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ Status = mRamDisk->Unregister (RamDiskDevicePath);
+ ASSERT_EFI_ERROR (Status);
+ FreePages (RamDiskBuffer, RamDiskSizeInPages);
+}
+
+/**
+ Get the file buffer from the specified Load File instance.
+
+ @param LoadFileHandle The specified Load File instance.
+ @param FilePath The file path which will pass to LoadFile().
+
+ @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,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;
+ VOID *FileBuffer;
+ EFI_HANDLE RamDiskHandle;
+ UINTN BufferSize;
+ EFI_DEVICE_PATH_PROTOCOL *FullPath;
+
+ ASSERT (Data != NULL);
+ ASSERT (DataSize != NULL);
+
+ *Data = NULL;
+ *DataSize = 0;
+
+ Status = gBS->OpenProtocol (
+ LoadFileHandle,
+ &gEfiLoadFileProtocolGuid,
+ (VOID **)&LoadFile,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ FileBuffer = NULL;
+ BufferSize = 0;
+ Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
+ if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) {
+ return NULL;
+ }
+
+ //
+ // In call tree of original BmGetLoadOptionBuffer, handling this case
+ // is deferred to subsequent call to GetFileBufferByFilePath.
+ //
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // 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.
+ //
+ 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);
+ }
+
+ //
+ // The load option resides in a RAM disk.
+ //
+ FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));
+ if (FileBuffer == NULL) {
+ DEBUG_CODE_BEGIN ();
+ EFI_DEVICE_PATH *LoadFilePath;
+ CHAR16 *LoadFileText;
+ CHAR16 *FileText;
+
+ LoadFilePath = DevicePathFromHandle (LoadFileHandle);
+ if (LoadFilePath == NULL) {
+ LoadFileText = NULL;
+ } else {
+ LoadFileText = ConvertDevicePathToText (LoadFilePath, FALSE, FALSE);
+ }
+
+ FileText = ConvertDevicePathToText (FilePath, FALSE, FALSE);
+
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: failed to allocate reserved pages: "
+ "BufferSize=%Lu LoadFile=\"%s\" FilePath=\"%s\"\n",
+ gEfiCallerBaseName,
+ __func__,
+ (UINT64)BufferSize,
+ LoadFileText,
+ FileText
+ ));
+
+ if (FileText != NULL) {
+ FreePool (FileText);
+ }
+
+ if (LoadFileText != NULL) {
+ FreePool (LoadFileText);
+ }
+
+ DEBUG_CODE_END ();
+ return NULL;
+ }
+
+ Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
+ if (EFI_ERROR (Status)) {
+ FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
+ return NULL;
+ }
+
+ FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle);
+ if (FullPath == NULL) {
+ //
+ // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.
+ //
+ BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
+ }
+
+ return FullPath;
+}
+
+/**
+ Return the full device path pointing to the load option.
+
+ FilePath may:
+ 1. Exactly matches to a LoadFile instance.
+ 2. Cannot match to any LoadFile instance. Wide match is required.
+ In either case, the routine may return:
+ 1. A copy of FilePath when FilePath matches to a LoadFile instance and
+ the LoadFile returns a load option buffer.
+ 2. A new device path with IP and URI information updated when wide match
+ happens.
+ 3. A new device path pointing to a load option in RAM disk.
+ In either case, only one full device path is returned for a specified
+ FilePath.
+
+ @param FilePath The media device path pointing to a LoadFile instance.
+
+ @return The load option buffer.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFiles (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT VOID **Data,
+ OUT UINT32 *DataSize,
+ IN BOOLEAN ValidateHttp
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+ EFI_EVENT NotifyEvent;
+
+ //
+ // Get file buffer from load file instance.
+ //
+ Node = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
+ //
+ // When wide match happens, pass full device path to LoadFile (),
+ // otherwise, pass remaining device path to LoadFile ().
+ //
+ FilePath = Node;
+ } else {
+ Handle = NULL;
+ //
+ // Use wide match algorithm to find one when
+ // cannot find a LoadFile instance to exactly match the FilePath
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiLoadFileProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ if (EFI_ERROR (Status)) {
+ Handles = NULL;
+ HandleCount = 0;
+ }
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) {
+ Handle = Handles[Index];
+ break;
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+ }
+
+ if (Handle == NULL) {
+ return NULL;
+ }
+
+ 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
new file mode 100644
index 00000000000..d4a054127b4
--- /dev/null
+++ b/Platform/OpenNetworkBoot/BmBootDescription.c
@@ -0,0 +1,168 @@
+/** @file
+ 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 "NetworkBootInternal.h"
+
+/**
+ Return the description for network boot device.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetNetworkDescription (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ MAC_ADDR_DEVICE_PATH *Mac;
+ VLAN_DEVICE_PATH *Vlan;
+ EFI_DEVICE_PATH_PROTOCOL *Ip;
+ EFI_DEVICE_PATH_PROTOCOL *Uri;
+ CHAR16 *Description;
+ UINTN DescriptionSize;
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiLoadFileProtocolGuid,
+ NULL,
+ gImageHandle,
+ Handle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath,
+ gImageHandle,
+ Handle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status) || (DevicePath == NULL)) {
+ return NULL;
+ }
+
+ //
+ // The PXE device path is like:
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)
+ //
+ // The HTTP device path is like:
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...)
+ //
+ while (!IsDevicePathEnd (DevicePath) &&
+ ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||
+ (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP))
+ )
+ {
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+
+ if (IsDevicePathEnd (DevicePath)) {
+ return NULL;
+ }
+
+ Mac = (MAC_ADDR_DEVICE_PATH *)DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+
+ //
+ // Locate the optional Vlan node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_VLAN_DP)
+ )
+ {
+ Vlan = (VLAN_DEVICE_PATH *)DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+ } else {
+ Vlan = NULL;
+ }
+
+ //
+ // Skip the optional Wi-Fi node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_WIFI_DP)
+ )
+ {
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+
+ //
+ // Locate the IP node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||
+ (DevicePathSubType (DevicePath) == MSG_IPv6_DP))
+ )
+ {
+ Ip = DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+ } else {
+ Ip = NULL;
+ }
+
+ //
+ // Skip the optional DNS node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_DNS_DP)
+ )
+ {
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+
+ //
+ // Locate the URI node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_URI_DP)
+ )
+ {
+ Uri = DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+ } else {
+ Uri = NULL;
+ }
+
+ //
+ // Build description like below:
+ // "PXE Boot IPv6 (MAC:11-22-33-44-55-66 VLAN1)"
+ // "HTTP Boot IPv4 (MAC:11-22-33-44-55-66)"
+ //
+ 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"%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],
+ Mac->MacAddress.Addr[1],
+ Mac->MacAddress.Addr[2],
+ Mac->MacAddress.Addr[3],
+ Mac->MacAddress.Addr[4],
+ Mac->MacAddress.Addr[5],
+ (Vlan == NULL) ? 0 : Vlan->VlanId
+ );
+ return Description;
+}
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..587f757e595
--- /dev/null
+++ b/Platform/OpenNetworkBoot/README.md
@@ -0,0 +1,464 @@
+# 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
+For PXE Boot support it would also be necessary to add (not provided with OpenCore):
+```
+Mtftp4Dxe (IPv4)
+Mtftp6Dxe (IPv6)
+```
diff --git a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c
new file mode 100644
index 00000000000..ed2aa313e23
--- /dev/null
+++ b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c
@@ -0,0 +1,519 @@
+/** @file
+ Miscellaneous routines for TLS auth config.
+
+ Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ 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)
+
+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;
+
+/**
+ Perform action for all signatures in specified database, with
+ possibility of aborting early.
+
+ @param[in] VariableName The variable name of the vendor's signature database.
+ @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
+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;
+ UINTN DataSize;
+ UINT8 *Data;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINT32 ItemDataSize;
+
+ ASSERT (ProcessCert != NULL);
+
+ Data = NULL;
+ CertList = NULL;
+ Cert = NULL;
+
+ //
+ // Read Variable.
+ //
+ DataSize = 0;
+ Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data);
+ if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) {
+ if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ Data = (UINT8 *)AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data);
+ if (EFI_ERROR (Status)) {
+ FreePool (Data);
+ return Status;
+ }
+
+ //
+ // Enumerate all data.
+ //
+ ItemDataSize = (UINT32)DataSize;
+ CertList = (EFI_SIGNATURE_LIST *)Data;
+ GuidIndex = 0;
+
+ while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+ if (!CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ //
+ // The signature type is not supported in current implementation.
+ //
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
+ 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);
+
+ 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);
+ }
+
+ FreePool (Data);
+
+ return Status;
+}
+
+/**
+ @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 ( (CertSize == sizeof (EFI_SIGNATURE_DATA) - 1 + Context->X509DataSize)
+ && (CompareMem (Cert->SignatureData, Context->X509Data, Context->X509DataSize) == 0)
+ )
+ {
+ return EFI_ALREADY_STARTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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] 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_OUT_OF_RESOURCES Could not allocate needed resources.
+**/
+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 Status;
+ UINTN DataSize;
+ UINT8 *Data;
+ UINT8 *OldData;
+ UINT32 Attr;
+ UINT32 Index;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_LIST *NewCertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN CertCount;
+ UINT32 Offset;
+ UINTN LocalDeleteCount;
+ UINT32 ItemDataSize;
+
+ ASSERT ((X509Data == NULL) || (X509DataSize != 0));
+
+ if (DeletedCount == NULL) {
+ DeletedCount = &LocalDeleteCount;
+ }
+
+ *DeletedCount = 0;
+
+ Data = NULL;
+ OldData = NULL;
+ CertList = NULL;
+ Cert = NULL;
+ Attr = 0;
+
+ //
+ // Get original signature list data.
+ //
+ DataSize = 0;
+ Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL);
+ if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) {
+ if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+ }
+
+ OldData = AllocateZeroPool (DataSize);
+ if (OldData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gRT->GetVariable (VariableName, VendorGuid, &Attr, &DataSize, OldData);
+ if (EFI_ERROR (Status)) {
+ FreePool (OldData);
+ return Status;
+ }
+
+ //
+ // Allocate space for new variable.
+ //
+ Data = AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ FreePool (OldData);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Enumerate all data, erasing target items.
+ //
+ ItemDataSize = (UINT32)DataSize;
+ CertList = (EFI_SIGNATURE_LIST *)OldData;
+ Offset = 0;
+ while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ //
+ // Copy EFI_SIGNATURE_LIST header then calculate the signature count in this list.
+ //
+ CopyMem (Data + Offset, CertList, (sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize));
+ NewCertList = (EFI_SIGNATURE_LIST *)(Data + Offset);
+ Offset += (sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ 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 ( 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;
+ ++(*DeletedCount);
+ } else {
+ //
+ // This item doesn't match. Copy it to the Data buffer.
+ //
+ CopyMem (Data + Offset, (UINT8 *)(Cert), CertList->SignatureSize);
+ Offset += CertList->SignatureSize;
+ }
+
+ Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize);
+ }
+ } else {
+ //
+ // This List doesn't match. Just copy it to the Data buffer.
+ //
+ CopyMem (Data + Offset, (UINT8 *)(CertList), CertList->SignatureListSize);
+ Offset += CertList->SignatureListSize;
+ }
+
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
+ }
+
+ if (*DeletedCount != 0) {
+ //
+ // Delete the EFI_SIGNATURE_LIST header if there is no signature remaining in any 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;
+ if (CertCount != 0) {
+ CopyMem (OldData + Offset, (UINT8 *)(CertList), CertList->SignatureListSize);
+ Offset += CertList->SignatureListSize;
+ }
+
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
+ }
+
+ DataSize = Offset;
+
+ //
+ // Set (or delete if everything was removed) the Variable.
+ //
+ Status = gRT->SetVariable (
+ VariableName,
+ VendorGuid,
+ Attr,
+ DataSize,
+ OldData
+ );
+ }
+
+ FreePool (Data);
+ FreePool (OldData);
+
+ return Status;
+}
+
+/**
+ Enroll a new X509 certificate into Variable.
+
+ @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 CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN EFI_GUID *OwnerGuid,
+ IN UINTN X509DataSize,
+ IN VOID *X509Data
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIGNATURE_LIST *CACert;
+ EFI_SIGNATURE_DATA *CACertData;
+ VOID *Data;
+ UINTN DataSize;
+ UINTN SigDataSize;
+ UINT32 Attr;
+
+ 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) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill Certificate Database parameters.
+ //
+ CACert = (EFI_SIGNATURE_LIST *)Data;
+ CACert->SignatureListSize = (UINT32)SigDataSize;
+ CACert->SignatureHeaderSize = 0;
+ CACert->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize);
+ CopyGuid (&CACert->SignatureType, &gEfiCertX509Guid);
+
+ CACertData = (EFI_SIGNATURE_DATA *)((UINT8 *)CACert + sizeof (EFI_SIGNATURE_LIST));
+ CopyGuid (&CACertData->SignatureOwner, OwnerGuid);
+ CopyMem ((UINT8 *)(CACertData->SignatureData), X509Data, X509DataSize);
+
+ //
+ // Check if the signature database entry already exists. If it does, use the
+ // EFI_VARIABLE_APPEND_WRITE attribute to append the new signature data to
+ // the original variable, plus preserve the original variable attributes.
+ //
+ Status = gRT->GetVariable (
+ VariableName,
+ VendorGuid,
+ &Attr,
+ &DataSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Attr |= EFI_VARIABLE_APPEND_WRITE;
+ } else if (Status == EFI_NOT_FOUND) {
+ Attr = TLS_AUTH_CONFIG_VAR_BASE_ATTR;
+ } else {
+ FreePool (Data);
+ return Status;
+ }
+
+ Status = gRT->SetVariable (
+ VariableName,
+ VendorGuid,
+ Attr,
+ SigDataSize,
+ Data
+ );
+
+ 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..ab20e1d15e0 100755
--- a/build_oc.tool
+++ b/build_oc.tool
@@ -186,21 +186,25 @@ 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"
"NvmExpressDxe.efi"
"OpenCanopy.efi"
"OpenHfsPlus.efi"
"OpenLegacyBoot.efi"
"OpenLinuxBoot.efi"
+ "OpenNetworkBoot.efi"
"OpenNtfsDxe.efi"
"OpenPartitionDxe.efi"
"OpenRuntime.efi"
@@ -208,11 +212,15 @@ 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"
"UsbMouseDxe.efi"
"XhciDxe.efi"
)