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