blob: 372ec68a5f2d6b2ee71be9783392bcc87aeb679d [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
81
82# Helper for fdt node property parsing
83def get_integer_property_value(fdt_node, name):
84 if fdt_node.exist_property(name):
85 p = fdt_node.get_property(name)
86
87 # <u32> Device Tree value
88 if len(p) == 1:
89 return p.value
90 # <u64> Device Tree value represented as two 32-bit values
91 if len(p) == 2:
92 msb = p[0]
93 lsb = p[1]
94 return lsb | (msb << 32)
95 return None
96
97
98class EfiGuid:
99 """Class representing EFI GUID (Globally Unique Identifier) as described by
100 the UEFI Specification v2.10"""
101
102 def __init__(self, time_low, time_mid, time_hi_and_version, clock_seq_and_node):
103 self.time_low = time_low
104 self.time_mid = time_mid
105 self.time_hi_and_version = time_hi_and_version
106 self.clock_seq_and_node = clock_seq_and_node
107 self.format_str = "IHH8B"
108
109 def pack(self):
110 return struct.pack(
111 self.format_str,
112 self.time_low,
113 self.time_mid,
114 self.time_hi_and_version,
115 *self.clock_seq_and_node,
116 )
117
118 def __str__(self):
119 return f"{hex(self.time_low)}, {hex(self.time_mid)}, \
120 {hex(self.time_hi_and_version)}, {[hex(i) for i in self.clock_seq_and_node]}"
121
122
123class HobGenericHeader:
124 """Class representing the Hob Generic Header data type as described
125 in the UEFI Platform Initialization Specification version 1.8.
126
127 Each HOB is required to contain this header specifying the type and length
128 of the HOB.
129 """
130
131 def __init__(self, hob_type, hob_length):
132 self.format_str = "HHI"
133 self.hob_type = hob_type
134 self.hob_length = struct.calcsize(self.format_str) + hob_length
135 self.reserved = 0
136
137 def pack(self):
138 return struct.pack(
139 self.format_str, self.hob_type, self.hob_length, self.reserved
140 )
141
142 def __str__(self):
143 return f"Hob Type: {self.hob_type} Hob Length: {self.hob_length}"
144
145
146class HobGuid:
147 """Class representing the Guid Extension HOB as described in the UEFI
148 Platform Initialization Specification version 1.8.
149
150 Allows the production of HOBs whose types are not defined by the
151 specification by generating a GUID for the HOB entry."""
152
153 def __init__(self, name: EfiGuid, data_format_str, data):
154 hob_length = struct.calcsize(name.format_str) + struct.calcsize(data_format_str)
155 self.header = HobGenericHeader(EFI_HOB_TYPE_GUID_EXTENSION, hob_length)
156 self.name = name
157 self.data = data
158 self.data_format_str = data_format_str
159 self.format_str = (
160 self.header.format_str + self.name.format_str + data_format_str
161 )
162
163 def pack(self):
164 return (
165 self.header.pack()
166 + self.name.pack()
167 + struct.pack(self.data_format_str, *self.data)
168 )
169
170 def __str__(self):
171 return f"Header: {self.header}\n Name: {self.name}\n Data: {self.data}"
172
173
174class HandoffInfoTable:
175 """Class representing the Handoff Info Table HOB (also known as PHIT HOB)
176 as described in the UEFI Platform Initialization Specification version 1.8.
177
178 Must be the first HOB in the HOB list. Contains general state
179 information.
180
181 For an SP, the range `memory_bottom` to `memory_top` will be the memory
182 range for the SP starting at the load address. `free_memory_bottom` to
183 `free_memory_top` indicates space where more HOB's could be added to the
184 HOB List."""
185
186 def __init__(self, memory_base, memory_size, free_memory_base, free_memory_size):
187 # header,uint32t,uint32t, uint64_t * 5
188 self.format_str = "II5Q"
189 hob_length = struct.calcsize(self.format_str)
190 self.header = HobGenericHeader(EFI_HOB_TYPE_HANDOFF, hob_length)
191 self.version = EFI_HOB_HANDOFF_TABLE_VERSION
192 self.boot_mode = STMM_BOOT_MODE
193 self.memory_top = memory_base + memory_size
194 self.memory_bottom = memory_base
195 self.free_memory_top = free_memory_base + free_memory_size
196 self.free_memory_bottom = free_memory_base + self.header.hob_length
197 self.hob_end = None
198
199 def set_hob_end_addr(self, hob_end_addr):
200 self.hob_end = hob_end_addr
201
202 def set_free_memory_bottom_addr(self, addr):
203 self.free_memory_bottom = addr
204
205 def pack(self):
206 return self.header.pack() + struct.pack(
207 self.format_str,
208 self.version,
209 self.boot_mode,
210 self.memory_top,
211 self.memory_bottom,
212 self.free_memory_top,
213 self.free_memory_bottom,
214 self.hob_end,
215 )
216
217
218class FirmwareVolumeHob:
219 """Class representing the Firmware Volume HOB type as described in the
220 UEFI Platform Initialization Specification version 1.8.
221
222 For an SP this will detail where the SP binary is located.
223 """
224
225 def __init__(self, base_address, img_offset, img_size):
226 # header, uint64_t, uint64_t
227 self.data_format_str = "2Q"
228 hob_length = struct.calcsize(self.data_format_str)
229 self.header = HobGenericHeader(EFI_HOB_TYPE_FV, hob_length)
230 self.format_str = self.header.format_str + self.data_format_str
231 self.base_address = base_address + img_offset
232 self.length = img_size - img_offset
233
234 def pack(self):
235 return self.header.pack() + struct.pack(
236 self.data_format_str, self.base_address, self.length
237 )
238
239
240class EndOfHobListHob:
241 """Class representing the End of HOB List HOB type as described in the
242 UEFI Platform Initialization Specification version 1.8.
243
244 Must be the last entry in a HOB list.
245 """
246
247 def __init__(self):
248 self.header = HobGenericHeader(EFI_HOB_TYPE_END_OF_HOB_LIST, 0)
249 self.format_str = ""
250
251 def pack(self):
252 return self.header.pack()
253
254
255class HobList:
256 """Class representing a HOB (Handoff Block list) based on the UEFI Platform
257 Initialization Sepcification version 1.8"""
258
259 def __init__(self, phit: HandoffInfoTable):
260 if phit is None:
261 raise Exception("HobList must be initialized with valid PHIT HOB")
262 final_hob = EndOfHobListHob()
263 phit.hob_end = phit.free_memory_bottom
264 phit.free_memory_bottom += final_hob.header.hob_length
265 self.hob_list = [phit, final_hob]
266
267 def add(self, hob):
268 if hob is not None:
269 if hob.header.hob_length > (
270 self.get_phit().free_memory_top - self.get_phit().free_memory_bottom
271 ):
272 raise MemoryError(
273 f"Cannot add HOB of length {hob.header.hob_length}. \
274 Resulting table size would exceed max table size of \
275 {self.max_size}. Current table size: {self.size}."
276 )
277 self.hob_list.insert(-1, hob)
278 self.get_phit().hob_end += hob.header.hob_length
279 self.get_phit().free_memory_bottom += hob.header.hob_length
280
281 def get_list(self):
282 return self.hob_list
283
284 def get_phit(self):
285 if self.hob_list is not None:
286 if type(self.hob_list[0]) is not HandoffInfoTable:
287 raise Exception("First hob in list must be of type PHIT")
288 return self.hob_list[0]
289
290
291def generate_mmram_desc(base_addr, page_count, granule, region_state):
292 physical_size = page_count << (PAGE_SIZE_SHIFT + (granule << 1))
293 physical_start = base_addr
294 cpu_start = base_addr
295
296 return ("4Q", (physical_start, cpu_start, physical_size, region_state))
297
298
299def generate_ns_buffer_guid(mmram_desc):
300 return HobGuid(EfiGuid(*MM_NS_BUFFER_GUID), *mmram_desc)
301
302
303def generate_pei_mmram_memory_reserve_guid(regions):
304 # uint32t n_reserved regions, array of mmram descriptors
305 format_str = "I"
306 data = [len(regions)]
307 for desc_format_str, mmram_desc in regions:
308 format_str += desc_format_str
309 data.extend(mmram_desc)
310 guid_data = (format_str, data)
311 return HobGuid(EfiGuid(*MM_PEI_MMRAM_MEMORY_RESERVE_GUID), *guid_data)
312
313
314def generate_hob_from_fdt_node(sp_fdt, hob_offset, hob_size=None):
315 """Create a HOB list binary from an SP FDT."""
316 fv_hob = None
317 ns_buffer_hob = None
318 mmram_reserve_hob = None
319 shared_buf_hob = None
320
321 load_address = get_integer_property_value(sp_fdt, "load-address")
322 img_size = get_integer_property_value(sp_fdt, "image-size")
323 entrypoint_offset = get_integer_property_value(sp_fdt, "entrypoint-offset")
324
325 if entrypoint_offset is None:
326 entrypoint_offset = 0x0
327 if hob_offset is None:
328 hob_offset = 0x0
329 if img_size is None:
330 img_size = 0x0
331
332 if sp_fdt.exist_node("memory-regions"):
333 if sp_fdt.exist_property("xlat-granule"):
334 granule = int(sp_fdt.get_property("xlat-granule").value)
335 else:
336 # Default granule to 4K
337 granule = 0
338 memory_regions = sp_fdt.get_node("memory-regions")
339 regions = []
340 for node in memory_regions.nodes:
341 base_addr = get_integer_property_value(node, "base-address")
342 page_count = get_integer_property_value(node, "pages-count")
343
344 if base_addr is None:
345 offset = get_integer_property_value(
346 node, "load-address-relative-offset"
347 )
348 if offset is None:
349 # Cannot create memory descriptor without base address, so skip
350 # node if base address cannot be defined
351 continue
352 else:
353 base_addr = load_address + offset
354
355 if node.name.strip() == "heap":
356 region_state = STMM_MMRAM_REGION_STATE_HEAP
357 else:
358 region_state = STMM_MMRAM_REGION_STATE_DEFAULT
359
360 mmram_desc = generate_mmram_desc(
361 base_addr, page_count, granule, region_state
362 )
363
364 if node.name.strip() == "ns_comm_buffer":
365 ns_buffer_hob = generate_ns_buffer_guid(mmram_desc)
366
367 regions.append(mmram_desc)
368
369 mmram_reserve_hob = generate_pei_mmram_memory_reserve_guid(regions)
370
371 fv_hob = FirmwareVolumeHob(load_address, entrypoint_offset, img_size)
372 hob_list_base = load_address + hob_offset
373
374 # TODO assuming default of 1 page allocated for HOB List
375 if hob_size is not None:
376 max_table_size = hob_size
377 else:
378 max_table_size = 1 << PAGE_SIZE_SHIFT
379 phit = HandoffInfoTable(
380 load_address, entrypoint_offset + img_size, hob_list_base, max_table_size
381 )
382
383 # Create a HobList containing only PHIT and EndofHobList HOBs.
384 hob_list = HobList(phit)
385
386 # Add HOBs to HOB list
387 if fv_hob is not None:
388 hob_list.add(fv_hob)
389 if ns_buffer_hob is not None:
390 hob_list.add(ns_buffer_hob)
391 if mmram_reserve_hob is not None:
392 hob_list.add(mmram_reserve_hob)
393 if shared_buf_hob is not None:
394 hob_list.add(shared_buf_hob)
395
396 return hob_list