blob: dced08650402b4679b0767be241e8240d9a38450 [file] [log] [blame]
Kathleen Capellacc594af2024-10-16 18:14:33 -04001#!/usr/bin/python3
2# Copyright (c) 2025, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5
6import struct
7
8EFI_HOB_HANDOFF_TABLE_VERSION = 0x000A
9
10PAGE_SIZE_SHIFT = 12 # TODO assuming 4K page size
11
12# HobType values of EFI_HOB_GENERIC_HEADER.
13
14EFI_HOB_TYPE_HANDOFF = 0x0001
15EFI_HOB_TYPE_MEMORY_ALLOCATION = 0x0002
16EFI_HOB_TYPE_RESOURCE_DESCRIPTOR = 0x0003
17EFI_HOB_TYPE_GUID_EXTENSION = 0x0004
18EFI_HOB_TYPE_FV = 0x0005
19EFI_HOB_TYPE_CPU = 0x0006
20EFI_HOB_TYPE_MEMORY_POOL = 0x0007
21EFI_HOB_TYPE_FV2 = 0x0009
22EFI_HOB_TYPE_LOAD_PEIM_UNUSED = 0x000A
23EFI_HOB_TYPE_UEFI_CAPSULE = 0x000B
24EFI_HOB_TYPE_FV3 = 0x000C
25EFI_HOB_TYPE_UNUSED = 0xFFFE
26EFI_HOB_TYPE_END_OF_HOB_LIST = 0xFFFF
27
28# GUID values
29"""struct efi_guid {
30 uint32_t time_low;
31 uint16_t time_mid;
32 uint16_t time_hi_and_version;
33 uint8_t clock_seq_and_node[8];
34}"""
35
36MM_PEI_MMRAM_MEMORY_RESERVE_GUID = (
37 0x0703F912,
38 0xBF8D,
39 0x4E2A,
40 (0xBE, 0x07, 0xAB, 0x27, 0x25, 0x25, 0xC5, 0x92),
41)
42MM_NS_BUFFER_GUID = (
43 0xF00497E3,
44 0xBFA2,
45 0x41A1,
46 (0x9D, 0x29, 0x54, 0xC2, 0xE9, 0x37, 0x21, 0xC5),
47)
48
49# MMRAM states and capabilities
50# See UEFI Platform Initialization Specification Version 1.8, IV-5.3.5
51EFI_MMRAM_OPEN = 0x00000001
52EFI_MMRAM_CLOSED = 0x00000002
53EFI_MMRAM_LOCKED = 0x00000004
54EFI_CACHEABLE = 0x00000008
55EFI_ALLOCATED = 0x00000010
56EFI_NEEDS_TESTING = 0x00000020
57EFI_NEEDS_ECC_INITIALIZATION = 0x00000040
58
59EFI_SMRAM_OPEN = EFI_MMRAM_OPEN
60EFI_SMRAM_CLOSED = EFI_MMRAM_CLOSED
61EFI_SMRAM_LOCKED = EFI_MMRAM_LOCKED
62
63# EFI boot mode.
64EFI_BOOT_WITH_FULL_CONFIGURATION = 0x00
65EFI_BOOT_WITH_MINIMAL_CONFIGURATION = 0x01
66EFI_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES = 0x02
67EFI_BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS = 0x03
68EFI_BOOT_WITH_DEFAULT_SETTINGS = 0x04
69EFI_BOOT_ON_S4_RESUME = 0x05
70EFI_BOOT_ON_S5_RESUME = 0x06
71EFI_BOOT_WITH_MFG_MODE_SETTINGS = 0x07
72EFI_BOOT_ON_S2_RESUME = 0x10
73EFI_BOOT_ON_S3_RESUME = 0x11
74EFI_BOOT_ON_FLASH_UPDATE = 0x12
75EFI_BOOT_IN_RECOVERY_MODE = 0x20
76
77STMM_BOOT_MODE = EFI_BOOT_WITH_FULL_CONFIGURATION
78STMM_MMRAM_REGION_STATE_DEFAULT = EFI_CACHEABLE | EFI_ALLOCATED
79STMM_MMRAM_REGION_STATE_HEAP = EFI_CACHEABLE
80
Kathleen Capella49c65662024-12-18 18:39:21 -050081"""`struct` python module allows user to specify endianness.
82We are expecting FVP or STMM platform as target and that they will be
83little-endian. See `struct` python module documentation if other endianness is
84needed."""
85ENDIANNESS = "<"
86
87
88def struct_pack_with_endianness(format_str, *args):
89 return struct.pack((ENDIANNESS + format_str), *args)
90
91
92def struct_calcsize_with_endianness(format_str):
93 return struct.calcsize(ENDIANNESS + format_str)
94
Kathleen Capellacc594af2024-10-16 18:14:33 -040095
96# Helper for fdt node property parsing
97def get_integer_property_value(fdt_node, name):
98 if fdt_node.exist_property(name):
99 p = fdt_node.get_property(name)
100
101 # <u32> Device Tree value
102 if len(p) == 1:
103 return p.value
104 # <u64> Device Tree value represented as two 32-bit values
105 if len(p) == 2:
106 msb = p[0]
107 lsb = p[1]
108 return lsb | (msb << 32)
109 return None
110
111
112class EfiGuid:
113 """Class representing EFI GUID (Globally Unique Identifier) as described by
114 the UEFI Specification v2.10"""
115
116 def __init__(self, time_low, time_mid, time_hi_and_version, clock_seq_and_node):
117 self.time_low = time_low
118 self.time_mid = time_mid
119 self.time_hi_and_version = time_hi_and_version
120 self.clock_seq_and_node = clock_seq_and_node
121 self.format_str = "IHH8B"
122
123 def pack(self):
Kathleen Capella49c65662024-12-18 18:39:21 -0500124 return struct_pack_with_endianness(
Kathleen Capellacc594af2024-10-16 18:14:33 -0400125 self.format_str,
126 self.time_low,
127 self.time_mid,
128 self.time_hi_and_version,
129 *self.clock_seq_and_node,
130 )
131
132 def __str__(self):
133 return f"{hex(self.time_low)}, {hex(self.time_mid)}, \
134 {hex(self.time_hi_and_version)}, {[hex(i) for i in self.clock_seq_and_node]}"
135
136
137class HobGenericHeader:
138 """Class representing the Hob Generic Header data type as described
139 in the UEFI Platform Initialization Specification version 1.8.
140
141 Each HOB is required to contain this header specifying the type and length
142 of the HOB.
143 """
144
145 def __init__(self, hob_type, hob_length):
146 self.format_str = "HHI"
147 self.hob_type = hob_type
Kathleen Capella49c65662024-12-18 18:39:21 -0500148 self.hob_length = struct_calcsize_with_endianness(self.format_str) + hob_length
Kathleen Capellacc594af2024-10-16 18:14:33 -0400149 self.reserved = 0
150
151 def pack(self):
Kathleen Capella49c65662024-12-18 18:39:21 -0500152 return struct_pack_with_endianness(
Kathleen Capellacc594af2024-10-16 18:14:33 -0400153 self.format_str, self.hob_type, self.hob_length, self.reserved
154 )
155
156 def __str__(self):
157 return f"Hob Type: {self.hob_type} Hob Length: {self.hob_length}"
158
159
160class HobGuid:
161 """Class representing the Guid Extension HOB as described in the UEFI
162 Platform Initialization Specification version 1.8.
163
164 Allows the production of HOBs whose types are not defined by the
165 specification by generating a GUID for the HOB entry."""
166
167 def __init__(self, name: EfiGuid, data_format_str, data):
Kathleen Capella49c65662024-12-18 18:39:21 -0500168 hob_length = struct_calcsize_with_endianness(
169 name.format_str
170 ) + struct_calcsize_with_endianness(data_format_str)
Kathleen Capellacc594af2024-10-16 18:14:33 -0400171 self.header = HobGenericHeader(EFI_HOB_TYPE_GUID_EXTENSION, hob_length)
172 self.name = name
173 self.data = data
174 self.data_format_str = data_format_str
175 self.format_str = (
176 self.header.format_str + self.name.format_str + data_format_str
177 )
178
179 def pack(self):
180 return (
181 self.header.pack()
182 + self.name.pack()
Kathleen Capella49c65662024-12-18 18:39:21 -0500183 + struct_pack_with_endianness(self.data_format_str, *self.data)
Kathleen Capellacc594af2024-10-16 18:14:33 -0400184 )
185
186 def __str__(self):
187 return f"Header: {self.header}\n Name: {self.name}\n Data: {self.data}"
188
189
190class HandoffInfoTable:
191 """Class representing the Handoff Info Table HOB (also known as PHIT HOB)
192 as described in the UEFI Platform Initialization Specification version 1.8.
193
194 Must be the first HOB in the HOB list. Contains general state
195 information.
196
197 For an SP, the range `memory_bottom` to `memory_top` will be the memory
198 range for the SP starting at the load address. `free_memory_bottom` to
199 `free_memory_top` indicates space where more HOB's could be added to the
200 HOB List."""
201
202 def __init__(self, memory_base, memory_size, free_memory_base, free_memory_size):
203 # header,uint32t,uint32t, uint64_t * 5
204 self.format_str = "II5Q"
Kathleen Capella49c65662024-12-18 18:39:21 -0500205 hob_length = struct_calcsize_with_endianness(self.format_str)
Kathleen Capellacc594af2024-10-16 18:14:33 -0400206 self.header = HobGenericHeader(EFI_HOB_TYPE_HANDOFF, hob_length)
207 self.version = EFI_HOB_HANDOFF_TABLE_VERSION
208 self.boot_mode = STMM_BOOT_MODE
209 self.memory_top = memory_base + memory_size
210 self.memory_bottom = memory_base
211 self.free_memory_top = free_memory_base + free_memory_size
212 self.free_memory_bottom = free_memory_base + self.header.hob_length
213 self.hob_end = None
214
215 def set_hob_end_addr(self, hob_end_addr):
216 self.hob_end = hob_end_addr
217
218 def set_free_memory_bottom_addr(self, addr):
219 self.free_memory_bottom = addr
220
221 def pack(self):
Kathleen Capella49c65662024-12-18 18:39:21 -0500222 return self.header.pack() + struct_pack_with_endianness(
Kathleen Capellacc594af2024-10-16 18:14:33 -0400223 self.format_str,
224 self.version,
225 self.boot_mode,
226 self.memory_top,
227 self.memory_bottom,
228 self.free_memory_top,
229 self.free_memory_bottom,
230 self.hob_end,
231 )
232
233
234class FirmwareVolumeHob:
235 """Class representing the Firmware Volume HOB type as described in the
236 UEFI Platform Initialization Specification version 1.8.
237
238 For an SP this will detail where the SP binary is located.
239 """
240
241 def __init__(self, base_address, img_offset, img_size):
242 # header, uint64_t, uint64_t
243 self.data_format_str = "2Q"
Kathleen Capella49c65662024-12-18 18:39:21 -0500244 hob_length = struct_calcsize_with_endianness(self.data_format_str)
Kathleen Capellacc594af2024-10-16 18:14:33 -0400245 self.header = HobGenericHeader(EFI_HOB_TYPE_FV, hob_length)
246 self.format_str = self.header.format_str + self.data_format_str
247 self.base_address = base_address + img_offset
248 self.length = img_size - img_offset
249
250 def pack(self):
Kathleen Capella49c65662024-12-18 18:39:21 -0500251 return self.header.pack() + struct_pack_with_endianness(
Kathleen Capellacc594af2024-10-16 18:14:33 -0400252 self.data_format_str, self.base_address, self.length
253 )
254
255
256class EndOfHobListHob:
257 """Class representing the End of HOB List HOB type as described in the
258 UEFI Platform Initialization Specification version 1.8.
259
260 Must be the last entry in a HOB list.
261 """
262
263 def __init__(self):
264 self.header = HobGenericHeader(EFI_HOB_TYPE_END_OF_HOB_LIST, 0)
265 self.format_str = ""
266
267 def pack(self):
268 return self.header.pack()
269
270
271class HobList:
272 """Class representing a HOB (Handoff Block list) based on the UEFI Platform
273 Initialization Sepcification version 1.8"""
274
275 def __init__(self, phit: HandoffInfoTable):
276 if phit is None:
277 raise Exception("HobList must be initialized with valid PHIT HOB")
278 final_hob = EndOfHobListHob()
279 phit.hob_end = phit.free_memory_bottom
280 phit.free_memory_bottom += final_hob.header.hob_length
281 self.hob_list = [phit, final_hob]
282
283 def add(self, hob):
284 if hob is not None:
285 if hob.header.hob_length > (
286 self.get_phit().free_memory_top - self.get_phit().free_memory_bottom
287 ):
288 raise MemoryError(
289 f"Cannot add HOB of length {hob.header.hob_length}. \
290 Resulting table size would exceed max table size of \
291 {self.max_size}. Current table size: {self.size}."
292 )
293 self.hob_list.insert(-1, hob)
294 self.get_phit().hob_end += hob.header.hob_length
295 self.get_phit().free_memory_bottom += hob.header.hob_length
296
297 def get_list(self):
298 return self.hob_list
299
300 def get_phit(self):
301 if self.hob_list is not None:
302 if type(self.hob_list[0]) is not HandoffInfoTable:
303 raise Exception("First hob in list must be of type PHIT")
304 return self.hob_list[0]
305
306
307def generate_mmram_desc(base_addr, page_count, granule, region_state):
308 physical_size = page_count << (PAGE_SIZE_SHIFT + (granule << 1))
309 physical_start = base_addr
310 cpu_start = base_addr
311
312 return ("4Q", (physical_start, cpu_start, physical_size, region_state))
313
314
Kathleen Capella35530872025-02-13 11:52:36 -0500315def generate_stmm_region_descriptor(base_addr, physical_size):
316 region_state = STMM_MMRAM_REGION_STATE_DEFAULT
317 physical_start = base_addr
318 cpu_start = base_addr
319 return ("4Q", (physical_start, cpu_start, physical_size, region_state))
320
321
Kathleen Capellacc594af2024-10-16 18:14:33 -0400322def generate_ns_buffer_guid(mmram_desc):
323 return HobGuid(EfiGuid(*MM_NS_BUFFER_GUID), *mmram_desc)
324
325
326def generate_pei_mmram_memory_reserve_guid(regions):
Kathleen Capella49c65662024-12-18 18:39:21 -0500327 # uint32t n_reserved regions, 4 bytes for padding so that array is aligned,
328 # array of mmram descriptors
329 format_str = "I4x"
Kathleen Capellacc594af2024-10-16 18:14:33 -0400330 data = [len(regions)]
331 for desc_format_str, mmram_desc in regions:
332 format_str += desc_format_str
333 data.extend(mmram_desc)
334 guid_data = (format_str, data)
335 return HobGuid(EfiGuid(*MM_PEI_MMRAM_MEMORY_RESERVE_GUID), *guid_data)
336
337
338def generate_hob_from_fdt_node(sp_fdt, hob_offset, hob_size=None):
339 """Create a HOB list binary from an SP FDT."""
340 fv_hob = None
341 ns_buffer_hob = None
342 mmram_reserve_hob = None
343 shared_buf_hob = None
344
345 load_address = get_integer_property_value(sp_fdt, "load-address")
346 img_size = get_integer_property_value(sp_fdt, "image-size")
347 entrypoint_offset = get_integer_property_value(sp_fdt, "entrypoint-offset")
348
349 if entrypoint_offset is None:
350 entrypoint_offset = 0x0
351 if hob_offset is None:
352 hob_offset = 0x0
353 if img_size is None:
354 img_size = 0x0
355
Kathleen Capella35530872025-02-13 11:52:36 -0500356 regions = []
357
358 # StMM requires the first memory region described in the
359 # MM_PEI_MMRAM_MEMORY_RESERVE_GUID describe the full partition layout.
360 regions.append(generate_stmm_region_descriptor(load_address, img_size))
361
Kathleen Capellacc594af2024-10-16 18:14:33 -0400362 if sp_fdt.exist_node("memory-regions"):
363 if sp_fdt.exist_property("xlat-granule"):
364 granule = int(sp_fdt.get_property("xlat-granule").value)
365 else:
366 # Default granule to 4K
367 granule = 0
368 memory_regions = sp_fdt.get_node("memory-regions")
Kathleen Capellacc594af2024-10-16 18:14:33 -0400369 for node in memory_regions.nodes:
370 base_addr = get_integer_property_value(node, "base-address")
371 page_count = get_integer_property_value(node, "pages-count")
372
373 if base_addr is None:
374 offset = get_integer_property_value(
375 node, "load-address-relative-offset"
376 )
377 if offset is None:
378 # Cannot create memory descriptor without base address, so skip
379 # node if base address cannot be defined
380 continue
381 else:
382 base_addr = load_address + offset
383
384 if node.name.strip() == "heap":
385 region_state = STMM_MMRAM_REGION_STATE_HEAP
386 else:
387 region_state = STMM_MMRAM_REGION_STATE_DEFAULT
388
389 mmram_desc = generate_mmram_desc(
390 base_addr, page_count, granule, region_state
391 )
392
393 if node.name.strip() == "ns_comm_buffer":
394 ns_buffer_hob = generate_ns_buffer_guid(mmram_desc)
395
396 regions.append(mmram_desc)
397
398 mmram_reserve_hob = generate_pei_mmram_memory_reserve_guid(regions)
399
400 fv_hob = FirmwareVolumeHob(load_address, entrypoint_offset, img_size)
401 hob_list_base = load_address + hob_offset
402
403 # TODO assuming default of 1 page allocated for HOB List
404 if hob_size is not None:
405 max_table_size = hob_size
406 else:
407 max_table_size = 1 << PAGE_SIZE_SHIFT
408 phit = HandoffInfoTable(
409 load_address, entrypoint_offset + img_size, hob_list_base, max_table_size
410 )
411
412 # Create a HobList containing only PHIT and EndofHobList HOBs.
413 hob_list = HobList(phit)
414
415 # Add HOBs to HOB list
416 if fv_hob is not None:
417 hob_list.add(fv_hob)
418 if ns_buffer_hob is not None:
419 hob_list.add(ns_buffer_hob)
420 if mmram_reserve_hob is not None:
421 hob_list.add(mmram_reserve_hob)
422 if shared_buf_hob is not None:
423 hob_list.add(shared_buf_hob)
424
425 return hob_list