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