xref: /freebsd/usr.bin/mkimg/gpt.c (revision dab8138e13dea539a387c458979403980a137bf2)
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_NTFS, ALIAS_PTR2TYPE(&gpt_uuid_ms_basic_data) },
70     {	ALIAS_PPCBOOT, ALIAS_PTR2TYPE(&gpt_uuid_prep_boot) },
71     {	ALIAS_HIFIVE_BBL, ALIAS_PTR2TYPE(&gpt_uuid_hifive_bbl) },
72     {	ALIAS_XBOOTLDR, ALIAS_PTR2TYPE(&gpt_uuid_xbootldr) },
73     {	ALIAS_HIFIVE_FSBL, ALIAS_PTR2TYPE(&gpt_uuid_hifive_fsbl) },
74     {	ALIAS_NONE, 0 }		/* Keep last! */
75 };
76 
77 /* CRC32 code derived from work by Gary S. Brown. */
78 static const uint32_t crc32_tab[] = {
79 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
80 	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
81 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
82 	0xf3b97148, 0x84be41de,	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
83 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,	0x14015c4f, 0x63066cd9,
84 	0xfa0f3d63, 0x8d080df5,	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
85 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,	0x35b5a8fa, 0x42b2986c,
86 	0xdbbbc9d6, 0xacbcf940,	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
87 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
88 	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
89 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,	0x76dc4190, 0x01db7106,
90 	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
91 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
92 	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
93 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
94 	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
95 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
96 	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
97 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
98 	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
99 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
100 	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
101 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
102 	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
103 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
104 	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
105 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
106 	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
107 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
108 	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
109 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
110 	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
111 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
112 	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
113 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
114 	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
115 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
116 	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
117 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
118 	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
119 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
120 	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
121 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
122 };
123 
124 static uint32_t
crc32(const void * buf,size_t sz)125 crc32(const void *buf, size_t sz)
126 {
127 	const uint8_t *p = (const uint8_t *)buf;
128 	uint32_t crc = ~0U;
129 
130 	while (sz--)
131 		crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
132 	return (crc ^ ~0U);
133 }
134 
135 /*
136  * Return the number of sectors needed to store the partition table.
137  */
138 static u_int
gpt_tblsz(void)139 gpt_tblsz(void)
140 {
141 	u_int eps;		/* Entries per Sector */
142 
143 	/*
144 	 * Count the number of sectors needed for the GPT Entry Array to store
145 	 * the number of partitions defined for this image.  Enforce the 16kB
146 	 * minimum space for the GPT Entry Array per UEFI v2.10 Section 5.3.
147 	 */
148 	eps = secsz / sizeof(struct gpt_ent);
149 	return (MAX(howmany(GPT_MIN_RESERVED, secsz), howmany(nparts, eps)));
150 }
151 
152 static lba_t
gpt_metadata(u_int where,lba_t blk)153 gpt_metadata(u_int where, lba_t blk)
154 {
155 
156 	if (where == SCHEME_META_IMG_START || where == SCHEME_META_IMG_END) {
157 		blk += gpt_tblsz();
158 		blk += (where == SCHEME_META_IMG_START) ? 2 : 1;
159 	}
160 	return (round_block(blk));
161 }
162 
163 static int
gpt_write_pmbr(lba_t blks,void * bootcode)164 gpt_write_pmbr(lba_t blks, void *bootcode)
165 {
166 	u_char *pmbr;
167 	uint32_t secs;
168 	int error;
169 
170 	secs = (blks > UINT32_MAX) ? UINT32_MAX : (uint32_t)blks - 1;
171 
172 	pmbr = malloc(secsz);
173 	if (pmbr == NULL)
174 		return (errno);
175 	if (bootcode != NULL) {
176 		memcpy(pmbr, bootcode, DOSPARTOFF);
177 		memset(pmbr + DOSPARTOFF, 0, secsz - DOSPARTOFF);
178 	} else
179 		memset(pmbr, 0, secsz);
180 	pmbr[DOSPARTOFF + 2] = 2;
181 	pmbr[DOSPARTOFF + 4] = 0xee;
182 	pmbr[DOSPARTOFF + 5] = 0xff;
183 	pmbr[DOSPARTOFF + 6] = 0xff;
184 	pmbr[DOSPARTOFF + 7] = 0xff;
185 	le32enc(pmbr + DOSPARTOFF + 8, 1);
186 	le32enc(pmbr + DOSPARTOFF + 12, secs);
187 	le16enc(pmbr + DOSMAGICOFFSET, DOSMAGIC);
188 	error = image_write(0, pmbr, 1);
189 	free(pmbr);
190 	return (error);
191 }
192 
193 static struct gpt_ent *
gpt_mktbl(u_int tblsz)194 gpt_mktbl(u_int tblsz)
195 {
196 	mkimg_uuid_t uuid;
197 	struct gpt_ent *tbl, *ent;
198 	struct part *part;
199 	int c, idx;
200 
201 	tbl = calloc(tblsz, secsz);
202 	if (tbl == NULL)
203 		return (NULL);
204 
205 	TAILQ_FOREACH(part, &partlist, link) {
206 		ent = tbl + part->index;
207 		mkimg_uuid_enc(&ent->ent_type, ALIAS_TYPE2PTR(part->type));
208 		mkimg_uuid(&uuid);
209 		mkimg_uuid_enc(&ent->ent_uuid, &uuid);
210 		le64enc(&ent->ent_lba_start, part->block);
211 		le64enc(&ent->ent_lba_end, part->block + part->size - 1);
212 		if (part->label != NULL) {
213 			idx = 0;
214 			while ((c = part->label[idx]) != '\0') {
215 				le16enc(ent->ent_name + idx, c);
216 				idx++;
217 			}
218 		}
219 	}
220 	return (tbl);
221 }
222 
223 static int
gpt_write_hdr(struct gpt_hdr * hdr,uint64_t self,uint64_t alt,uint64_t tbl)224 gpt_write_hdr(struct gpt_hdr *hdr, uint64_t self, uint64_t alt, uint64_t tbl)
225 {
226 	uint32_t crc;
227 
228 	le64enc(&hdr->hdr_lba_self, self);
229 	le64enc(&hdr->hdr_lba_alt, alt);
230 	le64enc(&hdr->hdr_lba_table, tbl);
231 	hdr->hdr_crc_self = 0;
232 	crc = crc32(hdr, offsetof(struct gpt_hdr, padding));
233 	le64enc(&hdr->hdr_crc_self, crc);
234 	return (image_write(self, hdr, 1));
235 }
236 
237 static int
gpt_write(lba_t imgsz,void * bootcode)238 gpt_write(lba_t imgsz, void *bootcode)
239 {
240 	mkimg_uuid_t uuid;
241 	struct gpt_ent *tbl;
242 	struct gpt_hdr *hdr;
243 	uint32_t crc;
244 	u_int tblsz;
245 	int error;
246 
247 	/* PMBR */
248 	error = gpt_write_pmbr(imgsz, bootcode);
249 	if (error)
250 		return (error);
251 
252 	/* GPT table(s) */
253 	tblsz = gpt_tblsz();
254 	tbl = gpt_mktbl(tblsz);
255 	if (tbl == NULL)
256 		return (errno);
257 	error = image_write(2, tbl, tblsz);
258 	if (error)
259 		goto out;
260 	error = image_write(imgsz - (tblsz + 1), tbl, tblsz);
261 	if (error)
262 		goto out;
263 
264 	/* GPT header(s) */
265 	hdr = malloc(secsz);
266 	if (hdr == NULL) {
267 		error = errno;
268 		goto out;
269 	}
270 	memset(hdr, 0, secsz);
271 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
272 	le32enc(&hdr->hdr_revision, GPT_HDR_REVISION);
273 	le32enc(&hdr->hdr_size, offsetof(struct gpt_hdr, padding));
274 	le64enc(&hdr->hdr_lba_start, 2 + tblsz);
275 	le64enc(&hdr->hdr_lba_end, imgsz - tblsz - 2);
276 	mkimg_uuid(&uuid);
277 	mkimg_uuid_enc(&hdr->hdr_uuid, &uuid);
278 	le32enc(&hdr->hdr_entries, tblsz * secsz / sizeof(struct gpt_ent));
279 	le32enc(&hdr->hdr_entsz, sizeof(struct gpt_ent));
280 	crc = crc32(tbl, tblsz * secsz);
281 	le32enc(&hdr->hdr_crc_table, crc);
282 	error = gpt_write_hdr(hdr, 1, imgsz - 1, 2);
283 	if (!error)
284 		error = gpt_write_hdr(hdr, imgsz - 1, 1, imgsz - tblsz - 1);
285 	free(hdr);
286 
287  out:
288 	free(tbl);
289 	return (error);
290 }
291 
292 static struct mkimg_scheme gpt_scheme = {
293 	.name = "gpt",
294 	.description = "GUID Partition Table",
295 	.aliases = gpt_aliases,
296 	.metadata = gpt_metadata,
297 	.write = gpt_write,
298 	.nparts = 4096,
299 	.labellen = 36,
300 	.bootcode = 512,
301 	.maxsecsz = 4096
302 };
303 
304 SCHEME_DEFINE(gpt_scheme);
305