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