Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 69 additions & 26 deletions OneCryptoPkg/Docs/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ 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 historically used a separate DXE Bin, and now also has an
MM-provider based single-copy prototype path. See
[Why the Difference?](#why-the-difference) for details.

## X64

Expand Down Expand Up @@ -52,45 +53,78 @@ Both StandaloneMm and SupvMm follow the same two-driver pattern:

## AARCH64

AARCH64 produces **4 drivers**: 2 from `OneCryptoBin` and 2 from
`OneCryptoLoaders`.
AARCH64 currently has two integration modes:

| Environment | Bin | Loader |
|-----------------|----------------------------|-----------------------------------|
| DXE | `OneCryptoBinDxe` | `OneCryptoLoaderDxeByProtocol` |
| StandaloneMm | `OneCryptoBinStandaloneMm` | `OneCryptoLoaderStandaloneMm` |
1. Legacy dual-bin mode (default): dedicated DXE Bin + protocol-based DXE loader.
2. Single-copy AARCH64 mode: secure-world MM Bin is the only stored
OneCrypto image; DXE fetches image bytes from MM and LoadImage()s them.

### Driver Set by AARCH64 Mode

Legacy dual-bin mode:

- DXE: `OneCryptoBinDxe` + `OneCryptoLoaderDxeByProtocol`
- StandaloneMm: `OneCryptoBinStandaloneMm` + `OneCryptoLoaderStandaloneMm`

Single-copy AARCH64 mode:

- DXE: `OneCryptoLoaderDxeFromMm`
- StandaloneMm: `OneCryptoImageProviderStandaloneMm` +
`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:
#### Legacy dual-bin mode

On AARCH64 the DXE Loader cannot directly read the secure-world FV, so this
mode uses a dedicated `OneCryptoBinDxe` (`DXE_DRIVER`) in the normal-world FV:

1. `OneCryptoBinDxe` is dispatched normally by the UEFI DXE dispatcher. Its
entry point installs `gOneCryptoPrivateProtocolGuid` with the crypto
constructor.
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.

#### Single-copy AARCH64 mode

This protocol-based approach avoids PE/COFF export parsing entirely.
This mode removes `OneCryptoBinDxe` from the normal-world FV and uses
`OneCryptoLoaderDxeFromMm`:

1. Loader uses `EFI_MM_COMMUNICATION2_PROTOCOL` and
`gOneCryptoImageProviderGuid` to query total image size.
2. Loader fetches OneCrypto PE32 bytes in chunks from MM.
3. Loader calls `LoadImage()` on the fetched bytes, resolves `CryptoEntry`,
injects dependencies, and installs `gOneCryptoProtocolGuid`.

### 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 AARCH64 mode, `OneCryptoImageProviderStandaloneMm` is an additional
MM module that registers an MM communication handler and serves OneCrypto PE32
bytes to DXE. It is intentionally separate from `OneCryptoLoaderStandaloneMm`:

1. Separation of concerns: transport/provider path vs protocol construction path.
2. Earlier/independent availability: provider can be dispatched without coupling
to MM loader behavior.
3. Better failure isolation and debugging for MM-communicate contract issues.

## 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.
directly.

On AARCH64 there are two valid designs:

1. Legacy dual-bin mode: include separate `OneCryptoBinDxe` in normal-world FV.
2. Single-copy AARCH64 mode: keep the single stored MM Bin and bridge
secure/non-secure separation using MM communication
(`OneCryptoImageProvider` and `OneCryptoLoaderDxeFromMm`).

## Module Summary

Expand All @@ -104,6 +138,15 @@ normal-world FV.
| `OneCryptoLoaderDxe` | `DXE_DRIVER` | ✓ | |
| `OneCryptoLoaderDxeByProtocol` | `DXE_DRIVER` | | ✓ |

Single-copy mode modules on AARCH64:

- `OneCryptoLoaderDxeFromMm` (`DXE_DRIVER`)
- `OneCryptoImageProviderStandaloneMm` (`MM_STANDALONE`)

This mode originated as the QAV-A prototype on QemuArmVirtPkg, but the design
is intended for real AARCH64 platforms and mainline integration. The current
feature branch implementation is the bring-up/prototype vehicle.

## Dependency Injection

The crypto Bin binary statically links BaseCryptLib, TlsLib, and OpenSSL, but
Expand Down
27 changes: 19 additions & 8 deletions OneCryptoPkg/Docs/FAQs.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ 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 QemuSbsaPkg 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?

Expand All @@ -20,9 +20,14 @@ On X64, the DXE Loader (`OneCryptoLoaderDxe`) calls `LoadImage()` on the
applied, then parses the PE/COFF exports to find and invoke the `CryptoEntry`
function.

On AARCH64 the approach is different — a dedicated `OneCryptoBinDxe`
(`DXE_DRIVER`) is used instead because the secure-world FV is not accessible
from DXE.
On AARCH64 there are now two approaches:

1. Legacy dual-bin mode: a dedicated `OneCryptoBinDxe` (`DXE_DRIVER`) is used
because the secure-world FV is not directly accessible from DXE.
2. Single-copy AARCH64 mode: DXE uses
`OneCryptoLoaderDxeFromMm` to fetch OneCrypto
PE32 bytes from `OneCryptoImageProviderStandaloneMm` over
`EFI_MM_COMMUNICATION2_PROTOCOL`, then calls `LoadImage()`.

See [Architecture.md](Architecture.md) for the full Bin + Loader pattern and
the differences between X64 and AARCH64.
Expand All @@ -42,7 +47,7 @@ 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:
Expand All @@ -59,4 +64,10 @@ strategies:
to find it. No `LoadImage()` or PE/COFF parsing is needed, and it uses
`NoSetupEntry` since constructors already ran during normal dispatch.

- **`OneCryptoLoaderDxeFromMm`** (AARCH64 single-copy mode) — DXE cannot read the
secure-world FV directly, so this loader requests OneCrypto image bytes from
StandaloneMM (`OneCryptoImageProviderStandaloneMm`) via
`EFI_MM_COMMUNICATION2_PROTOCOL`, then `LoadImage()`s the fetched PE32 and
resolves `CryptoEntry`.

See [Architecture.md](Architecture.md) for the full X64 vs AARCH64 breakdown.
2 changes: 2 additions & 0 deletions OneCryptoPkg/Docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,5 @@ To consume OneCryptoPkg in a platform:
- [Architecture.md](Architecture.md) — Bin + Loader pattern, X64 vs AARCH64,
dependency injection
- [FAQs.md](FAQs.md) — Common questions about OneCryptoPkg
- [QAV-A-Task-Notes.md](QAV-A-Task-Notes.md) — branch bring-up notes for
the single-copy AARCH64 mode
7 changes: 7 additions & 0 deletions OneCryptoPkg/DriverBuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,16 @@ def AddCommandLineOptions(self, parserObj):
help="skip OneCrypto packaging after build")
parserObj.add_argument("--version", dest="version", default="0.0.0", type=str,
help="The version of OneCryptoPkg to use for the SBOM. Default is 0.0.0")
parserObj.add_argument("--disable-tls", dest="disable_tls",
action="store_true", default=False,
help="Build OneCrypto without TLS (TlsLibNull + crypto-only OpenSSL) to slim the binary")

def RetrieveCommandLineOptions(self, args):
self.target = list(set(args.target)) if args.target else list(CommonPlatform.TargetsSupported)
self.arch = args.arch if args.arch else list(CommonPlatform.ArchSupported)
self.skip_packaging = args.skip_packaging
self.version = args.version
self.disable_tls = args.disable_tls

def GetWorkspaceRoot(self):
''' get WorkspacePath '''
Expand Down Expand Up @@ -212,6 +216,9 @@ def SetPlatformEnv(self):
self.env.SetValue("TARGET_ARCH", " ".join(self.arch), "CLI args")
self.env.SetValue("TOOL_CHAIN_TAG", "CLANGPDB", "Platform Hardcoded")

if self.disable_tls:
self.env.SetValue("BLD_*_ONE_CRYPTO_DISABLE_TLS", "TRUE", "CLI --disable-tls")

# Default turn on build reporting.
self.env.SetValue("BUILDREPORTING", "TRUE", "Enabling build report")
self.env.SetValue("BUILDREPORT_TYPES",
Expand Down
20 changes: 20 additions & 0 deletions OneCryptoPkg/Include/Guid/OneCryptoImageProviderGuid.h
Original file line number Diff line number Diff line change
@@ -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_
41 changes: 41 additions & 0 deletions OneCryptoPkg/Include/Private/OneCryptoImageProviderMessage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/** @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 2U
#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.
// LZMA_FV - Data is the LZMA-compressed payload of the GUIDed section that wraps
// the nested FV containing the OneCrypto PE32. Secure world (MM) lacks
// the memory budget to decompress the ~2 MB FV, so it hands the small
// compressed stream to the resource-rich normal world (DXE), which
// decompresses it and extracts the OneCrypto PE32.
//
#define ONE_CRYPTO_IMAGE_FORMAT_PE32 0U
#define ONE_CRYPTO_IMAGE_FORMAT_LZMA_FV 1U

typedef struct {
UINT32 Signature;
UINT32 Version;
UINT64 Offset;
UINT32 RequestedSize;
UINT32 ReturnedSize;
UINT32 TotalImageSize;
UINT32 Format;
EFI_GUID ImageGuid;
UINT8 Data[1];
} ONE_CRYPTO_IMAGE_PROVIDER_MSG;

#endif // ONE_CRYPTO_IMAGE_PROVIDER_MESSAGE_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## @file
# OneCrypto image provider in StandaloneMM for QAV fetch path.
#
# Copyright (C) Microsoft Corporation. All rights reserved.
# 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
Loading
Loading