diff --git a/.pytool/CISettings.py b/.pytool/CISettings.py index 269998ecd..15313a9c2 100644 --- a/.pytool/CISettings.py +++ b/.pytool/CISettings.py @@ -168,7 +168,7 @@ def GetDependencies(self): { "Path": "MU_BASECORE", "Url": "https://github.com/microsoft/mu_basecore.git", - "Commit": "922377cb580e03c45628ddb4e0a0871ccf2f6f2d" + "Commit": "5315549216a14e8b0f433af7012a99d6476afc31" }, { "Path": "Features/MM_SUPV", diff --git a/OneCryptoPkg/Docs/Architecture.md b/OneCryptoPkg/Docs/Architecture.md index 1b9109cfe..7cb65b380 100644 --- a/OneCryptoPkg/Docs/Architecture.md +++ b/OneCryptoPkg/Docs/Architecture.md @@ -5,9 +5,16 @@ OneCryptoPkg uses a **Bin + Loader** pattern to provide crypto services. A OpenSSL) and a **Loader** module discovers the Bin, injects runtime dependencies, and installs the public `gOneCryptoProtocolGuid` for consumers. -Today only X64 supports the phase agnostic crypto implementation. -See [Why the Difference?](#why-the-difference) for details on AARCH64. -However this is something we want to support in the long term for AARCH64. +X64 supports a single stored MM binary consumed by DXE. + +AARCH64 supports two mechanisms: + +- single-copy (preferred mechanism) + - MM-provider based shared copy +- dual-copy + - each phase carries its own binary + +See [Why the Difference?](#why-the-difference) for details. ## X64 @@ -52,45 +59,153 @@ Both StandaloneMm and SupvMm follow the same two-driver pattern: ## AARCH64 -AARCH64 produces **4 drivers**: 2 from `OneCryptoBin` and 2 from -`OneCryptoLoaders`. +AARCH64 supports two modes: -| Environment | Bin | Loader | -|-----------------|----------------------------|-----------------------------------| -| DXE | `OneCryptoBinDxe` | `OneCryptoLoaderDxeByProtocol` | -| StandaloneMm | `OneCryptoBinStandaloneMm` | `OneCryptoLoaderStandaloneMm` | +- **Single-copy** (preferred): the secure-world MM Bin is the only stored + OneCrypto image; DXE fetches its bytes from MM and `LoadImage()`s them. + - DXE: `OneCryptoLoaderDxeFromMm` + - StandaloneMm: `OneCryptoImageProviderStandaloneMm` + + `OneCryptoBinStandaloneMm` + `OneCryptoLoaderStandaloneMm` +- **Dual-copy**: dedicated normal-world and secure-world Bin and loader. + - DXE: `OneCryptoBinDxe` + `OneCryptoLoaderDxeByProtocol` + - StandaloneMm: `OneCryptoBinStandaloneMm` + `OneCryptoLoaderStandaloneMm` ### DXE Flow (AARCH64) -On AARCH64 the DXE Loader **cannot** reach into the secure-world firmware -volume to load the StandaloneMm binary. Instead, a dedicated `OneCryptoBinDxe` -(`DXE_DRIVER`) is included in the normal-world FV: +#### Single-copy mode + +With no DXE Bin, `OneCryptoLoaderDxeFromMm` pulls the image from the MM provider +over `EFI_MM_COMMUNICATION2_PROTOCOL`: + +1. Loader queries total image size and **serve format** via + `gOneCryptoImageProviderGuid`. +2. Loader fetches the bytes in chunks sized to the MM communication buffer. +3. Loader consumes the bytes according to the format (below), resolves + `CryptoEntry`, injects dependencies, and installs `gOneCryptoProtocolGuid`. + +##### Two serve formats (boot paths) + +The provider is format-agnostic and keys on one invariant: OneCryptoBin's +`FILE_GUID` (`ONE_CRYPTO_BINARY_GUID`). How the platform *packages* that file +selects which path runs — no provider change is needed. + +| Format | Provider serves | DXE work | When to use | +| --------------------------------- | ----------------------------- | ----------------------------------------------- | --------------------------------------- | +| `ONE_CRYPTO_IMAGE_FORMAT_PE32` | pristine PE32 bytes | `LoadImage()` directly | discoverable, uncompressed FV | +| `ONE_CRYPTO_IMAGE_FORMAT_GUIDED_FV` | raw compressed `GUID_DEFINED` bytes | decode, walk FV, extract by GUID, `LoadImage()` | flash constrained; OneCrypto compressed | + +The compressed path exists because secure-world MMRAM may not have room to +decompress the image, so the provider passes the compressed binary to DXE, +which has ample heap to expand it. + +#### Dual-copy mode -1. `OneCryptoBinDxe` is dispatched normally by the UEFI DXE dispatcher. Its - entry point installs `gOneCryptoPrivateProtocolGuid` with the crypto - constructor. +This mode ships a dedicated `OneCryptoBinDxe` (`DXE_DRIVER`) in the normal-world +FV: + +1. `OneCryptoBinDxe` is dispatched by DXE and installs + `gOneCryptoPrivateProtocolGuid`. 2. `OneCryptoLoaderDxeByProtocol` has a `[Depex]` on - `gOneCryptoPrivateProtocolGuid`. It calls `LocateProtocol()` to find the - private protocol, invokes the constructor, and installs the public - `gOneCryptoProtocolGuid`. + `gOneCryptoPrivateProtocolGuid`, calls `LocateProtocol()`, invokes the + constructor, and installs `gOneCryptoProtocolGuid`. -This protocol-based approach avoids PE/COFF export parsing entirely. +This protocol-based approach avoids PE/COFF export parsing. ### MM Flow (AARCH64) -The MM two-driver pattern (Bin + Loader) is the same as X64 — -`OneCryptoBinStandaloneMm` and `OneCryptoLoaderStandaloneMm` are the same -source modules on both architectures. See -[Why the Difference?](#why-the-difference) for why AARCH64 needs a separate -`OneCryptoBinDxe` instead of reusing the MM binary during DXE. +`OneCryptoBinStandaloneMm` + `OneCryptoLoaderStandaloneMm` follow the same MM +Bin+Loader pattern as X64. + +In single-copy mode, `OneCryptoImageProviderStandaloneMm` is a separate MM module +that owns the DXE transport path described above. Splitting it from +`OneCryptoLoaderStandaloneMm` keeps MM-communication failures isolated from the +protocol-construction logic. + +## Packaging the Single-Copy Image (FDF / DSC) + +Packaging trades flash size against a DXE-side decode, and discovery is +**strict**: the provider scans the FVs published to MM as FV/FV2/FV3 HOBs +(FV3 first) and matches a well-known `FILE_GUID`. It takes either OneCryptoBin's +PE32 directly (`ONE_CRYPTO_BINARY_GUID`, Mode A) or a container file +(`ONE_CRYPTO_CONTAINER_FV_GUID`) whose `GUID_DEFINED` section wraps the nested FV +(Mode B). It does **not** scan generic compressed nested FVs. Both GUIDs are +defined in `OneCryptoPkg/Include/Guid/OneCryptoFileGuid.h`. + +### Two packaging modes + +| Mode | FDF packaging | Flash | DXE decode | +| ------------------------------- | ------------------------------------------------- | ------- | ---------- | +| **A - Direct PE32** | top-level file in a discoverable FV, uncompressed | largest | none | +| **B - Dedicated compressed FV** | its own identity-tagged, LZMA-wrapped FV | small | in DXE | + +Mode A and B map to the `PE32` and `GUIDED_FV` +[serve formats](#two-serve-formats-boot-paths) the provider reports at runtime. + +Mode A is simplest and most robust; Mode B trades a DXE-side LZMA decode for a +smaller flash footprint on space-constrained parts. + +### FDF (Mode B example) + +Give OneCryptoBin its own compressed FV anchored by a well-known container GUID, +and keep the DXE loader + MM provider/loader in their normal FVs: + +```text +[FV.OneCryptoFv] # dedicated, compressed + INF .../OneCryptoBin/OneCryptoBinStandaloneMm.inf + +[FV.] # secure-world MM FV + INF .../OneCryptoLoaders/OneCryptoLoaderStandaloneMm.inf + INF .../OneCryptoLoaders/OneCryptoImageProviderStandaloneMm.inf + # anchor the dedicated FV so the provider finds it by identity: + FILE FV_IMAGE = { + SECTION GUIDED PROCESSING_REQUIRED = TRUE { + SECTION FV_IMAGE = OneCryptoFv + } + } + +[FV.] # normal-world DXE FV + INF .../OneCryptoLoaders/OneCryptoLoaderDxeFromMm.inf +``` + +### DSC + +Disable shared-crypto for the DXE/MM phases and route `BaseCryptLib` through the +OneCrypto protocol, so the variable/TPM/secure-boot MM consumers depend on +`gOneCryptoProtocolGuid` (published by OneCryptoBin in MM) rather than a +shared-crypto SMM protocol that no longer exists: + +```text +[LibraryClasses.common.DXE_DRIVER] + BaseCryptLib|.../BaseCryptLibOnOneCrypto/DxeCryptLib.inf + +[LibraryClasses.common.MM_STANDALONE] + BaseCryptLib|.../BaseCryptLibOnOneCrypto/StandaloneMmCryptLib.inf +``` + +Every INF referenced by the FDF must also appear in `[Components]`; list the DXE +loader, MM loader, MM provider, and MM Bin there. + +### Heap budget expectations + +The bytes that matter are **StandaloneMmCore heap** (secure-world MMRAM), not +the flash FV size. Rough per-component costs during MM bring-up: + +| Component | Secure-world heap cost | Notes | +| -------------------------------- | --------------------------------------------- | ---------------------- | +| `OneCryptoBinStandaloneMm` | ~1.5 MB, ~doubled while resident (FV + image) | full crypto + TLS | +| MM communication bounce buffer | up to the comm-buffer size (e.g. ~1 MB) | transient, per request | +| MM loader / provider | small (tens of KB) | -- | + +Treat these as per-component estimates to sum against the platform's own MMRAM +carve-out, not a fixed budget. The **~2 MB** Mode B LZMA decode is not included; +it runs in DXE. ## Why the Difference? -On AARCH64, StandaloneMm runs inside TrustZone and the secure-world firmware -volume is not accessible from normal-world DXE. On X64, `GetSectionFromAnyFv()` -can reach the MM firmware volume, so the DXE Loader reuses the MM binary -directly. On AARCH64, a separate `OneCryptoBinDxe` must be included in the -normal-world FV. +On X64, `GetSectionFromAnyFv()` can reach the MM firmware volume, so the DXE +Loader reuses the MM binary directly. On AARCH64, StandaloneMm runs inside +TrustZone and its firmware volume is not accessible from normal-world DXE, which +is why AARCH64 needs the two modes above rather than X64's direct reuse. ## Module Summary @@ -104,6 +219,11 @@ normal-world FV. | `OneCryptoLoaderDxe` | `DXE_DRIVER` | ✓ | | | `OneCryptoLoaderDxeByProtocol` | `DXE_DRIVER` | | ✓ | +Single-copy mode modules on AARCH64: + +- `OneCryptoLoaderDxeFromMm` (`DXE_DRIVER`) +- `OneCryptoImageProviderStandaloneMm` (`MM_STANDALONE`) + ## Dependency Injection The crypto Bin binary statically links BaseCryptLib, TlsLib, and OpenSSL, but @@ -119,10 +239,6 @@ populates this structure with the platform's real implementations and calls `OneCryptoCrtSetup()` before invoking `CryptoEntry`. ```mermaid ---- -config: - layout: elk ---- classDiagram direction TB diff --git a/OneCryptoPkg/Docs/FAQs.md b/OneCryptoPkg/Docs/FAQs.md index 8fb74be1d..a7385bd34 100644 --- a/OneCryptoPkg/Docs/FAQs.md +++ b/OneCryptoPkg/Docs/FAQs.md @@ -8,24 +8,21 @@ updating the DSC. ## Where can I see OneCryptoPkg integrated into a platform? -QemuQ35Pkg and QemuSbsaPkg on [mu_tiano_platforms](https://github.com/microsoft/mu_tiano_platforms) -uses the OneCrypto binary drivers. See pull request -[Platforms: Wire up OneCrypto binary drivers -](https://github.com/microsoft/mu_tiano_platforms/pull/1278). +QemuQ35Pkg and QemuArmVirtPkg on +[mu_tiano_platforms](https://github.com/microsoft/mu_tiano_platforms) use the +OneCrypto binary drivers. See pull request +[Platforms: Wire up OneCrypto binary drivers](https://github.com/microsoft/mu_tiano_platforms/pull/1278). ## OneCryptoBinSupvMm is MODULE_TYPE MM_STANDALONE — how does it run in DXE? On X64, the DXE Loader (`OneCryptoLoaderDxe`) calls `LoadImage()` on the -`MM_STANDALONE` binary to get the correct memory protections and mappings -applied, then parses the PE/COFF exports to find and invoke the `CryptoEntry` -function. +`MM_STANDALONE` binary so the UEFI loader applies the correct memory protections +and mappings, then parses the PE/COFF exports to find and invoke `CryptoEntry`. -On AARCH64 the approach is different — a dedicated `OneCryptoBinDxe` -(`DXE_DRIVER`) is used instead because the secure-world FV is not accessible -from DXE. - -See [Architecture.md](Architecture.md) for the full Bin + Loader pattern and -the differences between X64 and AARCH64. +AARCH64 cannot read the secure-world FV from DXE, so it either ships a dedicated +`DXE_DRIVER` Bin (dual-copy) or fetches the image from MM over +`EFI_MM_COMMUNICATION2_PROTOCOL` (single-copy). See +[Architecture.md](Architecture.md) for the full breakdown. ## Why are there both SetupEntry and NoSetupEntry in the Crypto Bin? @@ -42,21 +39,23 @@ not match the executing environment. The Loader must call `SetupEntry` instead, which manually initializes the library constructors before providing the crypto protocol. -## Why are there two DXE Loaders (OneCryptoLoaderDxe vs OneCryptoLoaderDxeByProtocol)? +## Why are there multiple DXE Loaders (OneCryptoLoaderDxe, OneCryptoLoaderDxeByProtocol, OneCryptoLoaderDxeFromMm)? They serve different architectures with fundamentally different loading strategies: -- **`OneCryptoLoaderDxe`** (X64) — Locates the `MM_STANDALONE` Bin image in the - firmware volume via `GetSectionFromAnyFv()`, calls `LoadImage()` to load it - into DXE memory, and parses the PE/COFF export directory to resolve - `CryptoEntry`. Because this is a cross-phase load, it uses `SetupEntry` to - manually run library constructors. - -- **`OneCryptoLoaderDxeByProtocol`** (AARCH64) — The Bin is a native - `DXE_DRIVER` (`OneCryptoBinDxe`) already dispatched by the DXE dispatcher. - The Loader simply calls `LocateProtocol()` on `gOneCryptoPrivateProtocolGuid` - to find it. No `LoadImage()` or PE/COFF parsing is needed, and it uses - `NoSetupEntry` since constructors already ran during normal dispatch. +- **`OneCryptoLoaderDxe`** (X64): locates the `MM_STANDALONE` Bin via + `GetSectionFromAnyFv()`, `LoadImage()`s it, and resolves `CryptoEntry` from the + PE/COFF exports. Cross-phase, so it uses `SetupEntry` to run constructors. + +- **`OneCryptoLoaderDxeByProtocol`** (AARCH64): the Bin is a native `DXE_DRIVER` + (`OneCryptoBinDxe`) already dispatched by DXE, so the loader just calls + `LocateProtocol()` on `gOneCryptoPrivateProtocolGuid`. No `LoadImage()` or + PE/COFF parsing, and `NoSetupEntry` since constructors already ran. + +- **`OneCryptoLoaderDxeFromMm`** (AARCH64 single-copy): DXE cannot read the + secure-world FV, so it requests the image bytes from + `OneCryptoImageProviderStandaloneMm` over `EFI_MM_COMMUNICATION2_PROTOCOL`, then + `LoadImage()`s the result and resolves `CryptoEntry`. See [Architecture.md](Architecture.md) for the full X64 vs AARCH64 breakdown. diff --git a/OneCryptoPkg/DriverBuild.py b/OneCryptoPkg/DriverBuild.py index 90a60102a..b91faa24a 100644 --- a/OneCryptoPkg/DriverBuild.py +++ b/OneCryptoPkg/DriverBuild.py @@ -66,7 +66,7 @@ def GetDependencies(): { "Path": "MU_BASECORE", "Url": "https://github.com/microsoft/mu_basecore.git", - "Commit": "922377cb580e03c45628ddb4e0a0871ccf2f6f2d" + "Commit": "5315549216a14e8b0f433af7012a99d6476afc31" }, { "Path": "Features/MM_SUPV", diff --git a/OneCryptoPkg/Include/Guid/OneCryptoFileGuid.h b/OneCryptoPkg/Include/Guid/OneCryptoFileGuid.h index 53604a618..3f0e47b83 100644 --- a/OneCryptoPkg/Include/Guid/OneCryptoFileGuid.h +++ b/OneCryptoPkg/Include/Guid/OneCryptoFileGuid.h @@ -2,7 +2,7 @@ This file contains the GUID for the phase agnostic shared one crypto binary - Copyright (C) Microsoft Corporation + Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent @@ -19,4 +19,17 @@ 0x76ABA88D, 0x9D16, 0x49A2, { 0xAA, 0x3A, 0xDB, 0x61, 0x12, 0xFA, 0xC5, 0xCC } \ } +// +// FILE_GUID of the dedicated, LZMA-compressed OneCrypto container FV that a +// platform may place in the boot firmware volume alongside the StandaloneMM +// payload FV. The image provider matches this to locate the compressed +// OneCrypto payload deterministically and serve it to DXE. +// +// No fallback to an arbitrary compressed FV is supported. +// +#define ONE_CRYPTO_CONTAINER_FV_GUID \ + { \ + 0x1DC82EA3, 0xD1E9, 0x4C10, { 0xA7, 0x88, 0x33, 0x5D, 0x16, 0x6E, 0x23, 0xA1 } \ + } + #endif // ONE_CRYPTO_FILE_GUID_H_ diff --git a/OneCryptoPkg/Include/Guid/OneCryptoImageProviderGuid.h b/OneCryptoPkg/Include/Guid/OneCryptoImageProviderGuid.h new file mode 100644 index 000000000..c663e4d29 --- /dev/null +++ b/OneCryptoPkg/Include/Guid/OneCryptoImageProviderGuid.h @@ -0,0 +1,20 @@ +/** @file + + GUID definition for the OneCrypto MM image provider communication handler. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ONE_CRYPTO_IMAGE_PROVIDER_GUID_H_ +#define ONE_CRYPTO_IMAGE_PROVIDER_GUID_H_ + +#define ONE_CRYPTO_IMAGE_PROVIDER_GUID \ + { \ + 0x5B9B9A72, 0xA4DF, 0x4D57, { 0xA1, 0xD3, 0x3A, 0x88, 0xEF, 0xB2, 0x5B, 0x9E } \ + } + +extern EFI_GUID gOneCryptoImageProviderGuid; + +#endif // ONE_CRYPTO_IMAGE_PROVIDER_GUID_H_ diff --git a/OneCryptoPkg/Include/Private/OneCryptoImageProviderMessage.h b/OneCryptoPkg/Include/Private/OneCryptoImageProviderMessage.h new file mode 100644 index 000000000..44c8d76f0 --- /dev/null +++ b/OneCryptoPkg/Include/Private/OneCryptoImageProviderMessage.h @@ -0,0 +1,51 @@ +/** @file + + Shared message format for OneCrypto image-provider MM communication. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ONE_CRYPTO_IMAGE_PROVIDER_MESSAGE_H_ +#define ONE_CRYPTO_IMAGE_PROVIDER_MESSAGE_H_ + +#define ONE_CRYPTO_IMAGE_PROVIDER_VERSION 1U +#define ONE_CRYPTO_IMAGE_PROVIDER_SIGNATURE SIGNATURE_32 ('O', 'C', 'I', 'P') + +// +// Payload format served by the provider (ONE_CRYPTO_IMAGE_PROVIDER_MSG.Format). +// +// PE32 - Data is the raw OneCrypto PE32 image; DXE LoadImage()s it directly. +// GUIDED_FV - Data is the whole GUID_DEFINED section (header + compressed +// payload) that wraps the nested FV containing the OneCrypto PE32. +// Secure world (MM) may lack the memory budget to decompress the FV, +// so it hands the small compressed section to the resource-rich +// normal world (DXE), which decodes it via ExtractGuidedSectionLib +// (LZMA is the only handler registered there today) and extracts +// the OneCrypto PE32. +// +#define ONE_CRYPTO_IMAGE_FORMAT_PE32 0U +#define ONE_CRYPTO_IMAGE_FORMAT_GUIDED_FV 1U + +// +// Chunked request/response for MM communication. One DXE->MM->DXE round trip +// either queries the image size (RequestedSize == 0) or fetches one chunk. +// Fields are IN (DXE writes, MM reads) or OUT (MM writes, DXE reads). +// +typedef struct { + UINT32 Signature; ///< IN: ONE_CRYPTO_IMAGE_PROVIDER_SIGNATURE; identifies the message format. + UINT32 Version; ///< IN: ONE_CRYPTO_IMAGE_PROVIDER_VERSION; rejected by MM if mismatched. + UINT64 Offset; ///< IN: Byte offset into the image at which this chunk begins. + UINT32 RequestedSize; ///< IN: Payload bytes wanted at Offset; 0 requests a size query only. + UINT32 ReturnedSize; ///< OUT: Valid bytes written to Data (the length of Data); <= RequestedSize. + UINT32 TotalImageSize; ///< OUT: Total image size, constant across chunks; not the size of Data. + UINT32 Format; ///< OUT: Encoding of Data (ONE_CRYPTO_IMAGE_FORMAT_*). + UINT32 Crc32; ///< OUT: CRC32 over the image for transport integrity (not authenticity); verified by DXE after reassembly. + UINT32 Reserved; ///< Reserved; keeps the header 8-byte aligned (must be 0). + UINT64 Status; ///< OUT: EFI_STATUS of the request (0 = success); DXE reads this, not the MMI return code. + EFI_GUID ImageGuid; ///< OUT: GUID identifying the OneCrypto binary being served. + UINT8 Data[]; ///< OUT: Payload bytes for this chunk; exactly ReturnedSize bytes are valid. +} ONE_CRYPTO_IMAGE_PROVIDER_MSG; + +#endif // ONE_CRYPTO_IMAGE_PROVIDER_MESSAGE_H_ diff --git a/OneCryptoPkg/OneCryptoLoaders/Integration/OneCryptoImageProviderStandaloneMm.inf b/OneCryptoPkg/OneCryptoLoaders/Integration/OneCryptoImageProviderStandaloneMm.inf new file mode 100644 index 000000000..da7a81014 --- /dev/null +++ b/OneCryptoPkg/OneCryptoLoaders/Integration/OneCryptoImageProviderStandaloneMm.inf @@ -0,0 +1,24 @@ +## @file +# OneCrypto image provider in StandaloneMM for QAV fetch path. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OneCryptoImageProviderStandaloneMm + FILE_GUID = 60A67696-D50D-488E-B436-82D0D3A11E8F + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + ENTRY_POINT = MmEntry + +[Packages] + MdePkg/MdePkg.dec + CryptoPkg/CryptoPkg.dec + +[Binaries] + PE32|OneCryptoImageProviderStandaloneMm.efi + SMM_DEPEX|OneCryptoImageProviderStandaloneMm.depex diff --git a/OneCryptoPkg/OneCryptoLoaders/OneCryptoImageProviderMm.c b/OneCryptoPkg/OneCryptoLoaders/OneCryptoImageProviderMm.c new file mode 100644 index 000000000..e1909e43c --- /dev/null +++ b/OneCryptoPkg/OneCryptoLoaders/OneCryptoImageProviderMm.c @@ -0,0 +1,517 @@ +/** @file + OneCryptoImageProviderMm.c + + Locates OneCrypto in the StandaloneMM firmware volumes and serves its bytes to + the DXE loader over MM communication. + + Supported discovery paths are: + 1) direct PE32 from ONE_CRYPTO_BINARY_GUID, or + 2) compressed payload from ONE_CRYPTO_CONTAINER_FV_GUID. + + If neither exists, this provider returns an EFI error; platform policy may + assert/fail-fast rather than continue without crypto. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +STATIC EFI_GUID mOneCryptoBinaryGuid = ONE_CRYPTO_BINARY_GUID; +STATIC EFI_GUID mOneCryptoContainerFvGuid = ONE_CRYPTO_CONTAINER_FV_GUID; +EFI_GUID gOneCryptoImageProviderGuid = ONE_CRYPTO_IMAGE_PROVIDER_GUID; + +// +// Located OneCrypto payload: a pointer into resident MM firmware (either the raw +// PE32 image, or the compressed container FV section) plus the format that +// tells DXE how to consume it. Cached after the first successful lookup. +// +STATIC VOID *mOneCryptoImageData = NULL; +STATIC UINTN mOneCryptoImageSize = 0; +STATIC UINT32 mOneCryptoImageFormat = ONE_CRYPTO_IMAGE_FORMAT_PE32; + +// +// Dispatch handle for the registered image-provider MMI handler and the +// ReadyToLock protocol-notify registration used to tear it down after the +// OneCrypto image has been fetched and launched during DXE. +// +STATIC EFI_HANDLE mDispatchHandle = NULL; +STATIC VOID *mReadyToLockRegistration = NULL; + +// +// DEBUG-only CRC32 over the located image, computed once and cached. Serves as a +// transport-integrity aid for the DXE receiver (see ONE_CRYPTO_IMAGE_PROVIDER_MSG). +// Left 0 in RELEASE builds, where the receiver performs no check. +// +STATIC UINT32 mOneCryptoImageCrc32 = 0; + +/** + Scan one firmware volume for OneCrypto. + + Two types are recognised: + - the OneCrypto file carrying a direct PE32 section (uncompressed platforms), + served as ONE_CRYPTO_IMAGE_FORMAT_PE32; and + - a compressed GUID_DEFINED section wrapping the nested FV that holds + OneCrypto, whose raw compressed stream is served as + ONE_CRYPTO_IMAGE_FORMAT_GUIDED_FV for DXE to decode. + + A direct PE32 is preferred when both are present. + + @param[in] FvHeader Firmware volume to scan. + + @retval EFI_SUCCESS OneCrypto located; module globals populated. + @retval EFI_NOT_FOUND Not found in this FV. +**/ +STATIC +EFI_STATUS +ScanFvForOneCrypto ( + IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader + ) +{ + EFI_STATUS Status; + EFI_FFS_FILE_HEADER *FileHeader; + EFI_COMMON_SECTION_HEADER *Section; + EFI_COMMON_SECTION_HEADER *ContainerSection; + VOID *SectionData; + UINTN SectionDataSize; + UINTN DataOffset; + UINTN SectionSize; + + ContainerSection = NULL; + FileHeader = NULL; + + if (FvHeader == NULL) { + return EFI_INVALID_PARAMETER; + } + + while (TRUE) { + Status = FfsFindNextFile (EFI_FV_FILETYPE_ALL, FvHeader, &FileHeader); + if (EFI_ERROR (Status)) { + break; + } + + // + // Preferred: the OneCrypto file carrying a direct PE32 image. + // + if (CompareGuid (&FileHeader->Name, &mOneCryptoBinaryGuid)) { + Status = FfsFindSectionData (EFI_SECTION_PE32, FileHeader, &SectionData, &SectionDataSize); + if (!EFI_ERROR (Status) && (SectionData != NULL) && (SectionDataSize != 0)) { + mOneCryptoImageData = SectionData; + mOneCryptoImageSize = SectionDataSize; + mOneCryptoImageFormat = ONE_CRYPTO_IMAGE_FORMAT_PE32; + DEBUG (( + DEBUG_INFO, + "OneCryptoImageProviderMm: Found direct OneCrypto PE32 size=0x%Lx\n", + (UINT64)SectionDataSize + )); + return EFI_SUCCESS; + } + } + + // + // Compressed source: the dedicated OneCrypto container FV, a file tagged + // with the well-known ONE_CRYPTO_CONTAINER_FV_GUID whose GUID_DEFINED + // section wraps the nested FV holding OneCrypto. Matching by this file + // identity keeps discovery deterministic when the boot FV also carries other + // compressed nested FVs (e.g. the StandaloneMM payload FV). The provider + // does not inspect the compression scheme -- it serves the raw section and + // the DXE loader decodes it via ExtractGuidedSectionLib. + // + if ((ContainerSection == NULL) && + CompareGuid (&FileHeader->Name, &mOneCryptoContainerFvGuid)) + { + Status = FfsFindSection (EFI_SECTION_GUID_DEFINED, FileHeader, &Section); + if (!EFI_ERROR (Status)) { + ContainerSection = Section; + } + } + } + + if (ContainerSection != NULL) { + if (IS_SECTION2 (ContainerSection)) { + DataOffset = ((CONST EFI_GUID_DEFINED_SECTION2 *)ContainerSection)->DataOffset; + SectionSize = SECTION2_SIZE (ContainerSection); + } else { + DataOffset = ((CONST EFI_GUID_DEFINED_SECTION *)ContainerSection)->DataOffset; + SectionSize = SECTION_SIZE (ContainerSection); + } + + if ((DataOffset < SectionSize) && (SectionSize > sizeof (EFI_COMMON_SECTION_HEADER))) { + // + // Hand DXE the whole GUID_DEFINED section, header included, so it can + // decode it with ExtractGuidedSectionLib's registered handler. + // + mOneCryptoImageData = (UINT8 *)ContainerSection; + mOneCryptoImageSize = SectionSize; + mOneCryptoImageFormat = ONE_CRYPTO_IMAGE_FORMAT_GUIDED_FV; + DEBUG (( + DEBUG_INFO, + "OneCryptoImageProviderMm: Serving GUID_DEFINED section to DXE size=0x%Lx\n", + (UINT64)mOneCryptoImageSize + )); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Locate OneCrypto across the StandaloneMM FV and FV2 HOBs. The result is cached + in module globals for subsequent chunked requests. + + @retval EFI_SUCCESS OneCrypto located. + @retval EFI_NOT_FOUND OneCrypto not present in any MM firmware volume. +**/ +STATIC +EFI_STATUS +LocateOneCryptoImage ( + VOID + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + if ((mOneCryptoImageData != NULL) && (mOneCryptoImageSize != 0)) { + return EFI_SUCCESS; + } + + // + // FV3 Hobs supersede FV and FV2 + // + for (Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV3, GetHobList ()); + Hob.Raw != NULL; + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV3, GET_NEXT_HOB (Hob))) + { + if ((Hob.FirmwareVolume3 == NULL) || (Hob.FirmwareVolume3->Length == 0)) { + continue; + } + + if (!EFI_ERROR (ScanFvForOneCrypto ((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)Hob.FirmwareVolume3->BaseAddress))) { + return EFI_SUCCESS; + } + } + + for (Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, GetHobList ()); + Hob.Raw != NULL; + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, GET_NEXT_HOB (Hob))) + { + if ((Hob.FirmwareVolume == NULL) || (Hob.FirmwareVolume->Length == 0)) { + continue; + } + + if (!EFI_ERROR (ScanFvForOneCrypto ((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)Hob.FirmwareVolume->BaseAddress))) { + return EFI_SUCCESS; + } + } + + for (Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV2, GetHobList ()); + Hob.Raw != NULL; + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV2, GET_NEXT_HOB (Hob))) + { + if ((Hob.FirmwareVolume2 == NULL) || (Hob.FirmwareVolume2->Length == 0)) { + continue; + } + + if (!EFI_ERROR (ScanFvForOneCrypto ((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)Hob.FirmwareVolume2->BaseAddress))) { + return EFI_SUCCESS; + } + } + + DEBUG ((DEBUG_ERROR, "OneCryptoImageProviderMm: OneCrypto not found in any MM FV HOB\n")); + return EFI_NOT_FOUND; +} + +/** + MM communication handler that provides OneCrypto image bytes to DXE. + + Request protocol (ONE_CRYPTO_IMAGE_PROVIDER_MSG): + - RequestedSize == 0 : size query. Returns TotalImageSize + Format. + - RequestedSize > 0 : returns up to RequestedSize bytes at Offset. + + @param[in] DispatchHandle Dispatch handle for this handler. + @param[in] Context Handler context (unused). + @param[in, out] CommBuffer Message payload buffer. + @param[in, out] CommBufferSize Input/output payload buffer size. + + @retval EFI_SUCCESS Always. Per the PI spec an MMI handler may only return one + of EFI_SUCCESS / EFI_WARN_INTERRUPT_SOURCE_QUIESCED / + EFI_WARN_INTERRUPT_SOURCE_PENDING / EFI_INTERRUPT_PENDING. + The operational result is reported in the response message + Status field (ONE_CRYPTO_IMAGE_PROVIDER_MSG.Status). +**/ +STATIC +EFI_STATUS +EFIAPI +OneCryptoImageProviderHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + ONE_CRYPTO_IMAGE_PROVIDER_MSG *Msg; + UINTN HeaderSize; + UINTN InputBufferSize; + UINTN MaxPayload; + UINTN Remaining; + UINTN CopySize; + UINT32 LocalSignature; + UINT32 LocalVersion; + UINT64 LocalOffset; + UINT32 LocalRequestedSize; + EFI_STATUS Status; + + if ((CommBuffer == NULL) || (CommBufferSize == NULL)) { + // + // The MM core always supplies both for a registered handler; a NULL here is + // a framework contract violation that cannot be reported through the buffer. + // MMI handlers may only return the PI-defined status codes, so quiesce. + // + ASSERT (FALSE); + return EFI_SUCCESS; + } + + HeaderSize = OFFSET_OF (ONE_CRYPTO_IMAGE_PROVIDER_MSG, Data); + + // + // Snapshot *CommBufferSize once before use: on AARCH64 CommBuffer lives in + // Non-Secure DRAM and could be changed concurrently by Normal World. + // + InputBufferSize = *CommBufferSize; + if (InputBufferSize < HeaderSize) { + // + // Too small to hold the response header (which carries Status), so the + // result cannot be reported back. Quiesce. + // + ASSERT (FALSE); + return EFI_SUCCESS; + } + + if ((InputBufferSize - HeaderSize) > MAX_UINT32) { + ASSERT (FALSE); + return EFI_SUCCESS; + } + + // + // Ensure the entire comm buffer lies in Non-Secure memory and does not + // overlap MMRAM before MM reads request fields from it or writes the + // response payload back into it. + // + if (!MmCommBufferValid ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBuffer, InputBufferSize)) { + // + // Comm buffer overlaps MMRAM or is otherwise invalid; not safe to touch, so + // the result cannot be reported through Status. Quiesce. + // + DEBUG ((DEBUG_ERROR, "OneCryptoImageProviderMm: CommBuffer 0x%p size 0x%Lx failed MM validation\n", CommBuffer, (UINT64)InputBufferSize)); + ASSERT (FALSE); + return EFI_SUCCESS; + } + + // + // From here the comm buffer is safe and large enough for the response header. + // Operational results are reported through Msg->Status; the MMI handler always + // returns EFI_SUCCESS (PI MM handlers may only return EFI_SUCCESS / + // EFI_WARN_INTERRUPT_SOURCE_QUIESCED / EFI_WARN_INTERRUPT_SOURCE_PENDING / + // EFI_INTERRUPT_PENDING). + // + Msg = (ONE_CRYPTO_IMAGE_PROVIDER_MSG *)CommBuffer; + ZeroMem (&Msg->ImageGuid, sizeof (Msg->ImageGuid)); + Msg->TotalImageSize = 0; + Msg->ReturnedSize = 0; + Msg->Status = (UINT64)EFI_SUCCESS; + + // + // Snapshot caller-supplied fields before validation/use: on AARCH64 CommBuffer + // lives in Non-Secure DRAM and could be changed concurrently by Normal World, + // so every field must be read exactly once into a local. + // + LocalSignature = Msg->Signature; + LocalVersion = Msg->Version; + LocalOffset = Msg->Offset; + LocalRequestedSize = Msg->RequestedSize; + + if ((LocalSignature != ONE_CRYPTO_IMAGE_PROVIDER_SIGNATURE) || + (LocalVersion != ONE_CRYPTO_IMAGE_PROVIDER_VERSION)) + { + DEBUG ((DEBUG_ERROR, "OneCryptoImageProviderMm: Invalid request header\n")); + Msg->Status = (UINT64)EFI_INVALID_PARAMETER; + *CommBufferSize = HeaderSize; + return EFI_SUCCESS; + } + + Status = LocateOneCryptoImage (); + if (EFI_ERROR (Status)) { + Msg->Status = (UINT64)Status; + *CommBufferSize = HeaderSize; + return EFI_SUCCESS; + } + + // + // TotalImageSize is UINT32; guard against >4 GiB payloads. + // + if (mOneCryptoImageSize > MAX_UINT32) { + DEBUG ((DEBUG_ERROR, "OneCryptoImageProviderMm: Image size 0x%Lx exceeds UINT32 max\n", (UINT64)mOneCryptoImageSize)); + Msg->Status = (UINT64)EFI_UNSUPPORTED; + *CommBufferSize = HeaderSize; + return EFI_SUCCESS; + } + + // + // Compute the transport-integrity CRC32 once and cache it. The resident + // image never changes, so a single pass suffices for every chunk request. + // + if (mOneCryptoImageCrc32 == 0) { + mOneCryptoImageCrc32 = CalculateCrc32 (mOneCryptoImageData, mOneCryptoImageSize); + } + + Msg->ImageGuid = mOneCryptoBinaryGuid; + Msg->TotalImageSize = (UINT32)mOneCryptoImageSize; + Msg->Format = mOneCryptoImageFormat; + Msg->Crc32 = mOneCryptoImageCrc32; + Msg->ReturnedSize = 0; + + // + // Size query: return total size + format, no payload. + // + if (LocalRequestedSize == 0) { + Msg->Status = (UINT64)EFI_SUCCESS; + *CommBufferSize = HeaderSize; + return EFI_SUCCESS; + } + + if (LocalOffset > mOneCryptoImageSize) { + DEBUG ((DEBUG_ERROR, "OneCryptoImageProviderMm: Invalid offset 0x%Lx for size 0x%Lx\n", LocalOffset, (UINT64)mOneCryptoImageSize)); + Msg->Status = (UINT64)EFI_INVALID_PARAMETER; + *CommBufferSize = HeaderSize; + return EFI_SUCCESS; + } + + MaxPayload = InputBufferSize - HeaderSize; + Remaining = mOneCryptoImageSize - (UINTN)LocalOffset; + CopySize = LocalRequestedSize; + + if (CopySize > MaxPayload) { + CopySize = MaxPayload; + } + + if (CopySize > Remaining) { + CopySize = Remaining; + } + + if (CopySize > 0) { + CopyMem (Msg->Data, (CONST UINT8 *)mOneCryptoImageData + (UINTN)LocalOffset, CopySize); + } + + Msg->ReturnedSize = (UINT32)CopySize; + Msg->Status = (UINT64)EFI_SUCCESS; + *CommBufferSize = HeaderSize + CopySize; + + return EFI_SUCCESS; +} + +/** + ReadyToLock notification callback. + + By the time MM ReadyToLock is signaled, the OneCrypto image has already been + fetched and launched during DXE, so the image-provider MMI handler is no longer + needed. Unregister it to remove its Non-Secure-reachable attack surface for the + remainder of boot and runtime. + + This runs outside MMI handler dispatch, so unregistering here is safe (there is + no self-unregister-during-dispatch hazard). + + @param[in] Protocol Installed protocol GUID (unused). + @param[in] Interface Protocol interface (unused). + @param[in] Handle Handle the protocol was installed on (unused). + + @retval EFI_SUCCESS Always. +**/ +STATIC +EFI_STATUS +EFIAPI +OneCryptoImageProviderReadyToLock ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + + if (mDispatchHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gMmst->MmiHandlerUnRegister (mDispatchHandle); + mDispatchHandle = NULL; + DEBUG ((DEBUG_INFO, "OneCryptoImageProviderMm: Image-provider handler unregistered at ReadyToLock: %r\n", Status)); + + return EFI_SUCCESS; +} + +/** + Module entry point. Registers the OneCrypto image-provider MM handler. + + @param[in] ImageHandle Image handle. + @param[in] MmSystemTable MM system table. + + @retval EFI_SUCCESS Handler registered. +**/ +EFI_STATUS +EFIAPI +MmEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *MmSystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE DispatchHandle; + + DispatchHandle = NULL; + Status = gMmst->MmiHandlerRegister ( + OneCryptoImageProviderHandler, + &gOneCryptoImageProviderGuid, + &DispatchHandle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoImageProviderMm: Failed to register handler: %r\n", Status)); + return Status; + } + + mDispatchHandle = DispatchHandle; + DEBUG ((DEBUG_INFO, "OneCryptoImageProviderMm: Handler registered for %g\n", &gOneCryptoImageProviderGuid)); + + // + // The OneCrypto image is fetched and launched during DXE, so the provider is + // only needed until then. Register a ReadyToLock notification to unregister the + // handler afterward, removing its Non-Secure-reachable attack surface for the + // rest of boot and runtime. A failure here is non-fatal: the handler still works, + // it just will not self-tear-down. + // + Status = gMmst->MmRegisterProtocolNotify ( + &gEfiMmReadyToLockProtocolGuid, + OneCryptoImageProviderReadyToLock, + &mReadyToLockRegistration + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoImageProviderMm: Failed to register ReadyToLock notify: %r\n", Status)); + } + + return EFI_SUCCESS; +} diff --git a/OneCryptoPkg/OneCryptoLoaders/OneCryptoImageProviderStandaloneMm.inf b/OneCryptoPkg/OneCryptoLoaders/OneCryptoImageProviderStandaloneMm.inf new file mode 100644 index 000000000..4ba86b255 --- /dev/null +++ b/OneCryptoPkg/OneCryptoLoaders/OneCryptoImageProviderStandaloneMm.inf @@ -0,0 +1,43 @@ +## @file +# Provides OneCrypto PE32 image bytes from StandaloneMM over MM communication. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OneCryptoImageProviderStandaloneMm + FILE_GUID = 60A67696-D50D-488E-B436-82D0D3A11E8F + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + ENTRY_POINT = MmEntry + +[Sources] + OneCryptoImageProviderMm.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + StandaloneMmPkg/StandaloneMmPkg.dec + OneCryptoPkg/OneCryptoPkg.dec + +[LibraryClasses] + StandaloneMmDriverEntryPoint + BaseLib + BaseMemoryLib + DebugLib + MmServicesTableLib + StandaloneMmMemLib + HobLib + FvLib + +[Protocols] + gEfiMmReadyToLockProtocolGuid ## NOTIFY + +[Depex] + TRUE + +[BuildOptions] + GCC:*_CLANGPDB_*_DLINK_FLAGS = /ALIGN:4096 diff --git a/OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeFromMm.c b/OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeFromMm.c new file mode 100644 index 000000000..aa9f5cb27 --- /dev/null +++ b/OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeFromMm.c @@ -0,0 +1,898 @@ +/** @file + OneCryptoLoaderDxeFromMm.c + + DXE loader that fetches OneCrypto image bytes from StandaloneMM via + EFI_MM_COMMUNICATION2_PROTOCOL, then LoadImage()s the fetched PE32 image. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +// +// Target size for the MM communication buffer we allocate. The real MM +// communication region is a fixed, platform-defined size (e.g. PcdMmBufferSize +// on ARM); SendImageProviderRequest adapts down to it via the +// EFI_BAD_BUFFER_SIZE handshake, so this is only an upper bound on how big a +// chunk we attempt, not a per-platform requirement. +// +#define ONE_CRYPTO_COMM_BUFFER_TARGET_SIZE 0x30000 + +STATIC EFI_GUID mOneCryptoBinaryGuid = ONE_CRYPTO_BINARY_GUID; + +// The dependencies of the shared library, must live as long as the shared code is used. +STATIC ONE_CRYPTO_DEPENDENCIES *mOneCryptoDepends = NULL; + +// Crypto protocol for the shared library. Using VOID* to be agnostic about protocol structure size/layout. +STATIC VOID *mOneCryptoProtocol = NULL; + +// Lazy RNG state tracking. +STATIC EFI_RNG_PROTOCOL *mCachedRngProtocol = NULL; + +/** + Lazy RNG implementation that locates EFI_RNG_PROTOCOL on first use. +**/ +BOOLEAN +EFIAPI +LazyPlatformGetRandomNumber64 ( + OUT UINT64 *Rand + ) +{ + EFI_STATUS Status; + + if (Rand == NULL) { + DEBUG ((DEBUG_ERROR, "LazyPlatformGetRandomNumber64: Null Rand pointer\n")); + return FALSE; + } + + if (mCachedRngProtocol == NULL) { + Status = gBS->LocateProtocol (&gEfiRngProtocolGuid, NULL, (VOID **)&mCachedRngProtocol); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "LazyPlatformGetRandomNumber64: EFI_RNG_PROTOCOL not available: %r\n", Status)); + return FALSE; + } + } + + Status = mCachedRngProtocol->GetRNG (mCachedRngProtocol, NULL, sizeof (UINT64), (UINT8 *)Rand); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "LazyPlatformGetRandomNumber64: GetRNG failed: %r\n", Status)); + return FALSE; + } + + return TRUE; +} + +/** + Installs shared dependencies required for the crypto entrypoint. +**/ +STATIC +VOID +InstallSharedDependencies ( + OUT ONE_CRYPTO_DEPENDENCIES *OneCryptoDepends + ) +{ + OneCryptoDepends->Major = ONE_CRYPTO_DEPENDENCIES_VERSION_MAJOR; + OneCryptoDepends->Minor = ONE_CRYPTO_DEPENDENCIES_VERSION_MINOR; + OneCryptoDepends->Reserved = 0; + OneCryptoDepends->AllocatePool = AllocatePool; + OneCryptoDepends->FreePool = FreePool; + OneCryptoDepends->DebugPrint = DebugPrint; + OneCryptoDepends->GetTime = gRT->GetTime; + OneCryptoDepends->GetRandomNumber64 = LazyPlatformGetRandomNumber64; + OneCryptoDepends->MicroSecondDelay = gBS->Stall; +} + +/** + Gets exported CryptoEntry from a loaded image. +**/ +STATIC +EFI_STATUS +EFIAPI +GetEntryFromLoadedImage ( + IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage, + OUT CRYPTO_ENTRY *Entry + ) +{ + EFI_STATUS Status; + UINT32 Rva; + INTERNAL_IMAGE_CONTEXT Image; + EFI_IMAGE_EXPORT_DIRECTORY *Exports; + + if ((LoadedImage == NULL) || (Entry == NULL) || (LoadedImage->ImageBase == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Image, sizeof (Image)); + Image.Context.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase; + Image.Context.ImageSize = (UINT64)LoadedImage->ImageSize; + Image.Context.Handle = LoadedImage->ImageBase; + Image.Context.ImageRead = PeCoffLoaderImageReadFromMemory; + + Status = PeCoffLoaderGetImageInfo (&Image.Context); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Image.Context.ImageType != EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) { + DEBUG ((DEBUG_ERROR, "Invalid image type: %d\n", Image.Context.ImageType)); + return EFI_UNSUPPORTED; + } + + Status = GetExportDirectoryInPeCoffImage (&Image, &Exports); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = FindExportedFunction (&Image, Exports, EXPORTED_ENTRY_NAME, &Rva); + if (EFI_ERROR (Status)) { + return Status; + } + + *Entry = (CRYPTO_ENTRY)((EFI_PHYSICAL_ADDRESS)LoadedImage->ImageBase + Rva); + return EFI_SUCCESS; +} + +/** + Sends one image-provider request to MM and adapts to the platform MM + communication buffer size. + + The MM communication buffer is a fixed, platform-defined region whose size is + not knowable a priori (on ARM it is PcdMmBufferSize). This routine therefore + transmits only the bytes each request needs -- the MM communicate header, the + provider message header, and RequestedSize payload bytes -- rather than the + whole local allocation. If the transport reports EFI_BAD_BUFFER_SIZE it also + returns its maximum region size, which this routine adopts: it clamps the + requested payload to what the region can carry and retries, and writes the + adopted size back through CommBufferAllocSize so the caller sizes subsequent + chunk requests to the platform maximum. + + @param[in] MmComm MM communication protocol. + @param[in, out] CommBuffer Communication buffer (may be reallocated). + @param[in, out] CommBufferAllocSize Effective communication buffer size; on + exit, clamped to the platform maximum if + the transport reported one. + @param[in] Offset Image offset being requested. + @param[in] RequestedSize Payload bytes requested (0 = size query). + @param[out] Msg Located provider message within CommBuffer. + @param[out] MsgSize Size of the returned message payload. + + @retval EFI_SUCCESS Request completed. + @retval EFI_BAD_BUFFER_SIZE The platform MM buffer cannot carry even a + one-byte payload. + @retval Others Transport or protocol error. +**/ +STATIC +EFI_STATUS +SendImageProviderRequest ( + IN EFI_MM_COMMUNICATION2_PROTOCOL *MmComm, + IN OUT VOID **CommBuffer, + IN OUT UINTN *CommBufferAllocSize, + IN UINT64 Offset, + IN UINT32 RequestedSize, + OUT ONE_CRYPTO_IMAGE_PROVIDER_MSG **Msg, + OUT UINTN *MsgSize + ) +{ + EFI_MM_COMMUNICATE_HEADER *CommHeader; + ONE_CRYPTO_IMAGE_PROVIDER_MSG *LocalMsg; + UINTN CommHeaderOverhead; + UINTN MsgHeaderSize; + UINTN MessageLength; + UINTN CommSize; + UINTN MaxPayload; + UINT32 ThisRequest; + EFI_STATUS Status; + + CommHeaderOverhead = OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data); + MsgHeaderSize = OFFSET_OF (ONE_CRYPTO_IMAGE_PROVIDER_MSG, Data); + ThisRequest = RequestedSize; + + // + // Issue the request, transmitting only the bytes it needs. On + // EFI_BAD_BUFFER_SIZE the transport reports its fixed region size in CommSize + // (see ArmPkg MmCommunicationDxe): adopt it, clamp the payload to fit, and + // retry. Because every retry strictly reduces the request against a fixed + // region, the loop converges. + // + while (TRUE) { + MessageLength = MsgHeaderSize + ThisRequest; + CommSize = CommHeaderOverhead + MessageLength; + + // + // Defensive: grow the local buffer if a request ever needs more than it + // holds. The caller sizes requests from *CommBufferAllocSize, so the normal + // shrink-to-fit path never trips this. + // + if (CommSize > *CommBufferAllocSize) { + FreePool (*CommBuffer); + *CommBuffer = AllocateZeroPool (CommSize); + if (*CommBuffer == NULL) { + *CommBufferAllocSize = 0; + return EFI_OUT_OF_RESOURCES; + } + + *CommBufferAllocSize = CommSize; + } + + CommHeader = (EFI_MM_COMMUNICATE_HEADER *)(*CommBuffer); + ZeroMem (CommHeader, CommSize); + + CopyGuid (&CommHeader->HeaderGuid, &gOneCryptoImageProviderGuid); + CommHeader->MessageLength = MessageLength; + + LocalMsg = (ONE_CRYPTO_IMAGE_PROVIDER_MSG *)CommHeader->Data; + LocalMsg->Signature = ONE_CRYPTO_IMAGE_PROVIDER_SIGNATURE; + LocalMsg->Version = ONE_CRYPTO_IMAGE_PROVIDER_VERSION; + LocalMsg->Offset = Offset; + LocalMsg->RequestedSize = ThisRequest; + + Status = MmComm->Communicate (MmComm, *CommBuffer, *CommBuffer, &CommSize); + DEBUG (( + DEBUG_INFO, + "OneCryptoLoaderDxeFromMm: MmComm->Communicate offset=0x%Lx req=0x%x status=%r commSize=0x%Lx\n", + Offset, + ThisRequest, + Status, + (UINT64)CommSize + )); + + if (Status != EFI_BAD_BUFFER_SIZE) { + break; + } + + // + // The transport rejected the size and returned its fixed region size in + // CommSize. The region must hold at least the headers plus one payload byte + // to make any progress. + // + if (CommSize <= (CommHeaderOverhead + MsgHeaderSize)) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: MM comm region too small max=0x%Lx needs>0x%Lx\n", + (UINT64)CommSize, + (UINT64)(CommHeaderOverhead + MsgHeaderSize) + )); + return EFI_BAD_BUFFER_SIZE; + } + + MaxPayload = CommSize - CommHeaderOverhead - MsgHeaderSize; + *CommBufferAllocSize = CommSize; + + // + // If clamping to the region does not actually reduce the request, the + // rejection is not one we can adapt to -- fail rather than spin. + // + if (ThisRequest <= MaxPayload) { + return EFI_BAD_BUFFER_SIZE; + } + + ThisRequest = (UINT32)MaxPayload; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (CommSize < (CommHeaderOverhead + MsgHeaderSize)) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: Response too small commSize=0x%Lx required>=0x%Lx\n", + (UINT64)CommSize, + (UINT64)(CommHeaderOverhead + MsgHeaderSize) + )); + return EFI_PROTOCOL_ERROR; + } + + *Msg = (ONE_CRYPTO_IMAGE_PROVIDER_MSG *)((EFI_MM_COMMUNICATE_HEADER *)(*CommBuffer))->Data; + *MsgSize = CommSize - CommHeaderOverhead; + return EFI_SUCCESS; +} + +/** + Decodes the compressed GUID_DEFINED section handed over by the MM provider, + locates the OneCrypto file within the decompressed nested FV, and returns a + standalone copy of its PE32 image bytes. + + @param[in] GuidedSection The GUID_DEFINED section (header included). + @param[in] GuidedSectionSize Size of the GUID_DEFINED section. + @param[out] Pe32Data Newly allocated buffer holding the OneCrypto PE32. + @param[out] Pe32Size Size of the returned PE32 image. + + @retval EFI_SUCCESS PE32 image extracted and returned in Pe32Data. +**/ +STATIC +EFI_STATUS +ExtractOneCryptoPe32FromGuidedFv ( + IN VOID *GuidedSection, + IN UINTN GuidedSectionSize, + OUT VOID **Pe32Data, + OUT UINTN *Pe32Size + ) +{ + EFI_STATUS Status; + UINT32 DecodedSize; + UINT32 ScratchSize; + UINT16 SectionAttribute; + UINT32 AuthenticationStatus; + VOID *Decoded; + VOID *Scratch; + EFI_FIRMWARE_VOLUME_HEADER *FvHeader; + EFI_COMMON_SECTION_HEADER *FvSection; + EFI_FFS_FILE_HEADER *FileHeader; + VOID *SectionData; + UINTN SectionDataSize; + VOID *Pe32Copy; + + *Pe32Data = NULL; + *Pe32Size = 0; + Decoded = NULL; + Scratch = NULL; + + // + // Need at least a GUID_DEFINED section header before the handler can inspect it. + // + if ((GuidedSection == NULL) || (GuidedSectionSize < sizeof (EFI_GUID_DEFINED_SECTION))) { + return EFI_INVALID_PARAMETER; + } + + // + // Query the registered guided-section handler for the decode + scratch buffer + // sizes. + // + Status = ExtractGuidedSectionGetInfo (GuidedSection, &DecodedSize, &ScratchSize, &SectionAttribute); + if (EFI_ERROR (Status) || (DecodedSize == 0)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: ExtractGuidedSectionGetInfo failed: %r size=0x%x\n", Status, DecodedSize)); + return EFI_COMPROMISED_DATA; + } + + Decoded = AllocatePool (DecodedSize); + if (Decoded == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Cleanup; + } + + if (ScratchSize != 0) { + Scratch = AllocatePool (ScratchSize); + if (Scratch == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Cleanup; + } + } + + // + // Decode via the registered guided-section handler. The section is + // PROCESSING_REQUIRED, so the handler expands it into our allocated buffer; + // Decoded then holds the decompressed nested FV. + // + Status = ExtractGuidedSectionDecode (GuidedSection, &Decoded, Scratch, &AuthenticationStatus); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: ExtractGuidedSectionDecode failed: %r\n", Status)); + Status = EFI_COMPROMISED_DATA; + goto Cleanup; + } + + // + // The decoded payload must be at least an FV header before we inspect it, or + // the Signature read below would run off the end of the decoded buffer. + // + if (DecodedSize < sizeof (EFI_FIRMWARE_VOLUME_HEADER)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Decoded payload too small for FV header size=0x%x\n", DecodedSize)); + Status = EFI_VOLUME_CORRUPTED; + goto Cleanup; + } + + // + // The decoded payload is the nested FV -- either the raw FV image, or a + // FIRMWARE_VOLUME_IMAGE section wrapping it. Resolve to the FV header. + // + if (((EFI_FIRMWARE_VOLUME_HEADER *)Decoded)->Signature == EFI_FVH_SIGNATURE) { + FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Decoded; + } else { + FvSection = NULL; + Status = FindFfsSectionInSections ( + Decoded, + DecodedSize, + EFI_SECTION_FIRMWARE_VOLUME_IMAGE, + &FvSection + ); + if (EFI_ERROR (Status) || (FvSection == NULL)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Decoded payload is not an FV: %r\n", Status)); + Status = EFI_VOLUME_CORRUPTED; + goto Cleanup; + } + + if (IS_SECTION2 (FvSection)) { + FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((EFI_COMMON_SECTION_HEADER2 *)FvSection + 1); + } else { + FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(FvSection + 1); + } + } + + // + // Bound the resolved FV against the decoded buffer before walking its files: + // FvHeader must sit inside Decoded with room for a full header, and the FV's + // self-described FvLength must not run past the end of the decoded payload. + // This stops a malformed nested FV from walking the FFS parser out of bounds. + // + if (((UINT8 *)FvHeader < (UINT8 *)Decoded) || + ((UINTN)((UINT8 *)FvHeader - (UINT8 *)Decoded) > DecodedSize) || + ((DecodedSize - (UINTN)((UINT8 *)FvHeader - (UINT8 *)Decoded)) < sizeof (EFI_FIRMWARE_VOLUME_HEADER)) || + (FvHeader->Signature != EFI_FVH_SIGNATURE) || + (FvHeader->FvLength > (DecodedSize - (UINTN)((UINT8 *)FvHeader - (UINT8 *)Decoded)))) + { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Decoded FV out of bounds of payload\n")); + Status = EFI_VOLUME_CORRUPTED; + goto Cleanup; + } + + // + // Find the OneCrypto file and its PE32 section within the decoded FV. + // + FileHeader = NULL; + Status = EFI_NOT_FOUND; + while (TRUE) { + if (EFI_ERROR (FfsFindNextFile (EFI_FV_FILETYPE_ALL, FvHeader, &FileHeader))) { + break; + } + + if (!CompareGuid (&FileHeader->Name, &mOneCryptoBinaryGuid)) { + continue; + } + + if (!EFI_ERROR (FfsFindSectionData (EFI_SECTION_PE32, FileHeader, &SectionData, &SectionDataSize)) && + (SectionData != NULL) && (SectionDataSize != 0)) + { + Pe32Copy = AllocateCopyPool (SectionDataSize, SectionData); + if (Pe32Copy == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Cleanup; + } + + *Pe32Data = Pe32Copy; + *Pe32Size = SectionDataSize; + Status = EFI_SUCCESS; + DEBUG (( + DEBUG_INFO, + "OneCryptoLoaderDxeFromMm: Extracted OneCrypto PE32 from decoded FV size=0x%Lx\n", + (UINT64)SectionDataSize + )); + goto Cleanup; + } + } + + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: OneCrypto PE32 not found in decoded FV\n")); + +Cleanup: + if (Decoded != NULL) { + FreePool (Decoded); + } + + if (Scratch != NULL) { + FreePool (Scratch); + } + + return Status; +} + +/** + Fetches complete OneCrypto PE32 image from MM provider using chunked requests. +**/ +STATIC +EFI_STATUS +FetchImageFromMm ( + OUT VOID **ImageData, + OUT UINTN *ImageSize, + OUT UINT32 *Format + ) +{ + EFI_MM_COMMUNICATION2_PROTOCOL *MmComm; + EFI_STATUS Status; + VOID *CommBuffer; + UINTN CommBufferAllocSize; + ONE_CRYPTO_IMAGE_PROVIDER_MSG *Msg; + UINTN MsgSize; + VOID *LocalImage; + UINTN HeaderSize; + UINTN ChunkCapacity; + UINTN Offset; + UINTN Remaining; + UINTN ThisChunk; + UINT32 ExpectedTotal; + UINT32 ExpectedCrc; + UINT32 ChunkReturnedSize; + EFI_STATUS ChunkStatus; + + *ImageData = NULL; + *ImageSize = 0; + *Format = ONE_CRYPTO_IMAGE_FORMAT_PE32; + + Status = gBS->LocateProtocol (&gEfiMmCommunication2ProtocolGuid, NULL, (VOID **)&MmComm); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: gEfiMmCommunication2ProtocolGuid not found: %r\n", Status)); + return Status; + } + + // + // Target comm-buffer size; the transport adapts down to the platform's fixed + // MM region via SendImageProviderRequest (see ONE_CRYPTO_COMM_BUFFER_TARGET_SIZE). + // + CommBufferAllocSize = ONE_CRYPTO_COMM_BUFFER_TARGET_SIZE; + CommBuffer = AllocateZeroPool (CommBufferAllocSize); + if (CommBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + HeaderSize = OFFSET_OF (ONE_CRYPTO_IMAGE_PROVIDER_MSG, Data); + + // Size query. + Status = SendImageProviderRequest ( + MmComm, + &CommBuffer, + &CommBufferAllocSize, + 0, + 0, + &Msg, + &MsgSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Size query request failed: %r\n", Status)); + goto Exit; + } + + // + // The MMI handler always returns EFI_SUCCESS at the transport level; the + // operational result is carried in Msg->Status. + // + Status = (EFI_STATUS)(UINTN)Msg->Status; + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Size-query provider status: %r\n", Status)); + goto Exit; + } + + if ((Msg->TotalImageSize == 0) || (Msg->ReturnedSize != 0)) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: Invalid size-query response total=0x%x returned=0x%x msgSize=0x%Lx\n", + Msg->TotalImageSize, + Msg->ReturnedSize, + (UINT64)MsgSize + )); + Status = EFI_PROTOCOL_ERROR; + goto Exit; + } + + // Validate ImageGuid in size-query response for consistency with chunk validation. + if (!CompareGuid (&Msg->ImageGuid, &mOneCryptoBinaryGuid)) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: ImageGuid mismatch in size-query response\n" + )); + Status = EFI_COMPROMISED_DATA; + goto Exit; + } + + // Snapshot TotalImageSize from the size-query response into a local variable. + // All subsequent loop logic and the final *ImageSize use this value exclusively + // to detect mid-transfer tampering and prevent heap-overflow via inflated totals. + ExpectedTotal = Msg->TotalImageSize; + *Format = Msg->Format; + + // Snapshot the transport-integrity CRC32 from the size-query response. + ExpectedCrc = Msg->Crc32; + + LocalImage = AllocatePool (ExpectedTotal); + if (LocalImage == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + ChunkCapacity = CommBufferAllocSize - OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data) - HeaderSize; + if (ChunkCapacity == 0) { + Status = EFI_BAD_BUFFER_SIZE; + FreePool (LocalImage); + goto Exit; + } + + Offset = 0; + while (Offset < ExpectedTotal) { + Remaining = ExpectedTotal - Offset; + ThisChunk = (Remaining < ChunkCapacity) ? Remaining : ChunkCapacity; + + Status = SendImageProviderRequest ( + MmComm, + &CommBuffer, + &CommBufferAllocSize, + Offset, + (UINT32)ThisChunk, + &Msg, + &MsgSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: Chunk request failed offset=0x%Lx req=0x%Lx status=%r\n", + (UINT64)Offset, + (UINT64)ThisChunk, + Status + )); + FreePool (LocalImage); + goto Exit; + } + + ChunkCapacity = CommBufferAllocSize - OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data) - HeaderSize; + if (ChunkCapacity == 0) { + Status = EFI_BAD_BUFFER_SIZE; + FreePool (LocalImage); + goto Exit; + } + + // + // Snapshot response fields out of the shared comm buffer before validating + // or using them: Msg points into memory the provider (and, on AARCH64, any + // other Non-Secure agent) can mutate concurrently, so each field must be + // read exactly once to avoid a TOCTOU between the bounds check and the copy. + // + ChunkReturnedSize = Msg->ReturnedSize; + ChunkStatus = (EFI_STATUS)(UINTN)Msg->Status; + + if (EFI_ERROR (ChunkStatus)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Chunk provider status offset=0x%Lx: %r\n", (UINT64)Offset, ChunkStatus)); + Status = ChunkStatus; + FreePool (LocalImage); + goto Exit; + } + + if ((ChunkReturnedSize == 0) || (ChunkReturnedSize > ThisChunk)) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: Invalid chunk response offset=0x%Lx requested=0x%Lx returned=0x%x msgSize=0x%Lx\n", + (UINT64)Offset, + (UINT64)ThisChunk, + ChunkReturnedSize, + (UINT64)MsgSize + )); + Status = EFI_PROTOCOL_ERROR; + FreePool (LocalImage); + goto Exit; + } + + // + // Defensive destination bound: LocalImage is exactly ExpectedTotal bytes. + // Guard the heap write independently of the per-chunk sizing above so a + // provider (or a future refactor) can never overflow the reassembly buffer. + // + if ((Offset + ChunkReturnedSize) > ExpectedTotal) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: Chunk overflows image offset=0x%Lx returned=0x%x total=0x%x\n", + (UINT64)Offset, + ChunkReturnedSize, + ExpectedTotal + )); + Status = EFI_PROTOCOL_ERROR; + FreePool (LocalImage); + goto Exit; + } + + CopyMem ((UINT8 *)LocalImage + Offset, Msg->Data, ChunkReturnedSize); + Offset += ChunkReturnedSize; + } + + // + // Transport-integrity check: recompute CRC32 over the reassembled image and + // compare against the provider's value. This catches accidental corruption or + // truncation of the chunked MM transfer during bring-up. It is NOT an + // authenticity check. + // + { + UINT32 ComputedCrc; + + ComputedCrc = CalculateCrc32 (LocalImage, ExpectedTotal); + if (ComputedCrc != ExpectedCrc) { + DEBUG (( + DEBUG_ERROR, + "OneCryptoLoaderDxeFromMm: CRC32 mismatch computed=0x%x expected=0x%x size=0x%x -- transport corruption\n", + ComputedCrc, + ExpectedCrc, + ExpectedTotal + )); + Status = EFI_CRC_ERROR; + FreePool (LocalImage); + goto Exit; + } + } + + *ImageData = LocalImage; + *ImageSize = ExpectedTotal; + Status = EFI_SUCCESS; + +Exit: + if (CommBuffer != NULL) { + FreePool (CommBuffer); + } + + return Status; +} + +/** + DXE entry point. +**/ +EFI_STATUS +EFIAPI +DxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *SectionData; + UINTN SectionSize; + CRYPTO_ENTRY Entry; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_HANDLE LoadedImageHandle; + UINT32 CryptoSize; + UINT32 ImageFormat; + VOID *Pe32Data; + UINTN Pe32Size; + + LoadedImageHandle = NULL; + LoadedImage = NULL; + SectionData = NULL; + SectionSize = 0; + CryptoSize = 0; + ImageFormat = ONE_CRYPTO_IMAGE_FORMAT_PE32; + + if (mOneCryptoDepends == NULL) { + mOneCryptoDepends = AllocatePool (sizeof (*mOneCryptoDepends)); + if (mOneCryptoDepends == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InstallSharedDependencies (mOneCryptoDepends); + } + + Status = FetchImageFromMm (&SectionData, &SectionSize, &ImageFormat); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: FetchImageFromMm failed: %r\n", Status)); + return Status; + } + + // + // When the secure-world provider cannot afford to decode the nested FV, it + // hands over the compressed guided section instead. Decode it here (normal + // world has ample memory) and extract the OneCrypto PE32 to load. + // + if (ImageFormat == ONE_CRYPTO_IMAGE_FORMAT_GUIDED_FV) { + Status = ExtractOneCryptoPe32FromGuidedFv (SectionData, SectionSize, &Pe32Data, &Pe32Size); + FreePool (SectionData); + SectionData = NULL; + SectionSize = 0; + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: ExtractOneCryptoPe32FromGuidedFv failed: %r\n", Status)); + return Status; + } + + SectionData = Pe32Data; + SectionSize = Pe32Size; + } + + Status = SystemTable->BootServices->LoadImage ( + FALSE, + ImageHandle, + NULL, + SectionData, + SectionSize, + &LoadedImageHandle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: LoadImage failed: %r\n", Status)); + goto Exit; + } + + Status = SystemTable->BootServices->HandleProtocol ( + LoadedImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (EFI_ERROR (Status) || (LoadedImage == NULL)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: HandleProtocol(LoadedImage) failed: %r\n", Status)); + goto Exit; + } + + Status = GetEntryFromLoadedImage (LoadedImage, &Entry); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: GetEntryFromLoadedImage failed: %r\n", Status)); + goto Exit; + } + + Status = Entry (mOneCryptoDepends, NULL, &CryptoSize); + if ((Status != EFI_BUFFER_TOO_SMALL) || (CryptoSize == 0)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Failed to query crypto protocol size: %r\n", Status)); + goto Exit; + } + + mOneCryptoProtocol = AllocatePool (CryptoSize); + if (mOneCryptoProtocol == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Status = Entry (mOneCryptoDepends, &mOneCryptoProtocol, &CryptoSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: Crypto entry failed: %r\n", Status)); + FreePool (mOneCryptoProtocol); + mOneCryptoProtocol = NULL; + goto Exit; + } + + Status = SystemTable->BootServices->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gOneCryptoProtocolGuid, + mOneCryptoProtocol, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "OneCryptoLoaderDxeFromMm: InstallProtocol failed: %r\n", Status)); + goto Exit; + } + + Status = EFI_SUCCESS; + +Exit: + // + // On any failure after LoadImage, unload the image and release the crypto + // protocol buffer so an error path does not leak the loaded image pages or + // the (allocated but not-yet-installed) protocol. + // + if (EFI_ERROR (Status)) { + if (mOneCryptoProtocol != NULL) { + FreePool (mOneCryptoProtocol); + mOneCryptoProtocol = NULL; + } + + if (LoadedImageHandle != NULL) { + SystemTable->BootServices->UnloadImage (LoadedImageHandle); + } + } + + if (SectionData != NULL) { + FreePool (SectionData); + } + + if ((Status != EFI_SUCCESS) && (mOneCryptoDepends != NULL)) { + FreePool (mOneCryptoDepends); + mOneCryptoDepends = NULL; + } + + return Status; +} diff --git a/OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeFromMm.inf b/OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeFromMm.inf new file mode 100644 index 000000000..ff2f2b3ba --- /dev/null +++ b/OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeFromMm.inf @@ -0,0 +1,55 @@ +## @file +# Loads OneCrypto from StandaloneMM image provider and installs OneCrypto protocol. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OneCryptoLoaderDxeFromMm + FILE_GUID = 9CAAC6D7-7A89-4A1D-AB95-08CD20B8E44A + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DxeEntryPoint + +[Sources] + OneCryptoLoaderDxeFromMm.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + OneCryptoPkg/OneCryptoPkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + DebugLib + BaseMemoryLib + MemoryAllocationLib + DxeServicesLib + FvLib + ExtractGuidedSectionLib + PeCoffGetEntryPointLib + PeCoffLib + PeCoffExtraActionLib + PeCoffExtendedLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + SafeIntLib + RngLib + +[Protocols] + gOneCryptoProtocolGuid ## PRODUCES + gEfiRngProtocolGuid ## CONSUMES + gEfiMmCommunication2ProtocolGuid ## CONSUMES + +[Guids] + gOneCryptoImageProviderGuid ## CONSUMES + +[Depex] + gEfiMmCommunication2ProtocolGuid + +[BuildOptions] + GCC:*_CLANGPDB_*_DLINK_FLAGS = /ALIGN:4096 diff --git a/OneCryptoPkg/OneCryptoPkg.dec b/OneCryptoPkg/OneCryptoPkg.dec index 89fd9f8f3..871a997e7 100644 --- a/OneCryptoPkg/OneCryptoPkg.dec +++ b/OneCryptoPkg/OneCryptoPkg.dec @@ -26,6 +26,9 @@ [Guids] gOneCryptoPkgTokenSpaceGuid = { 0x36470E80, 0x36F2, 0x4BA0, { 0x8C, 0xC8, 0x93, 0x7C, 0x7D, 0x9F, 0xF8, 0x88 } } + ## OneCrypto image provider MM communication handler GUID + gOneCryptoImageProviderGuid = { 0x5B9B9A72, 0xA4DF, 0x4D57, { 0xA1, 0xD3, 0x3A, 0x88, 0xEF, 0xB2, 0x5B, 0x9E } } + [Protocols] ## OneCrypto Private Protocol ## diff --git a/OneCryptoPkg/OneCryptoPkg.dsc b/OneCryptoPkg/OneCryptoPkg.dsc index c17d3c20a..18c0aa58d 100644 --- a/OneCryptoPkg/OneCryptoPkg.dsc +++ b/OneCryptoPkg/OneCryptoPkg.dsc @@ -19,6 +19,13 @@ DEFINE NON_ACCEL = FALSE !endif +!ifndef ONECRYPTO_AARCH64_MM_DEBUG + # Opt-in AARCH64 StandaloneMM debug printing over the FF-A console (e.g. + # Hafnium). Off by default so the package stays SPM-agnostic; the FF-A + # console debug lib and PcdFfaLibConduitSmc are the only non-portable bits. + DEFINE ONECRYPTO_AARCH64_MM_DEBUG = FALSE +!endif + [PcdsPatchableInModule.X64] gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x17 @@ -61,6 +68,11 @@ # Ensure DEBUG prints are enabled (excluding VERBOSE: 0x8040004F & ~0x00400000 = 0x8000004F) gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000004F gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel|0x8000004F + # FF-A conduit (SVC vs SMC) is SPM-specific and only matters when the FF-A + # console debug lib is linked (ONECRYPTO_AARCH64_MM_DEBUG). Hafnium uses SVC. +!if $(ONECRYPTO_AARCH64_MM_DEBUG) == TRUE + gEfiMdeModulePkgTokenSpaceGuid.PcdFfaLibConduitSmc|FALSE +!endif # OneCryptoPkg Debug Configuration # DEBUG builds: Enable Debug Print (BIT1) and Debug Code (BIT2) = 0x06 @@ -272,7 +284,19 @@ BaseLib | MdePkg/Library/BaseLib/BaseLib.inf BaseMemoryLib | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + !if $(ONECRYPTO_AARCH64_MM_DEBUG) == TRUE + # FF-A console debug printing for AARCH64 MM (e.g. Hafnium). Only + # consumer of the FF-A conduit libs + PcdFfaLibConduitSmc; opt-in. + PrintLib | MdePkg/Library/BasePrintLib/BasePrintLib.inf + DebugLib | MdeModulePkg/Library/ArmFfaConsoleDebugLib/ArmFfaConsoleDebugStandaloneMmLib.inf + DebugPrintErrorLevelLib | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + ArmSmcLib | MdePkg/Library/ArmSmcLib/ArmSmcLib.inf + ArmSvcLib | MdePkg/Library/ArmSvcLib/ArmSvcLib.inf + !else DebugLib | MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + DebugPrintErrorLevelLib | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + SerialPortLib | MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf + !endif PcdLib | MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf RngLib | MdePkg/Library/BaseRngLibNull/BaseRngLibNull.inf # Drivers should use the protocol, GetRandomNumber64 will not work. RegisterFilterLib | MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf @@ -292,6 +316,37 @@ FvLib | StandaloneMmPkg/Library/FvLib/FvLib.inf } + OneCryptoPkg/OneCryptoLoaders/OneCryptoImageProviderStandaloneMm.inf { + + BaseLib | MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + !if $(ONECRYPTO_AARCH64_MM_DEBUG) == TRUE + # FF-A console debug printing for AARCH64 MM (e.g. Hafnium). Only + # consumer of the FF-A conduit libs + PcdFfaLibConduitSmc; opt-in. + PrintLib | MdePkg/Library/BasePrintLib/BasePrintLib.inf + DebugLib | MdeModulePkg/Library/ArmFfaConsoleDebugLib/ArmFfaConsoleDebugStandaloneMmLib.inf + DebugPrintErrorLevelLib | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + ArmSmcLib | MdePkg/Library/ArmSmcLib/ArmSmcLib.inf + ArmSvcLib | MdePkg/Library/ArmSvcLib/ArmSvcLib.inf + !else + DebugLib | MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + DebugPrintErrorLevelLib | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + SerialPortLib | MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf + !endif + PcdLib | MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + RegisterFilterLib | MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf + StackCheckFailureHookLib | MdePkg/Library/StackCheckFailureHookLibNull/StackCheckFailureHookLibNull.inf + StackCheckLib | MdePkg/Library/StackCheckLib/StackCheckLib.inf + SafeIntLib | MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf + MemoryAllocationLib | StandaloneMmPkg/Library/StandaloneMmMemoryAllocationLib/StandaloneMmMemoryAllocationLib.inf + StandaloneMmDriverEntryPoint | MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf + MmServicesTableLib | MdePkg/Library/StandaloneMmServicesTableLib/StandaloneMmServicesTableLib.inf + StandaloneMmMemLib | StandaloneMmPkg/Library/StandaloneMmMemLib/StandaloneMmMemLib.inf + HobLib | StandaloneMmPkg/Library/StandaloneMmHobLib/StandaloneMmHobLib.inf + FvLib | StandaloneMmPkg/Library/FvLib/FvLib.inf + ExtractGuidedSectionLib | StandaloneMmPkg/Library/StandaloneMmExtractGuidedSectionLib/StandaloneMmExtractGuidedSectionLib.inf + } + ############################################################################# ## AARCH64 OneCryptoBin START ## @@ -343,12 +398,13 @@ NULL | MdePkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf } - ## OneCryptoLoaderDxeByProtocol for AARCH64 + ## OneCryptoLoaderDxeFromMm for AARCH64 # - # This loader consumes gOneCryptoPrivateProtocolGuid installed by OneCryptoBinDxe - # and produces gOneCryptoProtocolGuid for consumers. + # This loader fetches OneCrypto PE bytes from StandaloneMM over + # gEfiMmCommunication2ProtocolGuid, then LoadImage()s and publishes + # gOneCryptoProtocolGuid for DXE consumers. ## - OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeByProtocol.inf { + OneCryptoPkg/OneCryptoLoaders/OneCryptoLoaderDxeFromMm.inf { BaseLib | MdePkg/Library/BaseLib/BaseLib.inf BaseMemoryLib | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf @@ -358,10 +414,19 @@ UefiRuntimeServicesTableLib | MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf DevicePathLib | MdePkg/Library/UefiDevicePathLibDevicePathProtocol/UefiDevicePathLibDevicePathProtocol.inf RngLib | MdePkg/Library/BaseRngLibNull/BaseRngLibNull.inf # Drivers should use the protocol, GetRandomNumber64 will not work. + PeCoffLib | MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf + PeCoffExtraActionLib | MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf + PeCoffExtendedLib | OneCryptoPkg/Library/PeCoffExtendedLib/PeCoffExtendedLib.inf + PeCoffGetEntryPointLib | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf + DxeServicesLib | MdePkg/Library/DxeServicesLib/DxeServicesLib.inf + FvLib | MdePkg/Library/FvLib/FvLib.inf + ExtractGuidedSectionLib | MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf + NULL | MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf RegisterFilterLib | MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf HobLib | MdePkg/Library/DxeHobLib/DxeHobLib.inf StackCheckFailureHookLib | MdePkg/Library/StackCheckFailureHookLibNull/StackCheckFailureHookLibNull.inf StackCheckLib | MdePkg/Library/StackCheckLib/StackCheckLib.inf + SafeIntLib | MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf UefiDriverEntryPoint | MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf UefiBootServicesTableLib | MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf MemoryAllocationLib | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf diff --git a/OneCryptoPkg/Plugin/OneCryptoBundler/OneCryptoBundler.py b/OneCryptoPkg/Plugin/OneCryptoBundler/OneCryptoBundler.py index 2a69a85d2..32ef2a518 100644 --- a/OneCryptoPkg/Plugin/OneCryptoBundler/OneCryptoBundler.py +++ b/OneCryptoPkg/Plugin/OneCryptoBundler/OneCryptoBundler.py @@ -193,12 +193,15 @@ def driver_files(driver_dir, driver_name, new_name): (f"{workspace}/OneCryptoPkg/OneCryptoBin/Integration/OneCryptoBinStandaloneMm.inf", "OneCryptoBinStandaloneMm.inf"), ], "OneCryptoLoaders": [ - # OneCryptoBinDxeLoader (OneCryptoLoaderDxe) - *driver_files(f"{package_build_dir}/OneCryptoLoaders/OneCryptoLoaderDxeByProtocol", "OneCryptoLoaderDxe", "OneCryptoLoaderDxe"), + # OneCryptoBinDxeLoader (AARCH64 path: OneCryptoLoaderDxeFromMm, packaged as OneCryptoLoaderDxe) + *driver_files(f"{package_build_dir}/OneCryptoLoaders/OneCryptoLoaderDxeFromMm", "OneCryptoLoaderDxeFromMm", "OneCryptoLoaderDxe"), (f"{workspace}/OneCryptoPkg/OneCryptoLoaders/Integration/OneCryptoLoaderDxe.inf", "OneCryptoLoaderDxe.inf"), # OneCryptoBinStandaloneMmLoader (OneCryptoLoaderStandaloneMm) *driver_files(f"{package_build_dir}/OneCryptoLoaders/OneCryptoLoaderStandaloneMm", "OneCryptoLoaderStandaloneMm", "OneCryptoLoaderStandaloneMm"), (f"{workspace}/OneCryptoPkg/OneCryptoLoaders/Integration/OneCryptoLoaderStandaloneMm.inf", "OneCryptoLoaderStandaloneMm.inf"), + # StandaloneMM image provider for AARCH64 fetch path + *driver_files(f"{package_build_dir}/OneCryptoLoaders/OneCryptoImageProviderStandaloneMm", "OneCryptoImageProviderStandaloneMm", "OneCryptoImageProviderStandaloneMm"), + (f"{workspace}/OneCryptoPkg/OneCryptoLoaders/Integration/OneCryptoImageProviderStandaloneMm.inf", "OneCryptoImageProviderStandaloneMm.inf"), ], "BuildInfo": [ (f"{build_output}/BUILD_REPORT.TXT", "BUILD_REPORT.TXT"),