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