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