summaryrefslogtreecommitdiffstats
path: root/src/ukify
diff options
context:
space:
mode:
authoranonymix007 <48598263+anonymix007@users.noreply.github.com>2024-08-31 14:51:42 +0200
committeranonymix007 <48598263+anonymix007@users.noreply.github.com>2024-11-14 14:42:42 +0100
commit0333b9d5892983efc690d444fb8783624a1ef476 (patch)
treeb71fe42dcd054ff2e1991289c0701044d702e640 /src/ukify
parentukify: Support building UKIs with .dtbauto sections (diff)
downloadsystemd-0333b9d5892983efc690d444fb8783624a1ef476.tar.xz
systemd-0333b9d5892983efc690d444fb8783624a1ef476.zip
ukify: Support building UKIs with a .hwids section
This section contains a predefined set of HWIDs and the corresponding compatibles to be used in dtb matching
Diffstat (limited to 'src/ukify')
-rwxr-xr-xsrc/ukify/ukify.py127
1 files changed, 125 insertions, 2 deletions
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index 69904df6d2..5ea52898ee 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -240,6 +240,7 @@ class UkifyConfig:
devicetree: Path
devicetree_auto: list[Path]
efi_arch: str
+ hwids: Path
initrd: list[Path]
join_profiles: list[Path]
json: Union[Literal['pretty'], Literal['short'], Literal['off']]
@@ -367,6 +368,7 @@ DEFAULT_SECTIONS_TO_SHOW = {
'.splash': 'binary',
'.dtb': 'binary',
'.dtbauto': 'binary',
+ '.hwids': 'binary',
'.cmdline': 'text',
'.osrel': 'text',
'.uname': 'text',
@@ -622,8 +624,11 @@ def check_inputs(opts: UkifyConfig) -> None:
continue
if isinstance(value, Path):
- # Open file to check that we can read it, or generate an exception
- value.open().close()
+ # Check that we can open the directory or file, or generate and exception
+ if value.is_dir():
+ value.iterdir()
+ else:
+ value.open().close()
elif isinstance(value, list):
for item in value:
if isinstance(item, Path):
@@ -997,6 +1002,111 @@ def merge_sbat(input_pe: list[Path], input_text: list[str]) -> str:
)
+# Keep in sync with EFI_GUID (src/boot/efi/efi.h)
+# uint32_t Data1, uint16_t Data2, uint16_t Data3, uint8_t Data4[8]
+EFI_GUID = tuple[int, int, int, tuple[int, int, int, int, int, int, int, int]]
+EFI_GUID_STRUCT_SIZE = 4 + 2 + 2 + 1 * 8
+
+# Keep in sync with Device (src/boot/efi/chid.h)
+# uint32_t struct_size, uint32_t name_offset, uint32_t compatible_offset, EFI_GUID chid
+DEVICE_STRUCT_SIZE = 4 + 4 + 4 + EFI_GUID_STRUCT_SIZE
+NULL_DEVICE = b'\0' * DEVICE_STRUCT_SIZE
+
+
+def pack_device(offsets: dict[str, int], name: str, compatible: str, chids: list[EFI_GUID]) -> bytes:
+ data = b''
+
+ for data1, data2, data3, data4 in chids:
+ data += struct.pack(
+ '<IIIIHH8B', DEVICE_STRUCT_SIZE, offsets[name], offsets[compatible], data1, data2, data3, *data4
+ )
+
+ assert len(data) == DEVICE_STRUCT_SIZE * len(chids)
+ return data
+
+
+def hex_pairs_list(string: str) -> list[int]:
+ return [int(string[i : i + 2], 16) for i in range(0, len(string), 2)]
+
+
+def pack_strings(strings: set[str], base: int) -> tuple[bytes, dict[str, int]]:
+ blob = b''
+ offsets = {}
+
+ for string in sorted(strings):
+ offsets[string] = base + len(blob)
+ blob += string.encode('utf-8') + b'\00'
+
+ return (blob, offsets)
+
+
+def parse_hwid_dir(path: Path) -> bytes:
+ hwid_files = path.rglob('*.txt')
+
+ strings: set[str] = set()
+ devices: collections.defaultdict[tuple[str, str], list[EFI_GUID]] = collections.defaultdict(list)
+
+ uuid_regexp = re.compile(
+ r'\{[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}\}', re.I
+ )
+
+ for hwid_file in hwid_files:
+ content = hwid_file.open().readlines()
+
+ data: dict[str, str] = {
+ 'Manufacturer': '',
+ 'Family': '',
+ 'Compatible': '',
+ }
+ uuids: list[EFI_GUID] = []
+
+ for line in content:
+ for k in data:
+ if line.startswith(k):
+ data[k] = line.split(':')[1].strip()
+ break
+ else:
+ uuid = uuid_regexp.match(line)
+ if uuid is not None:
+ d1, d2, d3, d4, d5 = uuid.group(0)[1:-1].split('-')
+
+ data1 = int(d1, 16)
+ data2 = int(d2, 16)
+ data3 = int(d3, 16)
+ data4 = cast(
+ tuple[int, int, int, int, int, int, int, int],
+ tuple(hex_pairs_list(d4) + hex_pairs_list(d5)),
+ )
+
+ uuids.append((data1, data2, data3, data4))
+
+ for k, v in data.items():
+ if not v:
+ raise ValueError(f'hwid description file "{hwid_file}" does not contain "{k}"')
+
+ name = data['Manufacturer'] + ' ' + data['Family']
+ compatible = data['Compatible']
+
+ strings |= set([name, compatible])
+
+ # (compatible, name) pair uniquely identifies the device
+ devices[(compatible, name)] += uuids
+
+ total_device_structs = 1
+ for dev, uuids in devices.items():
+ total_device_structs += len(uuids)
+
+ strings_blob, offsets = pack_strings(strings, total_device_structs * DEVICE_STRUCT_SIZE)
+
+ devices_blob = b''
+ for (compatible, name), uuids in devices.items():
+ devices_blob += pack_device(offsets, name, compatible, uuids)
+
+ devices_blob += NULL_DEVICE
+
+ return devices_blob + strings_blob
+
+
STUB_SBAT = """\
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
uki,1,UKI,uki,1,https://uapi-group.org/specifications/specs/unified_kernel_image/
@@ -1063,12 +1173,18 @@ def make_uki(opts: UkifyConfig) -> None:
print('+', shell_join(cmd))
pcrpkey = subprocess.check_output(cmd)
+ hwids = None
+
+ if opts.hwids is not None:
+ hwids = parse_hwid_dir(opts.hwids)
+
sections = [
# name, content, measure?
('.osrel', opts.os_release, True),
('.cmdline', opts.cmdline, True),
('.dtb', opts.devicetree, True),
*(('.dtbauto', dtb, True) for dtb in opts.devicetree_auto),
+ ('.hwids', hwids, True),
('.uname', opts.uname, True),
('.splash', opts.splash, True),
('.pcrpkey', pcrpkey, True),
@@ -1639,6 +1755,13 @@ CONFIG_ITEMS = [
config_push=ConfigItem.config_list_prepend,
),
ConfigItem(
+ '--hwids',
+ metavar='DIR',
+ type=Path,
+ help='Directory with HWID text files [.hwids section]',
+ config_key='UKI/HWIDs',
+ ),
+ ConfigItem(
'--uname',
metavar='VERSION',
help='"uname -r" information [.uname section]',