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.
  • 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.
TypeNameNote
UINT8TypeThe type of booting info structure.
UINT8VersionThe version of booting info structure.
UINT16SizeThe size of booting info structure.
UINT32Attr

The attributes of MM.

1: COVE MM payload

    EFI_PARAM_HEADER
  • CPU info structure provides the cpu information that assigned to EDK2 Standalone MM.
TypeNameNote
UINT32ProcessorId The unique processor ID.
UINT32PackageThe zezo-based physical package number.
UINT32CoreThe zezo-based physical core number.
UINT32Flags1: This CPU is primary core provided boot service protocol (BSP).

EFI_RISCV_MM_CPU_INFO

  • Booting info structure version 1
TypeNameNote
EFI_PARAM_HEADERHeader
UINT64MmMemBaseThe base address of memory assigned to MM.
UINT64MmMemLimitThe limit address of memory assigned to MM.
UINT64MmImageBaseThe bass address of MM FD.
UINT64MmStackBase

The stack base address. Note, MM will setup SP register with this value regardless of it may be setup before entering entry point.

UINT64MmHeapBaseThe heap base address that will be used to allocate HOB memory.
UINT64MmNsCommBufBase

The base address of shared buffer between MM and non-secure world.

It is set up as non-confidential memory.

UINT64MmSharedBufBase

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.

UINT64MmImageSizeThe size of MM FD.
UINT64MmPcpuStackSizeThe size of stack per CPU.
UINT64MmHeapSizeThe size of heap region.
UINT64MmNsCommBufSizeThe size of shared buffer between MM and non-secure world.
UINT64MmSharedBufSizeThe size of shared buffer between MM and secure world.
UINT64NumMmMemRegions

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.

UINT64NumCpus

The number of CPUs info that assigned to MM.

EFI_RISCV_MM_CPU_INFOCpuInfo[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_DESCRIPTIONEFI_MM_COMMUNICATE_HEADER

///
/// Structure describing a MMRAM region and its accessibility attributes.
///
typedef struct {
///
/// Designates the physical address of the MMRAM in memory. This view of memory is
/// the same as seen by I/O-based agents, for example, but it may not be the address seen
/// by the processors.
///
EFI_PHYSICAL_ADDRESS PhysicalStart;
///
/// Designates the address of the MMRAM, as seen by software executing on the
/// processors. This address may or may not match PhysicalStart.
///
EFI_PHYSICAL_ADDRESS CpuStart;
///
/// Describes the number of bytes in the MMRAM region.
///
UINT64 PhysicalSize;
///
/// Describes the accessibility attributes of the MMRAM. These attributes include the
/// hardware state (e.g., Open/Closed/Locked), capability (e.g., cacheable), logical
/// allocation (e.g., allocated), and pre-use initialization (e.g., needs testing/ECC
/// initialization).
///
UINT64 RegionState;
} EFI_MMRAM_DESCRIPTOR;

///
/// To avoid confusion in interpreting frames, the communication buffer should always
/// begin with EFI_MM_COMMUNICATE_HEADER
///
typedef struct {
///
/// Allows for disambiguation of the message format.
///
EFI_GUID HeaderGuid;
///
/// Describes the size of Data (in bytes) and does not include the size of the header.
///
UINTN MessageLength;
///
/// Designates an array of bytes that is MessageLength in size.
///
UINT8 Data[1];
} EFI_MM_COMMUNICATE_HEADE

  • 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.