xref: /freebsd/usr.bin/mkimg/gpt.c (revision bf2fa8d9d11c9f2ceff09bacc406876fa37096be)
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/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
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 static u_int
130 gpt_tblsz(void)
131 {
132 	u_int ents;
133 
134 	ents = secsz / sizeof(struct gpt_ent);
135 	return ((nparts + ents - 1) / ents);
136 }
137 
138 static lba_t
139 gpt_metadata(u_int where, lba_t blk)
140 {
141 
142 	if (where == SCHEME_META_IMG_START || where == SCHEME_META_IMG_END) {
143 		blk += gpt_tblsz();
144 		blk += (where == SCHEME_META_IMG_START) ? 2 : 1;
145 	}
146 	return (round_block(blk));
147 }
148 
149 static int
150 gpt_write_pmbr(lba_t blks, void *bootcode)
151 {
152 	u_char *pmbr;
153 	uint32_t secs;
154 	int error;
155 
156 	secs = (blks > UINT32_MAX) ? UINT32_MAX : (uint32_t)blks - 1;
157 
158 	pmbr = malloc(secsz);
159 	if (pmbr == NULL)
160 		return (errno);
161 	if (bootcode != NULL) {
162 		memcpy(pmbr, bootcode, DOSPARTOFF);
163 		memset(pmbr + DOSPARTOFF, 0, secsz - DOSPARTOFF);
164 	} else
165 		memset(pmbr, 0, secsz);
166 	pmbr[DOSPARTOFF + 2] = 2;
167 	pmbr[DOSPARTOFF + 4] = 0xee;
168 	pmbr[DOSPARTOFF + 5] = 0xff;
169 	pmbr[DOSPARTOFF + 6] = 0xff;
170 	pmbr[DOSPARTOFF + 7] = 0xff;
171 	le32enc(pmbr + DOSPARTOFF + 8, 1);
172 	le32enc(pmbr + DOSPARTOFF + 12, secs);
173 	le16enc(pmbr + DOSMAGICOFFSET, DOSMAGIC);
174 	error = image_write(0, pmbr, 1);
175 	free(pmbr);
176 	return (error);
177 }
178 
179 static struct gpt_ent *
180 gpt_mktbl(u_int tblsz)
181 {
182 	mkimg_uuid_t uuid;
183 	struct gpt_ent *tbl, *ent;
184 	struct part *part;
185 	int c, idx;
186 
187 	tbl = calloc(tblsz, secsz);
188 	if (tbl == NULL)
189 		return (NULL);
190 
191 	TAILQ_FOREACH(part, &partlist, link) {
192 		ent = tbl + part->index;
193 		mkimg_uuid_enc(&ent->ent_type, ALIAS_TYPE2PTR(part->type));
194 		mkimg_uuid(&uuid);
195 		mkimg_uuid_enc(&ent->ent_uuid, &uuid);
196 		le64enc(&ent->ent_lba_start, part->block);
197 		le64enc(&ent->ent_lba_end, part->block + part->size - 1);
198 		if (part->label != NULL) {
199 			idx = 0;
200 			while ((c = part->label[idx]) != '\0') {
201 				le16enc(ent->ent_name + idx, c);
202 				idx++;
203 			}
204 		}
205 	}
206 	return (tbl);
207 }
208 
209 static int
210 gpt_write_hdr(struct gpt_hdr *hdr, uint64_t self, uint64_t alt, uint64_t tbl)
211 {
212 	uint32_t crc;
213 
214 	le64enc(&hdr->hdr_lba_self, self);
215 	le64enc(&hdr->hdr_lba_alt, alt);
216 	le64enc(&hdr->hdr_lba_table, tbl);
217 	hdr->hdr_crc_self = 0;
218 	crc = crc32(hdr, offsetof(struct gpt_hdr, padding));
219 	le64enc(&hdr->hdr_crc_self, crc);
220 	return (image_write(self, hdr, 1));
221 }
222 
223 static int
224 gpt_write(lba_t imgsz, void *bootcode)
225 {
226 	mkimg_uuid_t uuid;
227 	struct gpt_ent *tbl;
228 	struct gpt_hdr *hdr;
229 	uint32_t crc;
230 	u_int tblsz;
231 	int error;
232 
233 	/* PMBR */
234 	error = gpt_write_pmbr(imgsz, bootcode);
235 	if (error)
236 		return (error);
237 
238 	/* GPT table(s) */
239 	tblsz = gpt_tblsz();
240 	tbl = gpt_mktbl(tblsz);
241 	if (tbl == NULL)
242 		return (errno);
243 	error = image_write(2, tbl, tblsz);
244 	if (error)
245 		goto out;
246 	error = image_write(imgsz - (tblsz + 1), tbl, tblsz);
247 	if (error)
248 		goto out;
249 
250 	/* GPT header(s) */
251 	hdr = malloc(secsz);
252 	if (hdr == NULL) {
253 		error = errno;
254 		goto out;
255 	}
256 	memset(hdr, 0, secsz);
257 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
258 	le32enc(&hdr->hdr_revision, GPT_HDR_REVISION);
259 	le32enc(&hdr->hdr_size, offsetof(struct gpt_hdr, padding));
260 	le64enc(&hdr->hdr_lba_start, 2 + tblsz);
261 	le64enc(&hdr->hdr_lba_end, imgsz - tblsz - 2);
262 	mkimg_uuid(&uuid);
263 	mkimg_uuid_enc(&hdr->hdr_uuid, &uuid);
264 	le32enc(&hdr->hdr_entries, tblsz * secsz / sizeof(struct gpt_ent));
265 	le32enc(&hdr->hdr_entsz, sizeof(struct gpt_ent));
266 	crc = crc32(tbl, tblsz * secsz);
267 	le32enc(&hdr->hdr_crc_table, crc);
268 	error = gpt_write_hdr(hdr, 1, imgsz - 1, 2);
269 	if (!error)
270 		error = gpt_write_hdr(hdr, imgsz - 1, 1, imgsz - tblsz - 1);
271 	free(hdr);
272 
273  out:
274 	free(tbl);
275 	return (error);
276 }
277 
278 static struct mkimg_scheme gpt_scheme = {
279 	.name = "gpt",
280 	.description = "GUID Partition Table",
281 	.aliases = gpt_aliases,
282 	.metadata = gpt_metadata,
283 	.write = gpt_write,
284 	.nparts = 4096,
285 	.labellen = 36,
286 	.bootcode = 512,
287 	.maxsecsz = 4096
288 };
289 
290 SCHEME_DEFINE(gpt_scheme);
291