Compare commits

...

3 Commits

Author SHA1 Message Date
anonymix007 5115465001
Merge 098e44d03c into f29a07f3fc 2024-11-26 16:29:35 +00:00
anonymix007 098e44d03c man: Document ukify --hwids= and --devicetree-auto= options 2024-11-22 13:31:19 +03:00
anonymix007 cf331f1c9b ukify: Switch to JSON HWID description format 2024-11-22 00:42:47 +03:00
3 changed files with 58 additions and 65 deletions

View File

@ -71,6 +71,8 @@
<varname>Cmdline=</varname>/<option>--cmdline=</option>, <varname>Cmdline=</varname>/<option>--cmdline=</option>,
<varname>OSRelease=</varname>/<option>--os-release=</option>, <varname>OSRelease=</varname>/<option>--os-release=</option>,
<varname>DeviceTree=</varname>/<option>--devicetree=</option>, <varname>DeviceTree=</varname>/<option>--devicetree=</option>,
<varname>DeviceTreeAuto=</varname>/<option>--devicetree-auto=</option>,
<varname>HWIDs=</varname>/<option>--hwids=</option>,
<varname>Splash=</varname>/<option>--splash=</option>, <varname>Splash=</varname>/<option>--splash=</option>,
<varname>PCRPKey=</varname>/<option>--pcrpkey=</option>, <varname>PCRPKey=</varname>/<option>--pcrpkey=</option>,
<varname>Uname=</varname>/<option>--uname=</option>, <varname>Uname=</varname>/<option>--uname=</option>,
@ -374,6 +376,35 @@
<xi:include href="version-info.xml" xpointer="v253"/></listitem> <xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>DeviceTreeAuto=<replaceable>PATH</replaceable>...</varname></term>
<term><option>--devicetree-auto=<replaceable>PATH</replaceable></option></term>
<listitem><para>Zero or more automatically selectable DeviceTree files. In the configuration file, items are separated by
whitespace. Each DeviceTree will be in a separate <literal>.dtbauto</literal> section.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>HWIDs=<replaceable>PATH</replaceable></varname></term>
<term><option>--hwids=<replaceable>PATH</replaceable></option></term>
<listitem><para>The hardware ID device table (the <literal>.hwids</literal> section). The argument is a
path to a directory with JSON HWID device description files. Each file needs to contain a single JSON object with a <literal>name</literal>, <literal>compatible</literal> and <literal>hwids</literal> keys. The <literal>name</literal> and <literal>compatible</literal> keys must have string values and the <literal>hwids</literal> key must have a list of strings as value, where the strings must be valid UUIDs that represent CHIDs/HWIDs.
Example:
<programlisting><xi:include href="ukify_hwid.json.example" parse="text" /></programlisting>
Here <literal>Example Laptop 16 Gen 7</literal> is the device <literal>name</literal> (as defined by the manufacturer),
<literal>example,laptop-16-g7</literal> is the <literal>compatible</literal> (as defined by the kernel) and <literal>hwids</literal>
is an array of CHIDs/HWIDs (extracted i.e. from <command>fwupdtool hwids</command> output).
If not specified, the section will not be present. It is recommended to specify this parameter if automatically
selectable DeviceTrees are to be used.
</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>Uname=<replaceable>VERSION</replaceable></varname></term> <term><varname>Uname=<replaceable>VERSION</replaceable></varname></term>
<term><option>--uname=<replaceable>VERSION</replaceable></option></term> <term><option>--uname=<replaceable>VERSION</replaceable></option></term>

View File

@ -0,0 +1,8 @@
{
"name": "Example Laptop 16 Gen 7",
"compatible": "example,laptop-16-g7",
"hwids": [
"5dc05bf4-01f6-4089-b464-a08c47ea9295",
"3e3f8f3c-2003-46f2-811c-85554f7d5952"
]
}

View File

@ -42,6 +42,7 @@ import subprocess
import sys import sys
import tempfile import tempfile
import textwrap import textwrap
import uuid
from collections.abc import Iterable, Iterator, Sequence from collections.abc import Iterable, Iterator, Sequence
from hashlib import sha256 from hashlib import sha256
from pathlib import Path from pathlib import Path
@ -1013,14 +1014,9 @@ def merge_sbat(input_pe: list[Path], input_text: list[str]) -> str:
) )
# Keep in sync with EFI_GUID (src/boot/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 (DEVICE_TYPE_DEVICETREE) from src/boot/chid.h # Keep in sync with Device (DEVICE_TYPE_DEVICETREE) from src/boot/chid.h
# uint32_t descriptor, EFI_GUID chid, uint32_t name_offset, uint32_t compatible_offset # uint32_t descriptor, EFI_GUID chid, uint32_t name_offset, uint32_t compatible_offset
DEVICE_STRUCT_SIZE = 4 + EFI_GUID_STRUCT_SIZE + 4 + 4 DEVICE_STRUCT_SIZE = 4 + 16 + 4 + 4
NULL_DEVICE = b'\0' * DEVICE_STRUCT_SIZE NULL_DEVICE = b'\0' * DEVICE_STRUCT_SIZE
DEVICE_TYPE_DEVICETREE = 1 DEVICE_TYPE_DEVICETREE = 1
@ -1029,29 +1025,21 @@ def device_make_descriptor(device_type: int, size: int) -> int:
return (size) | (device_type << 28) return (size) | (device_type << 28)
def pack_device(offsets: dict[str, int], name: str, compatible: str, chids: list[EFI_GUID]) -> bytes: DEVICETREE_DESCRIPTOR = device_make_descriptor(DEVICE_TYPE_DEVICETREE, DEVICE_STRUCT_SIZE)
def pack_device(offsets: dict[str, int], name: str, compatible: str, chids: set[uuid.UUID]) -> bytes:
data = b'' data = b''
for data1, data2, data3, data4 in chids: for chid in sorted(chids):
data += struct.pack( data += struct.pack('<I', DEVICETREE_DESCRIPTOR)
'<IIHH8BII', data += chid.bytes_le
device_make_descriptor(DEVICE_TYPE_DEVICETREE, DEVICE_STRUCT_SIZE), data += struct.pack('<II', offsets[name], offsets[compatible])
data1,
data2,
data3,
*data4,
offsets[name],
offsets[compatible],
)
assert len(data) == DEVICE_STRUCT_SIZE * len(chids) assert len(data) == DEVICE_STRUCT_SIZE * len(chids)
return data 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]]: def pack_strings(strings: set[str], base: int) -> tuple[bytes, dict[str, int]]:
blob = b'' blob = b''
offsets = {} offsets = {}
@ -1064,56 +1052,22 @@ def pack_strings(strings: set[str], base: int) -> tuple[bytes, dict[str, int]]:
def parse_hwid_dir(path: Path) -> bytes: def parse_hwid_dir(path: Path) -> bytes:
hwid_files = path.rglob('*.txt') hwid_files = path.rglob('*.json')
strings: set[str] = set() strings: set[str] = set()
devices: collections.defaultdict[tuple[str, str], list[EFI_GUID]] = collections.defaultdict(list) devices: collections.defaultdict[tuple[str, str], set[uuid.UUID]] = collections.defaultdict(set)
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: for hwid_file in hwid_files:
content = hwid_file.open().readlines() data = json.loads(hwid_file.read_text(encoding='UTF-8'))
data: dict[str, str] = { for k in ['name', 'compatible', 'hwids']:
'Manufacturer': '', if k not in data:
'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}"') raise ValueError(f'hwid description file "{hwid_file}" does not contain "{k}"')
name = data['Manufacturer'] + ' ' + data['Family'] strings |= {data['name'], data['compatible']}
compatible = data['Compatible']
strings |= set([name, compatible]) # (name, compatible) pair uniquely identifies the device
devices[(data['name'], data['compatible'])] |= {uuid.UUID(u) for u in data['hwids']}
# (compatible, name) pair uniquely identifies the device
devices[(compatible, name)] += uuids
total_device_structs = 1 total_device_structs = 1
for dev, uuids in devices.items(): for dev, uuids in devices.items():
@ -1122,7 +1076,7 @@ def parse_hwid_dir(path: Path) -> bytes:
strings_blob, offsets = pack_strings(strings, total_device_structs * DEVICE_STRUCT_SIZE) strings_blob, offsets = pack_strings(strings, total_device_structs * DEVICE_STRUCT_SIZE)
devices_blob = b'' devices_blob = b''
for (compatible, name), uuids in devices.items(): for (name, compatible), uuids in devices.items():
devices_blob += pack_device(offsets, name, compatible, uuids) devices_blob += pack_device(offsets, name, compatible, uuids)
devices_blob += NULL_DEVICE devices_blob += NULL_DEVICE