mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-08 05:43:53 +00:00

StandaloneMM partition requires that the first memory region in its list of reserved memory regions describe the full partition layout. Hafnium SPMC checks that memory regions in FF-A manifest do not overlap. Therefore, this region is added directly in HOB generation code rather than as a memory region in the FF-A manifest for the StMM partition. Signed-off-by: Kathleen Capella <kathleen.capella@arm.com> Change-Id: Ia22174d755a5776e20ecf9639584f3c08cf9e60e
425 lines
14 KiB
Python
425 lines
14 KiB
Python
#!/usr/bin/python3
|
|
# Copyright (c) 2025, Arm Limited. All rights reserved.
|
|
#
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
import struct
|
|
|
|
EFI_HOB_HANDOFF_TABLE_VERSION = 0x000A
|
|
|
|
PAGE_SIZE_SHIFT = 12 # TODO assuming 4K page size
|
|
|
|
# HobType values of EFI_HOB_GENERIC_HEADER.
|
|
|
|
EFI_HOB_TYPE_HANDOFF = 0x0001
|
|
EFI_HOB_TYPE_MEMORY_ALLOCATION = 0x0002
|
|
EFI_HOB_TYPE_RESOURCE_DESCRIPTOR = 0x0003
|
|
EFI_HOB_TYPE_GUID_EXTENSION = 0x0004
|
|
EFI_HOB_TYPE_FV = 0x0005
|
|
EFI_HOB_TYPE_CPU = 0x0006
|
|
EFI_HOB_TYPE_MEMORY_POOL = 0x0007
|
|
EFI_HOB_TYPE_FV2 = 0x0009
|
|
EFI_HOB_TYPE_LOAD_PEIM_UNUSED = 0x000A
|
|
EFI_HOB_TYPE_UEFI_CAPSULE = 0x000B
|
|
EFI_HOB_TYPE_FV3 = 0x000C
|
|
EFI_HOB_TYPE_UNUSED = 0xFFFE
|
|
EFI_HOB_TYPE_END_OF_HOB_LIST = 0xFFFF
|
|
|
|
# GUID values
|
|
"""struct efi_guid {
|
|
uint32_t time_low;
|
|
uint16_t time_mid;
|
|
uint16_t time_hi_and_version;
|
|
uint8_t clock_seq_and_node[8];
|
|
}"""
|
|
|
|
MM_PEI_MMRAM_MEMORY_RESERVE_GUID = (
|
|
0x0703F912,
|
|
0xBF8D,
|
|
0x4E2A,
|
|
(0xBE, 0x07, 0xAB, 0x27, 0x25, 0x25, 0xC5, 0x92),
|
|
)
|
|
MM_NS_BUFFER_GUID = (
|
|
0xF00497E3,
|
|
0xBFA2,
|
|
0x41A1,
|
|
(0x9D, 0x29, 0x54, 0xC2, 0xE9, 0x37, 0x21, 0xC5),
|
|
)
|
|
|
|
# MMRAM states and capabilities
|
|
# See UEFI Platform Initialization Specification Version 1.8, IV-5.3.5
|
|
EFI_MMRAM_OPEN = 0x00000001
|
|
EFI_MMRAM_CLOSED = 0x00000002
|
|
EFI_MMRAM_LOCKED = 0x00000004
|
|
EFI_CACHEABLE = 0x00000008
|
|
EFI_ALLOCATED = 0x00000010
|
|
EFI_NEEDS_TESTING = 0x00000020
|
|
EFI_NEEDS_ECC_INITIALIZATION = 0x00000040
|
|
|
|
EFI_SMRAM_OPEN = EFI_MMRAM_OPEN
|
|
EFI_SMRAM_CLOSED = EFI_MMRAM_CLOSED
|
|
EFI_SMRAM_LOCKED = EFI_MMRAM_LOCKED
|
|
|
|
# EFI boot mode.
|
|
EFI_BOOT_WITH_FULL_CONFIGURATION = 0x00
|
|
EFI_BOOT_WITH_MINIMAL_CONFIGURATION = 0x01
|
|
EFI_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES = 0x02
|
|
EFI_BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS = 0x03
|
|
EFI_BOOT_WITH_DEFAULT_SETTINGS = 0x04
|
|
EFI_BOOT_ON_S4_RESUME = 0x05
|
|
EFI_BOOT_ON_S5_RESUME = 0x06
|
|
EFI_BOOT_WITH_MFG_MODE_SETTINGS = 0x07
|
|
EFI_BOOT_ON_S2_RESUME = 0x10
|
|
EFI_BOOT_ON_S3_RESUME = 0x11
|
|
EFI_BOOT_ON_FLASH_UPDATE = 0x12
|
|
EFI_BOOT_IN_RECOVERY_MODE = 0x20
|
|
|
|
STMM_BOOT_MODE = EFI_BOOT_WITH_FULL_CONFIGURATION
|
|
STMM_MMRAM_REGION_STATE_DEFAULT = EFI_CACHEABLE | EFI_ALLOCATED
|
|
STMM_MMRAM_REGION_STATE_HEAP = EFI_CACHEABLE
|
|
|
|
"""`struct` python module allows user to specify endianness.
|
|
We are expecting FVP or STMM platform as target and that they will be
|
|
little-endian. See `struct` python module documentation if other endianness is
|
|
needed."""
|
|
ENDIANNESS = "<"
|
|
|
|
|
|
def struct_pack_with_endianness(format_str, *args):
|
|
return struct.pack((ENDIANNESS + format_str), *args)
|
|
|
|
|
|
def struct_calcsize_with_endianness(format_str):
|
|
return struct.calcsize(ENDIANNESS + format_str)
|
|
|
|
|
|
# Helper for fdt node property parsing
|
|
def get_integer_property_value(fdt_node, name):
|
|
if fdt_node.exist_property(name):
|
|
p = fdt_node.get_property(name)
|
|
|
|
# <u32> Device Tree value
|
|
if len(p) == 1:
|
|
return p.value
|
|
# <u64> Device Tree value represented as two 32-bit values
|
|
if len(p) == 2:
|
|
msb = p[0]
|
|
lsb = p[1]
|
|
return lsb | (msb << 32)
|
|
return None
|
|
|
|
|
|
class EfiGuid:
|
|
"""Class representing EFI GUID (Globally Unique Identifier) as described by
|
|
the UEFI Specification v2.10"""
|
|
|
|
def __init__(self, time_low, time_mid, time_hi_and_version, clock_seq_and_node):
|
|
self.time_low = time_low
|
|
self.time_mid = time_mid
|
|
self.time_hi_and_version = time_hi_and_version
|
|
self.clock_seq_and_node = clock_seq_and_node
|
|
self.format_str = "IHH8B"
|
|
|
|
def pack(self):
|
|
return struct_pack_with_endianness(
|
|
self.format_str,
|
|
self.time_low,
|
|
self.time_mid,
|
|
self.time_hi_and_version,
|
|
*self.clock_seq_and_node,
|
|
)
|
|
|
|
def __str__(self):
|
|
return f"{hex(self.time_low)}, {hex(self.time_mid)}, \
|
|
{hex(self.time_hi_and_version)}, {[hex(i) for i in self.clock_seq_and_node]}"
|
|
|
|
|
|
class HobGenericHeader:
|
|
"""Class representing the Hob Generic Header data type as described
|
|
in the UEFI Platform Initialization Specification version 1.8.
|
|
|
|
Each HOB is required to contain this header specifying the type and length
|
|
of the HOB.
|
|
"""
|
|
|
|
def __init__(self, hob_type, hob_length):
|
|
self.format_str = "HHI"
|
|
self.hob_type = hob_type
|
|
self.hob_length = struct_calcsize_with_endianness(self.format_str) + hob_length
|
|
self.reserved = 0
|
|
|
|
def pack(self):
|
|
return struct_pack_with_endianness(
|
|
self.format_str, self.hob_type, self.hob_length, self.reserved
|
|
)
|
|
|
|
def __str__(self):
|
|
return f"Hob Type: {self.hob_type} Hob Length: {self.hob_length}"
|
|
|
|
|
|
class HobGuid:
|
|
"""Class representing the Guid Extension HOB as described in the UEFI
|
|
Platform Initialization Specification version 1.8.
|
|
|
|
Allows the production of HOBs whose types are not defined by the
|
|
specification by generating a GUID for the HOB entry."""
|
|
|
|
def __init__(self, name: EfiGuid, data_format_str, data):
|
|
hob_length = struct_calcsize_with_endianness(
|
|
name.format_str
|
|
) + struct_calcsize_with_endianness(data_format_str)
|
|
self.header = HobGenericHeader(EFI_HOB_TYPE_GUID_EXTENSION, hob_length)
|
|
self.name = name
|
|
self.data = data
|
|
self.data_format_str = data_format_str
|
|
self.format_str = (
|
|
self.header.format_str + self.name.format_str + data_format_str
|
|
)
|
|
|
|
def pack(self):
|
|
return (
|
|
self.header.pack()
|
|
+ self.name.pack()
|
|
+ struct_pack_with_endianness(self.data_format_str, *self.data)
|
|
)
|
|
|
|
def __str__(self):
|
|
return f"Header: {self.header}\n Name: {self.name}\n Data: {self.data}"
|
|
|
|
|
|
class HandoffInfoTable:
|
|
"""Class representing the Handoff Info Table HOB (also known as PHIT HOB)
|
|
as described in the UEFI Platform Initialization Specification version 1.8.
|
|
|
|
Must be the first HOB in the HOB list. Contains general state
|
|
information.
|
|
|
|
For an SP, the range `memory_bottom` to `memory_top` will be the memory
|
|
range for the SP starting at the load address. `free_memory_bottom` to
|
|
`free_memory_top` indicates space where more HOB's could be added to the
|
|
HOB List."""
|
|
|
|
def __init__(self, memory_base, memory_size, free_memory_base, free_memory_size):
|
|
# header,uint32t,uint32t, uint64_t * 5
|
|
self.format_str = "II5Q"
|
|
hob_length = struct_calcsize_with_endianness(self.format_str)
|
|
self.header = HobGenericHeader(EFI_HOB_TYPE_HANDOFF, hob_length)
|
|
self.version = EFI_HOB_HANDOFF_TABLE_VERSION
|
|
self.boot_mode = STMM_BOOT_MODE
|
|
self.memory_top = memory_base + memory_size
|
|
self.memory_bottom = memory_base
|
|
self.free_memory_top = free_memory_base + free_memory_size
|
|
self.free_memory_bottom = free_memory_base + self.header.hob_length
|
|
self.hob_end = None
|
|
|
|
def set_hob_end_addr(self, hob_end_addr):
|
|
self.hob_end = hob_end_addr
|
|
|
|
def set_free_memory_bottom_addr(self, addr):
|
|
self.free_memory_bottom = addr
|
|
|
|
def pack(self):
|
|
return self.header.pack() + struct_pack_with_endianness(
|
|
self.format_str,
|
|
self.version,
|
|
self.boot_mode,
|
|
self.memory_top,
|
|
self.memory_bottom,
|
|
self.free_memory_top,
|
|
self.free_memory_bottom,
|
|
self.hob_end,
|
|
)
|
|
|
|
|
|
class FirmwareVolumeHob:
|
|
"""Class representing the Firmware Volume HOB type as described in the
|
|
UEFI Platform Initialization Specification version 1.8.
|
|
|
|
For an SP this will detail where the SP binary is located.
|
|
"""
|
|
|
|
def __init__(self, base_address, img_offset, img_size):
|
|
# header, uint64_t, uint64_t
|
|
self.data_format_str = "2Q"
|
|
hob_length = struct_calcsize_with_endianness(self.data_format_str)
|
|
self.header = HobGenericHeader(EFI_HOB_TYPE_FV, hob_length)
|
|
self.format_str = self.header.format_str + self.data_format_str
|
|
self.base_address = base_address + img_offset
|
|
self.length = img_size - img_offset
|
|
|
|
def pack(self):
|
|
return self.header.pack() + struct_pack_with_endianness(
|
|
self.data_format_str, self.base_address, self.length
|
|
)
|
|
|
|
|
|
class EndOfHobListHob:
|
|
"""Class representing the End of HOB List HOB type as described in the
|
|
UEFI Platform Initialization Specification version 1.8.
|
|
|
|
Must be the last entry in a HOB list.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.header = HobGenericHeader(EFI_HOB_TYPE_END_OF_HOB_LIST, 0)
|
|
self.format_str = ""
|
|
|
|
def pack(self):
|
|
return self.header.pack()
|
|
|
|
|
|
class HobList:
|
|
"""Class representing a HOB (Handoff Block list) based on the UEFI Platform
|
|
Initialization Sepcification version 1.8"""
|
|
|
|
def __init__(self, phit: HandoffInfoTable):
|
|
if phit is None:
|
|
raise Exception("HobList must be initialized with valid PHIT HOB")
|
|
final_hob = EndOfHobListHob()
|
|
phit.hob_end = phit.free_memory_bottom
|
|
phit.free_memory_bottom += final_hob.header.hob_length
|
|
self.hob_list = [phit, final_hob]
|
|
|
|
def add(self, hob):
|
|
if hob is not None:
|
|
if hob.header.hob_length > (
|
|
self.get_phit().free_memory_top - self.get_phit().free_memory_bottom
|
|
):
|
|
raise MemoryError(
|
|
f"Cannot add HOB of length {hob.header.hob_length}. \
|
|
Resulting table size would exceed max table size of \
|
|
{self.max_size}. Current table size: {self.size}."
|
|
)
|
|
self.hob_list.insert(-1, hob)
|
|
self.get_phit().hob_end += hob.header.hob_length
|
|
self.get_phit().free_memory_bottom += hob.header.hob_length
|
|
|
|
def get_list(self):
|
|
return self.hob_list
|
|
|
|
def get_phit(self):
|
|
if self.hob_list is not None:
|
|
if type(self.hob_list[0]) is not HandoffInfoTable:
|
|
raise Exception("First hob in list must be of type PHIT")
|
|
return self.hob_list[0]
|
|
|
|
|
|
def generate_mmram_desc(base_addr, page_count, granule, region_state):
|
|
physical_size = page_count << (PAGE_SIZE_SHIFT + (granule << 1))
|
|
physical_start = base_addr
|
|
cpu_start = base_addr
|
|
|
|
return ("4Q", (physical_start, cpu_start, physical_size, region_state))
|
|
|
|
|
|
def generate_stmm_region_descriptor(base_addr, physical_size):
|
|
region_state = STMM_MMRAM_REGION_STATE_DEFAULT
|
|
physical_start = base_addr
|
|
cpu_start = base_addr
|
|
return ("4Q", (physical_start, cpu_start, physical_size, region_state))
|
|
|
|
|
|
def generate_ns_buffer_guid(mmram_desc):
|
|
return HobGuid(EfiGuid(*MM_NS_BUFFER_GUID), *mmram_desc)
|
|
|
|
|
|
def generate_pei_mmram_memory_reserve_guid(regions):
|
|
# uint32t n_reserved regions, 4 bytes for padding so that array is aligned,
|
|
# array of mmram descriptors
|
|
format_str = "I4x"
|
|
data = [len(regions)]
|
|
for desc_format_str, mmram_desc in regions:
|
|
format_str += desc_format_str
|
|
data.extend(mmram_desc)
|
|
guid_data = (format_str, data)
|
|
return HobGuid(EfiGuid(*MM_PEI_MMRAM_MEMORY_RESERVE_GUID), *guid_data)
|
|
|
|
|
|
def generate_hob_from_fdt_node(sp_fdt, hob_offset, hob_size=None):
|
|
"""Create a HOB list binary from an SP FDT."""
|
|
fv_hob = None
|
|
ns_buffer_hob = None
|
|
mmram_reserve_hob = None
|
|
shared_buf_hob = None
|
|
|
|
load_address = get_integer_property_value(sp_fdt, "load-address")
|
|
img_size = get_integer_property_value(sp_fdt, "image-size")
|
|
entrypoint_offset = get_integer_property_value(sp_fdt, "entrypoint-offset")
|
|
|
|
if entrypoint_offset is None:
|
|
entrypoint_offset = 0x0
|
|
if hob_offset is None:
|
|
hob_offset = 0x0
|
|
if img_size is None:
|
|
img_size = 0x0
|
|
|
|
regions = []
|
|
|
|
# StMM requires the first memory region described in the
|
|
# MM_PEI_MMRAM_MEMORY_RESERVE_GUID describe the full partition layout.
|
|
regions.append(generate_stmm_region_descriptor(load_address, img_size))
|
|
|
|
if sp_fdt.exist_node("memory-regions"):
|
|
if sp_fdt.exist_property("xlat-granule"):
|
|
granule = int(sp_fdt.get_property("xlat-granule").value)
|
|
else:
|
|
# Default granule to 4K
|
|
granule = 0
|
|
memory_regions = sp_fdt.get_node("memory-regions")
|
|
for node in memory_regions.nodes:
|
|
base_addr = get_integer_property_value(node, "base-address")
|
|
page_count = get_integer_property_value(node, "pages-count")
|
|
|
|
if base_addr is None:
|
|
offset = get_integer_property_value(
|
|
node, "load-address-relative-offset"
|
|
)
|
|
if offset is None:
|
|
# Cannot create memory descriptor without base address, so skip
|
|
# node if base address cannot be defined
|
|
continue
|
|
else:
|
|
base_addr = load_address + offset
|
|
|
|
if node.name.strip() == "heap":
|
|
region_state = STMM_MMRAM_REGION_STATE_HEAP
|
|
else:
|
|
region_state = STMM_MMRAM_REGION_STATE_DEFAULT
|
|
|
|
mmram_desc = generate_mmram_desc(
|
|
base_addr, page_count, granule, region_state
|
|
)
|
|
|
|
if node.name.strip() == "ns_comm_buffer":
|
|
ns_buffer_hob = generate_ns_buffer_guid(mmram_desc)
|
|
|
|
regions.append(mmram_desc)
|
|
|
|
mmram_reserve_hob = generate_pei_mmram_memory_reserve_guid(regions)
|
|
|
|
fv_hob = FirmwareVolumeHob(load_address, entrypoint_offset, img_size)
|
|
hob_list_base = load_address + hob_offset
|
|
|
|
# TODO assuming default of 1 page allocated for HOB List
|
|
if hob_size is not None:
|
|
max_table_size = hob_size
|
|
else:
|
|
max_table_size = 1 << PAGE_SIZE_SHIFT
|
|
phit = HandoffInfoTable(
|
|
load_address, entrypoint_offset + img_size, hob_list_base, max_table_size
|
|
)
|
|
|
|
# Create a HobList containing only PHIT and EndofHobList HOBs.
|
|
hob_list = HobList(phit)
|
|
|
|
# Add HOBs to HOB list
|
|
if fv_hob is not None:
|
|
hob_list.add(fv_hob)
|
|
if ns_buffer_hob is not None:
|
|
hob_list.add(ns_buffer_hob)
|
|
if mmram_reserve_hob is not None:
|
|
hob_list.add(mmram_reserve_hob)
|
|
if shared_buf_hob is not None:
|
|
hob_list.add(shared_buf_hob)
|
|
|
|
return hob_list
|