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