xref: /freebsd/usr.bin/mkimg/gpt.c (revision 67f1c082b5ec37e2060b74e6f1f952ed38761468)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2014 Juniper Networks, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/errno.h>
31 #include <stddef.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #include <gpt.h>
37 #include <mbr.h>
38 
39 #include "endian.h"
40 #include "image.h"
41 #include "mkimg.h"
42 #include "scheme.h"
43 
44 static mkimg_uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
45 static mkimg_uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
46 static mkimg_uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
47 static mkimg_uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
48 static mkimg_uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
49 static mkimg_uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
50 static mkimg_uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
51 static mkimg_uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
52 static mkimg_uuid_t gpt_uuid_mbr = GPT_ENT_TYPE_MBR;
53 static mkimg_uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
54 static mkimg_uuid_t gpt_uuid_prep_boot = GPT_ENT_TYPE_PREP_BOOT;
55 static mkimg_uuid_t gpt_uuid_hifive_bbl = GPT_ENT_TYPE_HIFIVE_BBL;
56 static mkimg_uuid_t gpt_uuid_xbootldr = GPT_ENT_TYPE_XBOOTLDR;
57 static mkimg_uuid_t gpt_uuid_hifive_fsbl = GPT_ENT_TYPE_HIFIVE_FSBL;
58 
59 static struct mkimg_alias gpt_aliases[] = {
60     {	ALIAS_EFI, ALIAS_PTR2TYPE(&gpt_uuid_efi) },
61     {	ALIAS_FREEBSD, ALIAS_PTR2TYPE(&gpt_uuid_freebsd) },
62     {	ALIAS_FREEBSD_BOOT, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_boot) },
63     {	ALIAS_FREEBSD_NANDFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_nandfs) },
64     {	ALIAS_FREEBSD_SWAP, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_swap) },
65     {	ALIAS_FREEBSD_UFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_ufs) },
66     {	ALIAS_FREEBSD_VINUM, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_vinum) },
67     {	ALIAS_FREEBSD_ZFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_zfs) },
68     {	ALIAS_MBR, ALIAS_PTR2TYPE(&gpt_uuid_mbr) },
69     {	ALIAS_MS_BASIC_DATA, ALIAS_PTR2TYPE(&gpt_uuid_ms_basic_data) },
70     {	ALIAS_NTFS, ALIAS_PTR2TYPE(&gpt_uuid_ms_basic_data) },
71     {	ALIAS_PPCBOOT, ALIAS_PTR2TYPE(&gpt_uuid_prep_boot) },
72     {	ALIAS_HIFIVE_BBL, ALIAS_PTR2TYPE(&gpt_uuid_hifive_bbl) },
73     {	ALIAS_XBOOTLDR, ALIAS_PTR2TYPE(&gpt_uuid_xbootldr) },
74     {	ALIAS_HIFIVE_FSBL, ALIAS_PTR2TYPE(&gpt_uuid_hifive_fsbl) },
75     {	ALIAS_NONE, 0 }		/* Keep last! */
76 };
77 
78 /* CRC32 code derived from work by Gary S. Brown. */
79 static const uint32_t crc32_tab[] = {
80 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
81 	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
82 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
83 	0xf3b97148, 0x84be41de,	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
84 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,	0x14015c4f, 0x63066cd9,
85 	0xfa0f3d63, 0x8d080df5,	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
86 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,	0x35b5a8fa, 0x42b2986c,
87 	0xdbbbc9d6, 0xacbcf940,	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
88 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
89 	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
90 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,	0x76dc4190, 0x01db7106,
91 	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
92 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
93 	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
94 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
95 	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
96 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
97 	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
98 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
99 	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
100 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
101 	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
102 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
103 	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
104 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
105 	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
106 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
107 	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
108 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
109 	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
110 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
111 	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
112 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
113 	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
114 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
115 	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
116 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
117 	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
118 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
119 	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
120 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
121 	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
122 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
123 };
124 
125 static uint32_t
crc32(const void * buf,size_t sz)126 crc32(const void *buf, size_t sz)
127 {
128 	const uint8_t *p = (const uint8_t *)buf;
129 	uint32_t crc = ~0U;
130 
131 	while (sz--)
132 		crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
133 	return (crc ^ ~0U);
134 }
135 
136 /*
137  * Return the number of sectors needed to store the partition table.
138  */
139 static u_int
gpt_tblsz(void)140 gpt_tblsz(void)
141 {
142 	u_int eps;		/* Entries per Sector */
143 
144 	/*
145 	 * Count the number of sectors needed for the GPT Entry Array to store
146 	 * the number of partitions defined for this image.  Enforce the 16kB
147 	 * minimum space for the GPT Entry Array per UEFI v2.10 Section 5.3.
148 	 */
149 	eps = secsz / sizeof(struct gpt_ent);
150 	return (MAX(howmany(GPT_MIN_RESERVED, secsz), howmany(nparts, eps)));
151 }
152 
153 static lba_t
gpt_metadata(u_int where,lba_t blk)154 gpt_metadata(u_int where, lba_t blk)
155 {
156 
157 	if (where == SCHEME_META_IMG_START || where == SCHEME_META_IMG_END) {
158 		blk += gpt_tblsz();
159 		blk += (where == SCHEME_META_IMG_START) ? 2 : 1;
160 	}
161 	return (round_block(blk));
162 }
163 
164 static int
gpt_write_pmbr(lba_t blks,void * bootcode)165 gpt_write_pmbr(lba_t blks, void *bootcode)
166 {
167 	u_char *pmbr;
168 	uint32_t secs;
169 	int error;
170 
171 	secs = (blks > UINT32_MAX) ? UINT32_MAX : (uint32_t)blks - 1;
172 
173 	pmbr = malloc(secsz);
174 	if (pmbr == NULL)
175 		return (errno);
176 	if (bootcode != NULL) {
177 		memcpy(pmbr, bootcode, DOSPARTOFF);
178 		memset(pmbr + DOSPARTOFF, 0, secsz - DOSPARTOFF);
179 	} else
180 		memset(pmbr, 0, secsz);
181 	pmbr[DOSPARTOFF + 2] = 2;
182 	pmbr[DOSPARTOFF + 4] = 0xee;
183 	pmbr[DOSPARTOFF + 5] = 0xff;
184 	pmbr[DOSPARTOFF + 6] = 0xff;
185 	pmbr[DOSPARTOFF + 7] = 0xff;
186 	le32enc(pmbr + DOSPARTOFF + 8, 1);
187 	le32enc(pmbr + DOSPARTOFF + 12, secs);
188 	le16enc(pmbr + DOSMAGICOFFSET, DOSMAGIC);
189 	error = image_write(0, pmbr, 1);
190 	free(pmbr);
191 	return (error);
192 }
193 
194 static struct gpt_ent *
gpt_mktbl(u_int tblsz)195 gpt_mktbl(u_int tblsz)
196 {
197 	mkimg_uuid_t uuid;
198 	struct gpt_ent *tbl, *ent;
199 	struct part *part;
200 	int c, idx;
201 
202 	tbl = calloc(tblsz, secsz);
203 	if (tbl == NULL)
204 		return (NULL);
205 
206 	TAILQ_FOREACH(part, &partlist, link) {
207 		ent = tbl + part->index;
208 		mkimg_uuid_enc(&ent->ent_type, ALIAS_TYPE2PTR(part->type));
209 		mkimg_uuid(&uuid);
210 		mkimg_uuid_enc(&ent->ent_uuid, &uuid);
211 		le64enc(&ent->ent_lba_start, part->block);
212 		le64enc(&ent->ent_lba_end, part->block + part->size - 1);
213 		if (part->label != NULL) {
214 			idx = 0;
215 			while ((c = part->label[idx]) != '\0') {
216 				le16enc(ent->ent_name + idx, c);
217 				idx++;
218 			}
219 		}
220 	}
221 	return (tbl);
222 }
223 
224 static int
gpt_write_hdr(struct gpt_hdr * hdr,uint64_t self,uint64_t alt,uint64_t tbl)225 gpt_write_hdr(struct gpt_hdr *hdr, uint64_t self, uint64_t alt, uint64_t tbl)
226 {
227 	uint32_t crc;
228 
229 	le64enc(&hdr->hdr_lba_self, self);
230 	le64enc(&hdr->hdr_lba_alt, alt);
231 	le64enc(&hdr->hdr_lba_table, tbl);
232 	hdr->hdr_crc_self = 0;
233 	crc = crc32(hdr, offsetof(struct gpt_hdr, padding));
234 	le64enc(&hdr->hdr_crc_self, crc);
235 	return (image_write(self, hdr, 1));
236 }
237 
238 static int
gpt_write(lba_t imgsz,void * bootcode)239 gpt_write(lba_t imgsz, void *bootcode)
240 {
241 	mkimg_uuid_t uuid;
242 	struct gpt_ent *tbl;
243 	struct gpt_hdr *hdr;
244 	uint32_t crc;
245 	u_int tblsz;
246 	int error;
247 
248 	/* PMBR */
249 	error = gpt_write_pmbr(imgsz, bootcode);
250 	if (error)
251 		return (error);
252 
253 	/* GPT table(s) */
254 	tblsz = gpt_tblsz();
255 	tbl = gpt_mktbl(tblsz);
256 	if (tbl == NULL)
257 		return (errno);
258 	error = image_write(2, tbl, tblsz);
259 	if (error)
260 		goto out;
261 	error = image_write(imgsz - (tblsz + 1), tbl, tblsz);
262 	if (error)
263 		goto out;
264 
265 	/* GPT header(s) */
266 	hdr = malloc(secsz);
267 	if (hdr == NULL) {
268 		error = errno;
269 		goto out;
270 	}
271 	memset(hdr, 0, secsz);
272 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
273 	le32enc(&hdr->hdr_revision, GPT_HDR_REVISION);
274 	le32enc(&hdr->hdr_size, offsetof(struct gpt_hdr, padding));
275 	le64enc(&hdr->hdr_lba_start, 2 + tblsz);
276 	le64enc(&hdr->hdr_lba_end, imgsz - tblsz - 2);
277 	mkimg_uuid(&uuid);
278 	mkimg_uuid_enc(&hdr->hdr_uuid, &uuid);
279 	le32enc(&hdr->hdr_entries, tblsz * secsz / sizeof(struct gpt_ent));
280 	le32enc(&hdr->hdr_entsz, sizeof(struct gpt_ent));
281 	crc = crc32(tbl, tblsz * secsz);
282 	le32enc(&hdr->hdr_crc_table, crc);
283 	error = gpt_write_hdr(hdr, 1, imgsz - 1, 2);
284 	if (!error)
285 		error = gpt_write_hdr(hdr, imgsz - 1, 1, imgsz - tblsz - 1);
286 	free(hdr);
287 
288  out:
289 	free(tbl);
290 	return (error);
291 }
292 
293 static struct mkimg_scheme gpt_scheme = {
294 	.name = "gpt",
295 	.description = "GUID Partition Table",
296 	.aliases = gpt_aliases,
297 	.metadata = gpt_metadata,
298 	.write = gpt_write,
299 	.nparts = 4096,
300 	.labellen = 36,
301 	.bootcode = 512,
302 	.maxsecsz = 4096
303 };
304 
305 SCHEME_DEFINE(gpt_scheme);
306