1#!/usr/bin/env python3
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4import re
5import sys
6import uuid
7
8HEADER = f'''\
9| Name | Partition Type UUID | Allowed File Systems | Explanation |
10|------|---------------------|----------------------|-------------|
11'''
12
13ARCHITECTURES = {
14    'ALPHA':       'Alpha',
15    'ARC':         'ARC',
16    'ARM':         '32-bit ARM',
17    'ARM64':       '64-bit ARM/AArch64',
18    'IA64':        'Itanium/IA-64',
19    'LOONGARCH64': 'LoongArch 64-bit',
20    'MIPS_LE':     '32-bit MIPS LittleEndian (mipsel)',
21    'MIPS64_LE':   '64-bit MIPS LittleEndian (mips64el)',
22    'PPC':         '32-bit PowerPC',
23    'PPC64':       '64-bit PowerPC BigEndian',
24    'PPC64_LE':    '64-bit PowerPC LittleEndian',
25    'RISCV32':     'RISC-V 32-bit',
26    'RISCV64':     'RISC-V 64-bit',
27    'S390':        's390',
28    'S390X':       's390x',
29    'TILEGX':      'TILE-Gx',
30    'X86':         'x86',
31    'X86_64':      'amd64/x86_64',
32}
33
34TYPES = {
35    'ROOT' :            'Root Partition',
36    'ROOT_VERITY' :     'Root Verity Partition',
37    'ROOT_VERITY_SIG' : 'Root Verity Signature Partition',
38    'USR' :             '`/usr/` Partition',
39    'USR_VERITY' :      '`/usr/` Verity Partition',
40    'USR_VERITY_SIG' :  '`/usr/` Verity Signature Partition',
41
42    'ESP':              'EFI System Partition',
43    'SRV':              'Server Data Partition',
44    'VAR':              'Variable Data Partition',
45    'TMP':              'Temporary Data Partition',
46    'SWAP':             'Swap',
47    'HOME':             'Home Partition',
48    'USER_HOME':        'Per-user Home Partition',
49    'LINUX_GENERIC':    'Generic Linux Data Partition',
50    'XBOOTLDR':         'Extended Boot Loader Partition',
51}
52
53DESCRIPTIONS = {
54    'ROOT': (
55        'Any native, optionally in LUKS',
56        'On systems with matching architecture, the first partition with this type UUID on the disk '
57        'containing the active EFI ESP is automatically mounted to the root directory <tt>/</tt>. '
58        'If the partition is encrypted with LUKS or has dm-verity integrity data (see below), the '
59        'device mapper file will be named `/dev/mapper/root`.'),
60    'USR': (
61        'Any native, optionally in LUKS',
62        'Similar semantics to root partition, but just the `/usr/` partition.'),
63    'ROOT_VERITY': (
64        'A dm-verity superblock followed by hash data',
65        'Contains dm-verity integrity hash data for the matching root partition. If this feature is '
66        'used the partition UUID of the root partition should be the first 128 bits of the root hash '
67        'of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the '
68        'final 128 bits of it, so that the root partition and its Verity partition can be discovered '
69        'easily, simply by specifying the root hash.'),
70    'USR_VERITY': (
71        'A dm-verity superblock followed by hash data',
72        'Similar semantics to root Verity partition, but just for the `/usr/` partition.'),
73    'ROOT_VERITY_SIG': (
74        'A serialized JSON object, see below',
75        'Contains a root hash and a PKCS#7 signature for it, permitting signed dm-verity GPT images.'),
76    'USR_VERITY_SIG': (
77        'A serialized JSON object, see below',
78        'Similar semantics to root Verity signature partition, but just for the `/usr/` partition.'),
79
80    'ESP': (
81        'VFAT',
82        'The ESP used for the current boot is automatically mounted to `/efi/` (or `/boot/` as '
83        'fallback), unless a different partition is mounted there (possibly via `/etc/fstab`, or '
84        'because the Extended Boot Loader Partition — see below — exists) or the directory is '
85        'non-empty on the root disk.  This partition type is defined by the '
86        '[UEFI Specification](http://www.uefi.org/specifications).'),
87    'XBOOTLDR': (
88        'Typically VFAT',
89        'The Extended Boot Loader Partition (XBOOTLDR) used for the current boot is automatically '
90        'mounted to <tt>/boot/</tt>, unless a different partition is mounted there (possibly via '
91        '<tt>/etc/fstab</tt>) or the directory is non-empty on the root disk. This partition type '
92        'is defined by the [Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION).'),
93    'SWAP': (
94        'Swap, optionally in LUKS',
95        'All swap partitions on the disk containing the root partition are automatically enabled. '
96        'If the partition is encrypted with LUKS, the device mapper file will be named '
97        '`/dev/mapper/swap`. This partition type predates the Discoverable Partitions Specification.'),
98    'HOME': (
99        'Any native, optionally in LUKS',
100        'The first partition with this type UUID on the disk containing the root partition is '
101        'automatically mounted to `/home/`. If the partition is encrypted with LUKS, the device '
102        'mapper file will be named `/dev/mapper/home`.'),
103    'SRV': (
104        'Any native, optionally in LUKS',
105        'The first partition with this type UUID on the disk containing the root partition is '
106        'automatically mounted to `/srv/`. If the partition is encrypted with LUKS, the device '
107        'mapper file will be named `/dev/mapper/srv`.'),
108    'VAR': (
109        'Any native, optionally in LUKS',
110        'The first partition with this type UUID on the disk containing the root partition is '
111        'automatically mounted to `/var/` — under the condition that its partition UUID matches '
112        'the first 128 bits of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` '
113        '(i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from '
114        '[`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). '
115        'This special requirement is made because `/var/` (unlike the other partition types '
116        'listed here) is inherently private to a specific installation and cannot possibly be '
117        'shared between multiple OS installations on the same disk, and thus should be bound to '
118        'a specific instance of the OS, identified by its machine ID. If the partition is '
119        'encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`.'),
120    'TMP': (
121        'Any native, optionally in LUKS',
122        'The first partition with this type UUID on the disk containing the root partition is '
123        'automatically mounted to `/var/tmp/`. If the partition is encrypted with LUKS, the '
124        'device mapper file will be named `/dev/mapper/tmp`. Note that the intended mount point '
125        'is indeed `/var/tmp/`, not `/tmp/`. The latter is typically maintained in memory via '
126        '<tt>tmpfs</tt> and does not require a partition on disk. In some cases it might be '
127        'desirable to make `/tmp/` persistent too, in which case it is recommended to make it '
128        'a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID.'),
129    'USER_HOME': (
130        'Any native, optionally in LUKS',
131        'A home partition of a user, managed by '
132        '[`systemd-homed`](https://www.freedesktop.org/software/systemd/man/systemd-homed.html).'),
133    'LINUX_GENERIC': (
134        'Any native, optionally in LUKS',
135        'No automatic mounting takes place for other Linux data partitions. This partition type '
136        'should be used for all partitions that carry Linux file systems. The installer needs '
137        'to mount them explicitly via entries in <tt>/etc/fstab</tt>. Optionally, these '
138        'partitions may be encrypted with LUKS. This partition type predates the Discoverable '
139        'Partitions Specification.'),
140}
141
142def extract(file):
143    for line in file:
144        # print(line)
145        m = re.match(r'^#define\s+GPT_(.*SD_ID128_MAKE\(.*\))', line)
146        if not m:
147            continue
148
149        if m2 := re.match(r'^(ROOT|USR)_([A-Z0-9]+|X86_64|PPC64_LE|MIPS_LE|MIPS64_LE)(|_VERITY|_VERITY_SIG)\s+SD_ID128_MAKE\((.*)\)', m.group(1)):
150            type, arch, suffix, u = m2.groups()
151            u = uuid.UUID(u.replace(',', ''))
152            assert arch in ARCHITECTURES
153            type = f'{type}{suffix}'
154            assert type in TYPES
155
156            yield type, arch, u
157
158        elif m2 := re.match(r'(\w+)\s+SD_ID128_MAKE\((.*)\)', m.group(1)):
159            type, u = m2.groups()
160            u = uuid.UUID(u.replace(',', ''))
161            yield type, None, u
162
163        else:
164            raise Exception(f'Failed to match: {m.group(1)}')
165
166def generate(defines):
167    prevtype = None
168
169    print(HEADER, end='')
170
171    uuids = set()
172
173    for type, arch, uuid in defines:
174        tdesc = TYPES[type]
175        adesc = '' if arch is None else f' ({ARCHITECTURES[arch]})'
176
177        # Let's make sure that we didn't select&paste the same value twice
178        assert uuid not in uuids
179        uuids.add(uuid)
180
181        if type != prevtype:
182            prevtype = type
183            morea, moreb = DESCRIPTIONS[type]
184        else:
185            morea = moreb = 'ditto'
186
187        print(f'| _{tdesc}{adesc}_ | `{uuid}` | {morea} | {moreb} |')
188
189if __name__ == '__main__':
190    known = extract(sys.stdin)
191    generate(known)
192