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