EDK2_00_02_02 Standalone MM design (draft)
About
- This document discusses about EDK2 Standalone MM design for RISC-V. Before continuing this document, it is recommended to read this good document about how Standalone MM/Traditional MM designed: https://microsoft.github.io/mu/dyn/mu_feature_mm_supv/MmSupervisorPkg/Docs/StandaloneMmOverview/standalone_mm_framework/
- This document discussed how Standalone MM is implemented for RISC-V. As RISC-V specs is open source and vendors can choose to follow a ratified extension to create Standalone MM, the design of Standalone MM for RISC-V should be portable, minimum footprint.
- Currently, there are 2 ways to launch Standalone MM that has POC approved:
- Based on Salus (https://github.com/rivosinc/salus) following COVE specs (not yet ratified)
- Overview of Salus:
+---U-mode--+ +-----VS-mode-----+ +-VS-mode-+ | | | | | | | | | +---VU-mode---+ | | | | Salus | | | VMM(crosvm) | | | Guest | | Delegated | | +-------------+ | | | | Tasks | | | | | | | | Host(linux) | | | +-----------+ +-----------------+ +---------+ | | | TBD syscall SBI (COVH-API) SBI(COVG-API) | | | +-------------HS-mode-----------------------+ | Salus | +-------------------------------------------+ | SBI | +----------M-mode---------------------------+ | Firmware(OpenSBI) | +-------------------------------------------+
- EDK2 run in VS-mode (Host) then use COVH-API that talks to Salus to create guest TVM in VS-mode.
- Confidential memory created which converted from EDK2 non-confidential memory. The memory can only be used in Guest.
- The Standalone MM FD binary is measured and copy to guest memory.
- The Standalone MM entry point is called then start initialization.
- After Standalone MM finishes booting, EDK2 takes the control and continuing booting.
- When a request needs to send to Standalone MM, EDK2 copies request content (MM Protocol) to a shared non confidential memory then uses COVH-API to trigger Standalone MM run and serve the request.
- Based on Penglai (https://github.com/Penglai-Enclave/Penglai-Enclave-sPMP) following sPMP extension (not yet ratified)
- Overview of Penglai
- Both EDK2 and StandaloneMm are initiated by the Penglai Secure Monitor and running in S-mode Encalve
- Use sPMP + PMP to ensure memory isolation between S-mode Enclave
- Use different interrupt isolation mechanisms for different interrupt controllers to ensure that interrupts are only visible to the specified Enclave
- Configure the IOPMP to block arbitrary device access to the secure memory.
- When a request needs to send to Standalone MM, EDK2 copies request content (MM Protocol) to a shared non confidential memory then uses secure communication channel to trigger Standalone MM run and serve the request.
- Based on Salus (https://github.com/rivosinc/salus) following COVE specs (not yet ratified)
- No matter Standalone MM is launched, it should maintain the following requirements:
- Standalone MM resources are protected completely from outside including MMIO, confidential memory.
- Standalone MM can be loaded from any modes such as M-mode, S-Mode, VS-Mode with Hypervisor. U-mode is not suitable as it is limited accessing resource.
- Standalone MM can be loaded from EDK2 PEI or DXE phase in Salus case and lives through the life cycle of current boot.
- This document is trying to standardize the following subjects:
- Parameters of entry point of EDK2 Standalone MM.
- Booting information passed from launcher entity to EDK2 Standalone MM.
- Communication protocols between EDK2 Standalone MM and EDK2
- How resource in EDK2 Standalone MM is protected based on the design of entity that loads EDK2 Standalone MM:
- In Salus, EDK2 Standalone MM is loaded as TVM in VS mode. The protection based on COVE specs.
- In Penglai, EDK2 Standalone MM is run as enclave TVM that protected by PMP/sPMP.
Parameters of entry point of EDK2 Standalone MM
The entry point should take the following parameters:
- a0:
- Current CPU ID.
- This can be physical or logical ID (in Salus case)
- a1:
- The pointer to booting information structure.
Booting information structure
The following booting are copied from ARM definition. There is always a specific requirement for RISC-V so the structure can't be the same between ARM and RISC-V.
The code should have separated header file for RISC-V
- Param header structure to determine the format of booting info structure.
Type | Name | Note |
---|---|---|
UINT8 | Type | The type of booting info structure. |
UINT8 | Version | The version of booting info structure. |
UINT16 | Size | The size of booting info structure. |
UINT32 | Attr | The attributes of MM. 1: COVE MM payload |
- EFI_PARAM_HEADER
- CPU info structure provides the cpu information that assigned to EDK2 Standalone MM.
Type | Name | Note |
---|---|---|
UINT32 | ProcessorId | The unique processor ID. |
UINT32 | Package | The zezo-based physical package number. |
UINT32 | Core | The zezo-based physical core number. |
UINT32 | Flags | 1: This CPU is primary core provided boot service protocol (BSP). |
EFI_RISCV_MM_CPU_INFO
- Booting info structure version 1
Type | Name | Note |
---|---|---|
EFI_PARAM_HEADER | Header | |
UINT64 | MmMemBase | The base address of memory assigned to MM. |
UINT64 | MmMemLimit | The limit address of memory assigned to MM. |
UINT64 | MmImageBase | The bass address of MM FD. |
UINT64 | MmStackBase | The stack base address. Note, MM will setup SP register with this value regardless of it may be setup before entering entry point. |
UINT64 | MmHeapBase | The heap base address that will be used to allocate HOB memory. |
UINT64 | MmNsCommBufBase | The base address of shared buffer between MM and non-secure world. It is set up as non-confidential memory. |
UINT64 | MmSharedBufBase | The base address of shared buffer between MM and secure world. It is set up as confidential memory. But it should not be trusted by MM. |
UINT64 | MmImageSize | The size of MM FD. |
UINT64 | MmPcpuStackSize | The size of stack per CPU. |
UINT64 | MmHeapSize | The size of heap region. |
UINT64 | MmNsCommBufSize | The size of shared buffer between MM and non-secure world. |
UINT64 | MmSharedBufSize | The size of shared buffer between MM and secure world. |
UINT64 | NumMmMemRegions | The number of memory regions that be registered with MM core. Minimum are 6 core regions: MemBase, ImageBase, StackBase, HeapBase, NsCommBufBase, SharedBufBase. Additional region can be added at the end of structure for a driver. |
UINT64 | NumCpus | The number of CPUs info that assigned to MM. |
EFI_RISCV_MM_CPU_INFO | CpuInfo[NumCpus] | The array of CPUs info. |
EFI_RISCV_MM_BOOT_INFO
Communication between EDK2 Standalone MM and EDK2
Information will be communicated using MmNsCommBufBase and MmNsCommBufSize that passed from booting info.
The format of MmNsCommBufBase:
EFI_MMRAM_DESCRIPTION |
---|
EFI_MM_COMMUNICATE_HEADER |
PAYLOAD |
Both EFI_MMRAM_DESCRIPTION and EFI_MM_COMMUNICATE_HEADER are standard EDK2 communicate protocol as described:
EFI_MMRAM_DESCRIPTION | EFI_MM_COMMUNICATE_HEADER |
---|---|
/// | /// |
- The PhysicalStart and CPUStart have the same value of MmNsCommBufBase.
- The PhysicalSize have the same value of MmNsCommBufSize.
- The MessageLength describes the length of payload size.
Pseudo code of delegated event loop.
Make sure MessageLength is zero
While true
- Cpu sleep (in Salus) or SBI request (in Penglai)
- Check MessageLength non zero then process message
- Before wake-up CPU in MM context, EDK2 makes sure MessageLength and Data are updated with new request.
- How to wake CPU in MM context is dependent how EDK2 MM is loaded as not be discussed in this document.