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