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/param.h>
28#include <sys/errno.h>
29#include <stddef.h>
30#include <stdint.h>
31#include <stdlib.h>
32#include <string.h>
33
34#include <gpt.h>
35#include <mbr.h>
36
37#include "endian.h"
38#include "image.h"
39#include "mkimg.h"
40#include "scheme.h"
41
42static mkimg_uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
43static mkimg_uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
44static mkimg_uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
45static mkimg_uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
46static mkimg_uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
47static mkimg_uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
48static mkimg_uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
49static mkimg_uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
50static mkimg_uuid_t gpt_uuid_mbr = GPT_ENT_TYPE_MBR;
51static mkimg_uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
52static mkimg_uuid_t gpt_uuid_prep_boot = GPT_ENT_TYPE_PREP_BOOT;
53
54static struct mkimg_alias gpt_aliases[] = {
55    {	ALIAS_EFI, ALIAS_PTR2TYPE(&gpt_uuid_efi) },
56    {	ALIAS_FREEBSD, ALIAS_PTR2TYPE(&gpt_uuid_freebsd) },
57    {	ALIAS_FREEBSD_BOOT, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_boot) },
58    {	ALIAS_FREEBSD_NANDFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_nandfs) },
59    {	ALIAS_FREEBSD_SWAP, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_swap) },
60    {	ALIAS_FREEBSD_UFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_ufs) },
61    {	ALIAS_FREEBSD_VINUM, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_vinum) },
62    {	ALIAS_FREEBSD_ZFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_zfs) },
63    {	ALIAS_MBR, ALIAS_PTR2TYPE(&gpt_uuid_mbr) },
64    {	ALIAS_NTFS, ALIAS_PTR2TYPE(&gpt_uuid_ms_basic_data) },
65    {	ALIAS_PPCBOOT, ALIAS_PTR2TYPE(&gpt_uuid_prep_boot) },
66    {	ALIAS_NONE, 0 }		/* Keep last! */
67};
68
69/* CRC32 code derived from work by Gary S. Brown. */
70static 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
116static uint32_t
117crc32(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/*
128 * Return the number of sectors needed to store the partition table.
129 */
130static u_int
131gpt_tblsz(void)
132{
133	u_int eps;		/* Entries per Sector */
134
135	/*
136	 * Count the number of sectors needed for the GPT Entry Array to store
137	 * the number of partitions defined for this image.  Enforce the 16kB
138	 * minimum space for the GPT Entry Array per UEFI v2.10 Section 5.3.
139	 */
140	eps = secsz / sizeof(struct gpt_ent);
141	return (MAX(howmany(GPT_MIN_RESERVED, secsz), howmany(nparts, eps)));
142}
143
144static lba_t
145gpt_metadata(u_int where, lba_t blk)
146{
147
148	if (where == SCHEME_META_IMG_START || where == SCHEME_META_IMG_END) {
149		blk += gpt_tblsz();
150		blk += (where == SCHEME_META_IMG_START) ? 2 : 1;
151	}
152	return (round_block(blk));
153}
154
155static int
156gpt_write_pmbr(lba_t blks, void *bootcode)
157{
158	u_char *pmbr;
159	uint32_t secs;
160	int error;
161
162	secs = (blks > UINT32_MAX) ? UINT32_MAX : (uint32_t)blks - 1;
163
164	pmbr = malloc(secsz);
165	if (pmbr == NULL)
166		return (errno);
167	if (bootcode != NULL) {
168		memcpy(pmbr, bootcode, DOSPARTOFF);
169		memset(pmbr + DOSPARTOFF, 0, secsz - DOSPARTOFF);
170	} else
171		memset(pmbr, 0, secsz);
172	pmbr[DOSPARTOFF + 2] = 2;
173	pmbr[DOSPARTOFF + 4] = 0xee;
174	pmbr[DOSPARTOFF + 5] = 0xff;
175	pmbr[DOSPARTOFF + 6] = 0xff;
176	pmbr[DOSPARTOFF + 7] = 0xff;
177	le32enc(pmbr + DOSPARTOFF + 8, 1);
178	le32enc(pmbr + DOSPARTOFF + 12, secs);
179	le16enc(pmbr + DOSMAGICOFFSET, DOSMAGIC);
180	error = image_write(0, pmbr, 1);
181	free(pmbr);
182	return (error);
183}
184
185static struct gpt_ent *
186gpt_mktbl(u_int tblsz)
187{
188	mkimg_uuid_t uuid;
189	struct gpt_ent *tbl, *ent;
190	struct part *part;
191	int c, idx;
192
193	tbl = calloc(tblsz, secsz);
194	if (tbl == NULL)
195		return (NULL);
196
197	TAILQ_FOREACH(part, &partlist, link) {
198		ent = tbl + part->index;
199		mkimg_uuid_enc(&ent->ent_type, ALIAS_TYPE2PTR(part->type));
200		mkimg_uuid(&uuid);
201		mkimg_uuid_enc(&ent->ent_uuid, &uuid);
202		le64enc(&ent->ent_lba_start, part->block);
203		le64enc(&ent->ent_lba_end, part->block + part->size - 1);
204		if (part->label != NULL) {
205			idx = 0;
206			while ((c = part->label[idx]) != '\0') {
207				le16enc(ent->ent_name + idx, c);
208				idx++;
209			}
210		}
211	}
212	return (tbl);
213}
214
215static int
216gpt_write_hdr(struct gpt_hdr *hdr, uint64_t self, uint64_t alt, uint64_t tbl)
217{
218	uint32_t crc;
219
220	le64enc(&hdr->hdr_lba_self, self);
221	le64enc(&hdr->hdr_lba_alt, alt);
222	le64enc(&hdr->hdr_lba_table, tbl);
223	hdr->hdr_crc_self = 0;
224	crc = crc32(hdr, offsetof(struct gpt_hdr, padding));
225	le64enc(&hdr->hdr_crc_self, crc);
226	return (image_write(self, hdr, 1));
227}
228
229static int
230gpt_write(lba_t imgsz, void *bootcode)
231{
232	mkimg_uuid_t uuid;
233	struct gpt_ent *tbl;
234	struct gpt_hdr *hdr;
235	uint32_t crc;
236	u_int tblsz;
237	int error;
238
239	/* PMBR */
240	error = gpt_write_pmbr(imgsz, bootcode);
241	if (error)
242		return (error);
243
244	/* GPT table(s) */
245	tblsz = gpt_tblsz();
246	tbl = gpt_mktbl(tblsz);
247	if (tbl == NULL)
248		return (errno);
249	error = image_write(2, tbl, tblsz);
250	if (error)
251		goto out;
252	error = image_write(imgsz - (tblsz + 1), tbl, tblsz);
253	if (error)
254		goto out;
255
256	/* GPT header(s) */
257	hdr = malloc(secsz);
258	if (hdr == NULL) {
259		error = errno;
260		goto out;
261	}
262	memset(hdr, 0, secsz);
263	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
264	le32enc(&hdr->hdr_revision, GPT_HDR_REVISION);
265	le32enc(&hdr->hdr_size, offsetof(struct gpt_hdr, padding));
266	le64enc(&hdr->hdr_lba_start, 2 + tblsz);
267	le64enc(&hdr->hdr_lba_end, imgsz - tblsz - 2);
268	mkimg_uuid(&uuid);
269	mkimg_uuid_enc(&hdr->hdr_uuid, &uuid);
270	le32enc(&hdr->hdr_entries, tblsz * secsz / sizeof(struct gpt_ent));
271	le32enc(&hdr->hdr_entsz, sizeof(struct gpt_ent));
272	crc = crc32(tbl, tblsz * secsz);
273	le32enc(&hdr->hdr_crc_table, crc);
274	error = gpt_write_hdr(hdr, 1, imgsz - 1, 2);
275	if (!error)
276		error = gpt_write_hdr(hdr, imgsz - 1, 1, imgsz - tblsz - 1);
277	free(hdr);
278
279 out:
280	free(tbl);
281	return (error);
282}
283
284static struct mkimg_scheme gpt_scheme = {
285	.name = "gpt",
286	.description = "GUID Partition Table",
287	.aliases = gpt_aliases,
288	.metadata = gpt_metadata,
289	.write = gpt_write,
290	.nparts = 4096,
291	.labellen = 36,
292	.bootcode = 512,
293	.maxsecsz = 4096
294};
295
296SCHEME_DEFINE(gpt_scheme);
297