xref: /aosp_15_r20/bootable/libbootloader/gbl/tools/gen_gpt_disk.py (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1*5225e6b1SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*5225e6b1SAndroid Build Coastguard Worker#
3*5225e6b1SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project
4*5225e6b1SAndroid Build Coastguard Worker#
5*5225e6b1SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*5225e6b1SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*5225e6b1SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*5225e6b1SAndroid Build Coastguard Worker#
9*5225e6b1SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*5225e6b1SAndroid Build Coastguard Worker#
11*5225e6b1SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*5225e6b1SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*5225e6b1SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*5225e6b1SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*5225e6b1SAndroid Build Coastguard Worker# limitations under the License.
16*5225e6b1SAndroid Build Coastguard Worker"""Generate a GPT disk
17*5225e6b1SAndroid Build Coastguard Worker
18*5225e6b1SAndroid Build Coastguard WorkerExample:
19*5225e6b1SAndroid Build Coastguard Worker
20*5225e6b1SAndroid Build Coastguard WorkerThe following command creates a 16mb disk file gpt.bin with two partitions:
21*5225e6b1SAndroid Build Coastguard Worker
22*5225e6b1SAndroid Build Coastguard Worker  1) "boot_a" of size 4096KB, with data initialized from "file_a",
23*5225e6b1SAndroid Build Coastguard Worker  2) "boot_b" of size 8192KB, with data initialized to zeroes.
24*5225e6b1SAndroid Build Coastguard Worker
25*5225e6b1SAndroid Build Coastguard Worker    gen_gpt_disk.py ./gpt.bin 16M
26*5225e6b1SAndroid Build Coastguard Worker        --partition "boot_a,4096K,<path to file_a>" \
27*5225e6b1SAndroid Build Coastguard Worker        --partition "boot_b,8192K,"
28*5225e6b1SAndroid Build Coastguard Worker
29*5225e6b1SAndroid Build Coastguard WorkerAll GUIDs will be non-deterministic for reproducibility.
30*5225e6b1SAndroid Build Coastguard Worker"""
31*5225e6b1SAndroid Build Coastguard Worker
32*5225e6b1SAndroid Build Coastguard Workerimport argparse
33*5225e6b1SAndroid Build Coastguard Workerimport os
34*5225e6b1SAndroid Build Coastguard Workerimport pathlib
35*5225e6b1SAndroid Build Coastguard Workerimport shutil
36*5225e6b1SAndroid Build Coastguard Workerimport sys
37*5225e6b1SAndroid Build Coastguard Workerimport subprocess
38*5225e6b1SAndroid Build Coastguard Workerimport tempfile
39*5225e6b1SAndroid Build Coastguard Worker
40*5225e6b1SAndroid Build Coastguard WorkerGPT_BLOCK_SIZE = 512
41*5225e6b1SAndroid Build Coastguard Worker
42*5225e6b1SAndroid Build Coastguard Worker# Some GPT libraries may expect a valid GUID here, these are just pre-generated
43*5225e6b1SAndroid Build Coastguard Worker# valid GUIDs for reproducibility.
44*5225e6b1SAndroid Build Coastguard WorkerDISK_GUID = "b116114b-b6ae-4186-8231-b35104cb4c9e"
45*5225e6b1SAndroid Build Coastguard WorkerPARTITION_GUIDS = [
46*5225e6b1SAndroid Build Coastguard Worker    "36bc51fd-c3d6-4109-a2ac-35bddd757e2a",
47*5225e6b1SAndroid Build Coastguard Worker    "42aaac2e-37e3-43ba-9930-42dfa96e6334",
48*5225e6b1SAndroid Build Coastguard Worker    "bdadfeca-879c-43e9-8f0d-8ef7da29b5e7",
49*5225e6b1SAndroid Build Coastguard Worker    "8d5b90b2-0d65-476b-8691-94f74fcac271",
50*5225e6b1SAndroid Build Coastguard Worker    "dafe682f-3f2b-4472-a1b8-a0f217701909",
51*5225e6b1SAndroid Build Coastguard Worker    "7097fd78-f559-461e-bb9b-db176a8169d8",
52*5225e6b1SAndroid Build Coastguard Worker    "c03dd176-117b-4e65-8205-37ebec007c1a",
53*5225e6b1SAndroid Build Coastguard Worker    "6d9159db-9b9e-4906-8fa4-31f5ffaef50e",
54*5225e6b1SAndroid Build Coastguard Worker    "4cdfda9f-23aa-4b27-9fea-24bb08238135",
55*5225e6b1SAndroid Build Coastguard Worker    "2a0a3df9-e8ef-4627-b4ce-ef1e847924f4",
56*5225e6b1SAndroid Build Coastguard Worker    "3c9b64f9-c659-4e5d-9d25-72028c46e6b8",
57*5225e6b1SAndroid Build Coastguard Worker    "95f142f9-d1f3-41ad-96dc-b969ee8242a1",
58*5225e6b1SAndroid Build Coastguard Worker    "5181811b-e836-4e66-bfe2-91aa86da515f",
59*5225e6b1SAndroid Build Coastguard Worker    "f2dbad77-38ac-43de-b4c7-2f7432d2339e",
60*5225e6b1SAndroid Build Coastguard Worker    "fc172d2c-f735-49a5-8be0-a475d0fc5be9",
61*5225e6b1SAndroid Build Coastguard Worker    "5ad48195-8e26-4496-a7e2-669028db2dce",
62*5225e6b1SAndroid Build Coastguard Worker    "f49a6965-8168-4c0c-8102-7b64a8176c25",
63*5225e6b1SAndroid Build Coastguard Worker    "be495262-5d9b-4fcb-9240-d9e84b195abe",
64*5225e6b1SAndroid Build Coastguard Worker    "c5ab3a8d-f898-420f-9039-a7445760fb0f",
65*5225e6b1SAndroid Build Coastguard Worker    "bc79912b-44bf-4ed8-8320-796ba47714d1",
66*5225e6b1SAndroid Build Coastguard Worker]
67*5225e6b1SAndroid Build Coastguard Worker
68*5225e6b1SAndroid Build Coastguard Worker
69*5225e6b1SAndroid Build Coastguard Workerdef parse_args():
70*5225e6b1SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
71*5225e6b1SAndroid Build Coastguard Worker    parser.add_argument("out", help="output file")
72*5225e6b1SAndroid Build Coastguard Worker    parser.add_argument(
73*5225e6b1SAndroid Build Coastguard Worker        "disk_size", type=str, help="disk size of the image. i.e. 64k, 1M etc"
74*5225e6b1SAndroid Build Coastguard Worker    )
75*5225e6b1SAndroid Build Coastguard Worker    parser.add_argument(
76*5225e6b1SAndroid Build Coastguard Worker        "--partition",
77*5225e6b1SAndroid Build Coastguard Worker        type=str,
78*5225e6b1SAndroid Build Coastguard Worker        action="append",
79*5225e6b1SAndroid Build Coastguard Worker        help="specifies a partition. Format should be"
80*5225e6b1SAndroid Build Coastguard Worker        "--partition=<part name>,<size>,<file name>",
81*5225e6b1SAndroid Build Coastguard Worker    )
82*5225e6b1SAndroid Build Coastguard Worker    return parser.parse_args()
83*5225e6b1SAndroid Build Coastguard Worker
84*5225e6b1SAndroid Build Coastguard Worker
85*5225e6b1SAndroid Build Coastguard Workerdef parse_size_str(size_str: str) -> int:
86*5225e6b1SAndroid Build Coastguard Worker    """Parse size string into integer, taking into account unit.
87*5225e6b1SAndroid Build Coastguard Worker
88*5225e6b1SAndroid Build Coastguard Worker    Example:
89*5225e6b1SAndroid Build Coastguard Worker        2M, 2m -> 2*1024*1024
90*5225e6b1SAndroid Build Coastguard Worker        2K, 2k -> 2*1024
91*5225e6b1SAndroid Build Coastguard Worker        512 -> 512
92*5225e6b1SAndroid Build Coastguard Worker    """
93*5225e6b1SAndroid Build Coastguard Worker    size_str = size_str.lower()
94*5225e6b1SAndroid Build Coastguard Worker    if size_str.endswith("m"):
95*5225e6b1SAndroid Build Coastguard Worker        return int(size_str[:-1]) * 1024 * 1024
96*5225e6b1SAndroid Build Coastguard Worker    if size_str.endswith("k"):
97*5225e6b1SAndroid Build Coastguard Worker        return int(size_str[:-1]) * 1024
98*5225e6b1SAndroid Build Coastguard Worker    return int(size_str)
99*5225e6b1SAndroid Build Coastguard Worker
100*5225e6b1SAndroid Build Coastguard Worker
101*5225e6b1SAndroid Build Coastguard Workerdef _append(src_file: str, offset: int, size: int, dst_file: str):
102*5225e6b1SAndroid Build Coastguard Worker    """Append a portion of a file to the end of another file
103*5225e6b1SAndroid Build Coastguard Worker
104*5225e6b1SAndroid Build Coastguard Worker    Args:
105*5225e6b1SAndroid Build Coastguard Worker        src_file: path to the source file
106*5225e6b1SAndroid Build Coastguard Worker        offset: offset in the source file
107*5225e6b1SAndroid Build Coastguard Worker        size: number of bytes to append
108*5225e6b1SAndroid Build Coastguard Worker        dst_file: destination file
109*5225e6b1SAndroid Build Coastguard Worker
110*5225e6b1SAndroid Build Coastguard Worker    Returns:
111*5225e6b1SAndroid Build Coastguard Worker        number of bytes actually copied.
112*5225e6b1SAndroid Build Coastguard Worker    """
113*5225e6b1SAndroid Build Coastguard Worker    with open(src_file, "rb") as src_f:
114*5225e6b1SAndroid Build Coastguard Worker        src_f.seek(offset, 0)
115*5225e6b1SAndroid Build Coastguard Worker        data = src_f.read(size)
116*5225e6b1SAndroid Build Coastguard Worker        if len(data) != size:
117*5225e6b1SAndroid Build Coastguard Worker            raise ValueError(f"Want {size} bytes from {src_file}, but got {len(data)}.")
118*5225e6b1SAndroid Build Coastguard Worker        with open(dst_file, "ab") as dst_f:
119*5225e6b1SAndroid Build Coastguard Worker            dst_f.write(data)
120*5225e6b1SAndroid Build Coastguard Worker    return size
121*5225e6b1SAndroid Build Coastguard Worker
122*5225e6b1SAndroid Build Coastguard Worker
123*5225e6b1SAndroid Build Coastguard Workerdef main() -> int:
124*5225e6b1SAndroid Build Coastguard Worker    args = parse_args()
125*5225e6b1SAndroid Build Coastguard Worker    with tempfile.TemporaryDirectory() as temp_dir:
126*5225e6b1SAndroid Build Coastguard Worker        temp_disk = pathlib.Path(temp_dir) / "temp_disk"
127*5225e6b1SAndroid Build Coastguard Worker
128*5225e6b1SAndroid Build Coastguard Worker        disk_size = parse_size_str(args.disk_size)
129*5225e6b1SAndroid Build Coastguard Worker        temp_disk.write_bytes(disk_size * b"\x00")
130*5225e6b1SAndroid Build Coastguard Worker
131*5225e6b1SAndroid Build Coastguard Worker        part_start = 34 * GPT_BLOCK_SIZE  # MBR + GPT header + entries
132*5225e6b1SAndroid Build Coastguard Worker        partition_info = []
133*5225e6b1SAndroid Build Coastguard Worker        sgdisk_command = [
134*5225e6b1SAndroid Build Coastguard Worker            "sgdisk",
135*5225e6b1SAndroid Build Coastguard Worker            str(temp_disk),
136*5225e6b1SAndroid Build Coastguard Worker            "--clear",
137*5225e6b1SAndroid Build Coastguard Worker            "--set-alignment",
138*5225e6b1SAndroid Build Coastguard Worker            "1",
139*5225e6b1SAndroid Build Coastguard Worker            "--disk-guid",
140*5225e6b1SAndroid Build Coastguard Worker            DISK_GUID,
141*5225e6b1SAndroid Build Coastguard Worker        ]
142*5225e6b1SAndroid Build Coastguard Worker
143*5225e6b1SAndroid Build Coastguard Worker        for i, part in enumerate(args.partition, start=1):
144*5225e6b1SAndroid Build Coastguard Worker            name, size, file = part.split(",")
145*5225e6b1SAndroid Build Coastguard Worker            if not size:
146*5225e6b1SAndroid Build Coastguard Worker                raise ValueError("Must provide a size")
147*5225e6b1SAndroid Build Coastguard Worker            size = parse_size_str(size)
148*5225e6b1SAndroid Build Coastguard Worker
149*5225e6b1SAndroid Build Coastguard Worker            sgdisk_command += [
150*5225e6b1SAndroid Build Coastguard Worker                "--new",
151*5225e6b1SAndroid Build Coastguard Worker                f"{i}:{part_start // GPT_BLOCK_SIZE}:{(part_start + size) // GPT_BLOCK_SIZE - 1}",
152*5225e6b1SAndroid Build Coastguard Worker                "--partition-guid",
153*5225e6b1SAndroid Build Coastguard Worker                f"{i}:{PARTITION_GUIDS[i]}",
154*5225e6b1SAndroid Build Coastguard Worker                "--change-name",
155*5225e6b1SAndroid Build Coastguard Worker                f"{i}:{name}",
156*5225e6b1SAndroid Build Coastguard Worker            ]
157*5225e6b1SAndroid Build Coastguard Worker
158*5225e6b1SAndroid Build Coastguard Worker            partition_info.append((name, part_start, size, file))
159*5225e6b1SAndroid Build Coastguard Worker            part_start += size
160*5225e6b1SAndroid Build Coastguard Worker
161*5225e6b1SAndroid Build Coastguard Worker        res = subprocess.run(sgdisk_command, check=True, text=True)
162*5225e6b1SAndroid Build Coastguard Worker
163*5225e6b1SAndroid Build Coastguard Worker        # Create the final disk with partition content
164*5225e6b1SAndroid Build Coastguard Worker        dest_file = pathlib.Path(args.out)
165*5225e6b1SAndroid Build Coastguard Worker        dest_file.write_bytes(0 * b"\x00")
166*5225e6b1SAndroid Build Coastguard Worker        append_offset = 0
167*5225e6b1SAndroid Build Coastguard Worker        for name, start, size, file in partition_info:
168*5225e6b1SAndroid Build Coastguard Worker            end = start + size
169*5225e6b1SAndroid Build Coastguard Worker            # Fill gap
170*5225e6b1SAndroid Build Coastguard Worker            append_offset += _append(
171*5225e6b1SAndroid Build Coastguard Worker                str(temp_disk), append_offset, start - append_offset, args.out
172*5225e6b1SAndroid Build Coastguard Worker            )
173*5225e6b1SAndroid Build Coastguard Worker
174*5225e6b1SAndroid Build Coastguard Worker            # Copy over file
175*5225e6b1SAndroid Build Coastguard Worker            if file:
176*5225e6b1SAndroid Build Coastguard Worker                file_size = os.path.getsize(file)
177*5225e6b1SAndroid Build Coastguard Worker                if file_size > size:
178*5225e6b1SAndroid Build Coastguard Worker                    raise ValueError(f"{file} too large for {name}")
179*5225e6b1SAndroid Build Coastguard Worker                append_offset += _append(file, 0, file_size, args.out)
180*5225e6b1SAndroid Build Coastguard Worker
181*5225e6b1SAndroid Build Coastguard Worker            # If partition size greater than file size, copy the remaining
182*5225e6b1SAndroid Build Coastguard Worker            # partition content from `temp_disk` (zeroes)
183*5225e6b1SAndroid Build Coastguard Worker            append_offset += _append(
184*5225e6b1SAndroid Build Coastguard Worker                str(temp_disk), append_offset, end - append_offset, args.out
185*5225e6b1SAndroid Build Coastguard Worker            )
186*5225e6b1SAndroid Build Coastguard Worker
187*5225e6b1SAndroid Build Coastguard Worker        # Copy the remaining disk from `temp_disk` (zeroes + back up header/entries)
188*5225e6b1SAndroid Build Coastguard Worker        append_offset += _append(
189*5225e6b1SAndroid Build Coastguard Worker            str(temp_disk), append_offset, disk_size - append_offset, args.out
190*5225e6b1SAndroid Build Coastguard Worker        )
191*5225e6b1SAndroid Build Coastguard Worker
192*5225e6b1SAndroid Build Coastguard Worker    return 0
193*5225e6b1SAndroid Build Coastguard Worker
194*5225e6b1SAndroid Build Coastguard Worker
195*5225e6b1SAndroid Build Coastguard Workerif __name__ == "__main__":
196*5225e6b1SAndroid Build Coastguard Worker    sys.exit(main())
197