Skip to content

Commit

Permalink
OpenNetworkBoot: Support loading .dmg and .chunklist without requirin…
Browse files Browse the repository at this point in the history
…g fake MIME type
  • Loading branch information
mikebeaton committed Aug 11, 2024
1 parent 5901b60 commit 3f66bb7
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 63 deletions.
2 changes: 2 additions & 0 deletions OpenCorePkg.dsc
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@

!include NetworkPkg/NetworkLibs.dsc.inc

HttpLib|NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf

!include Ext4Pkg/Ext4Defines.dsc.inc
!include Ext4Pkg/Ext4Libs.dsc.inc

Expand Down
142 changes: 106 additions & 36 deletions Platform/OpenNetworkBoot/HttpBootCallback.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@

#include "NetworkBootInternal.h"

#include <IndustryStandard/Http11.h>
#include <Library/HttpLib.h>
#include <Protocol/Http.h>

#define HTTP_CONTENT_TYPE_APP_EFI "application/efi"

OC_DMG_LOADING_SUPPORT gDmgLoading;

STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback;
Expand All @@ -22,13 +26,13 @@ STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback;
EFI_STATUS
ValidateDmgAndHttps (
CHAR16 *Uri,
BOOLEAN ShowLog
BOOLEAN ShowLog,
BOOLEAN *HasDmgExtension
)
{
CHAR8 *Match;
CHAR8 *Uri8;
UINTN UriSize;
BOOLEAN HasDmgExtension;
CHAR8 *Match;
CHAR8 *Uri8;
UINTN UriSize;

if (gRequireHttpsUri && !HasHttpsUri (Uri)) {
//
Expand All @@ -41,24 +45,25 @@ ValidateDmgAndHttps (
return EFI_UNSUPPORTED;
}

if (gDmgLoading == OcDmgLoadingDisabled) {
UriSize = StrSize (Uri);
Uri8 = AllocatePool (UriSize);
if (Uri8 == NULL) {
return EFI_OUT_OF_RESOURCES;
}
UriSize = StrSize (Uri);
Uri8 = AllocatePool (UriSize);
if (Uri8 == NULL) {
return EFI_OUT_OF_RESOURCES;
}

UnicodeStrToAsciiStrS (Uri, Uri8, UriSize);
UnicodeStrToAsciiStrS (Uri, Uri8, UriSize);

Match = ".dmg";
HasDmgExtension = UriFileHasExtension (Uri8, Match);
if (!HasDmgExtension) {
Match = ".chunklist";
HasDmgExtension = UriFileHasExtension (Uri8, Match);
}
Match = ".dmg";
*HasDmgExtension = UriFileHasExtension (Uri8, Match);
if (!*HasDmgExtension) {
Match = ".chunklist";
*HasDmgExtension = UriFileHasExtension (Uri8, Match);
}

FreePool (Uri8);
if (HasDmgExtension) {
FreePool (Uri8);

if (gDmgLoading == OcDmgLoadingDisabled) {
if (*HasDmgExtension) {
if (ShowLog) {
DEBUG ((DEBUG_INFO, "NTBT: %a file is requested while DMG loading is disabled\n", Match));
}
Expand All @@ -83,24 +88,89 @@ OpenNetworkBootHttpBootCallback (
{
EFI_STATUS Status;
EFI_HTTP_MESSAGE *HttpMessage;
EFI_HTTP_HEADER *Header;
STATIC BOOLEAN HasDmgExtension = FALSE;

if ((DataType == HttpBootHttpRequest) && (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
);
if (EFI_ERROR (Status)) {
return Status;
switch (DataType) {
//
// 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;
}
}
}
}

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,
// this will 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 (
Expand Down
1 change: 1 addition & 0 deletions Platform/OpenNetworkBoot/OpenNetworkBoot.inf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
[LibraryClasses]
DebugLib
DevicePathLib
HttpLib
OcBootManagementLib
OcFlexArrayLib
OcVirtualFsLib
Expand Down
45 changes: 18 additions & 27 deletions Platform/OpenNetworkBoot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ 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
Expand All @@ -54,14 +53,22 @@ 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` driver may be locked
down to `https://` URIs only (even in some cases where the machine has no BIOS UI
for HTTP Boot; e.g. Dell OptiPlex 3070). This means that while `HttpBootDxe` 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
> **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.

## Identifying missing network boot drivers

The `dh` command in UEFI Shell (e.g. `OpenShell` provided with
Expand Down Expand Up @@ -249,9 +256,12 @@ 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 standard HTTP boot drivers (including
the `HttpBootDxe` driver currently shipped with OpenCore).
file extensions will _not_ be loaded by HTTP boot drivers.

## Booting DMG files

Expand All @@ -261,25 +271,6 @@ 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.

### MIME types

In order for `.dmg` and `.chunklist` files to be loaded by standard HTTP boot
drivers (including the `HttpBootDxe` driver currently shipped with OpenCore),
they must be served with the MIME type `application/efi`, otherwise they will
be treated as having an unknown file type and will not be loaded.

> **Note**: `.dmg` files should not use the MIME types for ISO or IMG files mentioned
above, or else the HTTP boot drivers will try to load, mount and boot from
them as if they were the corresponding type of ramdisk, which will fail.

For example, to set up the required MIME types when using Apache installed via
Homebrew on macOS, the following line should be added to
`/opt/homebrew/etc/httpd/mime.types`:

```
application/efi efi dmg chunklist
```

### `DmgLoading` configuration setting

The behaviour of `OpenNetworkBoot`'s DMG support depends on the OpenCore
Expand Down

0 comments on commit 3f66bb7

Please sign in to comment.