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