blob: 9715f4015e70890cf6e41c1d9ebddf5dfbdd0e8e [file] [log] [blame]
Kathleen Capella87bd9a92024-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 Capelladbb42a22024-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 Capella87bd9a92024-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 Capelladbb42a22024-12-18 18:39:21 -0500124 return struct_pack_with_endianness(
Kathleen Capella87bd9a92024-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 Capelladbb42a22024-12-18 18:39:21 -0500148 self.hob_length = struct_calcsize_with_endianness(self.format_str) + hob_length
Kathleen Capella87bd9a92024-10-16 18:14:33 -0400149 self.reserved = 0
150
151 def pack(self):
Kathleen Capelladbb42a22024-12-18 18:39:21 -0500152 return struct_pack_with_endianness(
Kathleen Capella87bd9a92024-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 Capelladbb42a22024-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 Capella87bd9a92024-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 Capelladbb42a22024-12-18 18:39:21 -0500183 + struct_pack_with_endianness(self.data_format_str, *self.data)
Kathleen Capella87bd9a92024-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 Capelladbb42a22024-12-18 18:39:21 -0500205 hob_length = struct_calcsize_with_endianness(self.format_str)
Kathleen Capella87bd9a92024-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 Capelladbb42a22024-12-18 18:39:21 -0500222 return self.header.pack() + struct_pack_with_endianness(
Kathleen Capella87bd9a92024-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 Capelladbb42a22024-12-18 18:39:21 -0500244 hob_length = struct_calcsize_with_endianness(self.data_format_str)
Kathleen Capella87bd9a92024-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 Capelladbb42a22024-12-18 18:39:21 -0500251 return self.header.pack() + struct_pack_with_endianness(
Kathleen Capella87bd9a92024-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
315def generate_ns_buffer_guid(mmram_desc):
316 return HobGuid(EfiGuid(*MM_NS_BUFFER_GUID), *mmram_desc)
317
318
319def generate_pei_mmram_memory_reserve_guid(regions):
Kathleen Capelladbb42a22024-12-18 18:39:21 -0500320 # uint32t n_reserved regions, 4 bytes for padding so that array is aligned,
321 # array of mmram descriptors
322 format_str = "I4x"
Kathleen Capella87bd9a92024-10-16 18:14:33 -0400323 data = [len(regions)]
324 for desc_format_str, mmram_desc in regions:
325 format_str += desc_format_str
326 data.extend(mmram_desc)
327 guid_data = (format_str, data)
328 return HobGuid(EfiGuid(*MM_PEI_MMRAM_MEMORY_RESERVE_GUID), *guid_data)
329
330
331def generate_hob_from_fdt_node(sp_fdt, hob_offset, hob_size=None):
332 """Create a HOB list binary from an SP FDT."""
333 fv_hob = None
334 ns_buffer_hob = None
335 mmram_reserve_hob = None
336 shared_buf_hob = None
337
338 load_address = get_integer_property_value(sp_fdt, "load-address")
339 img_size = get_integer_property_value(sp_fdt, "image-size")
340 entrypoint_offset = get_integer_property_value(sp_fdt, "entrypoint-offset")
341
342 if entrypoint_offset is None:
343 entrypoint_offset = 0x0
344 if hob_offset is None:
345 hob_offset = 0x0
346 if img_size is None:
347 img_size = 0x0
348
349 if sp_fdt.exist_node("memory-regions"):
350 if sp_fdt.exist_property("xlat-granule"):
351 granule = int(sp_fdt.get_property("xlat-granule").value)
352 else:
353 # Default granule to 4K
354 granule = 0
355 memory_regions = sp_fdt.get_node("memory-regions")
356 regions = []
357 for node in memory_regions.nodes:
358 base_addr = get_integer_property_value(node, "base-address")
359 page_count = get_integer_property_value(node, "pages-count")
360
361 if base_addr is None:
362 offset = get_integer_property_value(
363 node, "load-address-relative-offset"
364 )
365 if offset is None:
366 # Cannot create memory descriptor without base address, so skip
367 # node if base address cannot be defined
368 continue
369 else:
370 base_addr = load_address + offset
371
372 if node.name.strip() == "heap":
373 region_state = STMM_MMRAM_REGION_STATE_HEAP
374 else:
375 region_state = STMM_MMRAM_REGION_STATE_DEFAULT
376
377 mmram_desc = generate_mmram_desc(
378 base_addr, page_count, granule, region_state
379 )
380
381 if node.name.strip() == "ns_comm_buffer":
382 ns_buffer_hob = generate_ns_buffer_guid(mmram_desc)
383
384 regions.append(mmram_desc)
385
386 mmram_reserve_hob = generate_pei_mmram_memory_reserve_guid(regions)
387
388 fv_hob = FirmwareVolumeHob(load_address, entrypoint_offset, img_size)
389 hob_list_base = load_address + hob_offset
390
391 # TODO assuming default of 1 page allocated for HOB List
392 if hob_size is not None:
393 max_table_size = hob_size
394 else:
395 max_table_size = 1 << PAGE_SIZE_SHIFT
396 phit = HandoffInfoTable(
397 load_address, entrypoint_offset + img_size, hob_list_base, max_table_size
398 )
399
400 # Create a HobList containing only PHIT and EndofHobList HOBs.
401 hob_list = HobList(phit)
402
403 # Add HOBs to HOB list
404 if fv_hob is not None:
405 hob_list.add(fv_hob)
406 if ns_buffer_hob is not None:
407 hob_list.add(ns_buffer_hob)
408 if mmram_reserve_hob is not None:
409 hob_list.add(mmram_reserve_hob)
410 if shared_buf_hob is not None:
411 hob_list.add(shared_buf_hob)
412
413 return hob_list