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