1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2014 Juniper Networks, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
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 static mkimg_uuid_t gpt_uuid_prep_boot = GPT_ENT_TYPE_PREP_BOOT;
55 static mkimg_uuid_t gpt_uuid_hifive_bbl = GPT_ENT_TYPE_HIFIVE_BBL;
56 static mkimg_uuid_t gpt_uuid_xbootldr = GPT_ENT_TYPE_XBOOTLDR;
57 static mkimg_uuid_t gpt_uuid_hifive_fsbl = GPT_ENT_TYPE_HIFIVE_FSBL;
58
59 static struct mkimg_alias gpt_aliases[] = {
60 { ALIAS_EFI, ALIAS_PTR2TYPE(&gpt_uuid_efi) },
61 { ALIAS_FREEBSD, ALIAS_PTR2TYPE(&gpt_uuid_freebsd) },
62 { ALIAS_FREEBSD_BOOT, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_boot) },
63 { ALIAS_FREEBSD_NANDFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_nandfs) },
64 { ALIAS_FREEBSD_SWAP, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_swap) },
65 { ALIAS_FREEBSD_UFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_ufs) },
66 { ALIAS_FREEBSD_VINUM, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_vinum) },
67 { ALIAS_FREEBSD_ZFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_zfs) },
68 { ALIAS_MBR, ALIAS_PTR2TYPE(&gpt_uuid_mbr) },
69 { ALIAS_MS_BASIC_DATA, ALIAS_PTR2TYPE(&gpt_uuid_ms_basic_data) },
70 { ALIAS_NTFS, ALIAS_PTR2TYPE(&gpt_uuid_ms_basic_data) },
71 { ALIAS_PPCBOOT, ALIAS_PTR2TYPE(&gpt_uuid_prep_boot) },
72 { ALIAS_HIFIVE_BBL, ALIAS_PTR2TYPE(&gpt_uuid_hifive_bbl) },
73 { ALIAS_XBOOTLDR, ALIAS_PTR2TYPE(&gpt_uuid_xbootldr) },
74 { ALIAS_HIFIVE_FSBL, ALIAS_PTR2TYPE(&gpt_uuid_hifive_fsbl) },
75 { ALIAS_NONE, 0 } /* Keep last! */
76 };
77
78 /* CRC32 code derived from work by Gary S. Brown. */
79 static const uint32_t crc32_tab[] = {
80 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
81 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
82 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
83 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
84 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
85 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
86 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
87 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
88 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
89 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
90 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
91 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
92 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
93 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
94 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
95 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
96 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
97 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
98 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
99 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
100 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
101 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
102 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
103 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
104 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
105 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
106 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
107 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
108 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
109 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
110 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
111 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
112 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
113 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
114 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
115 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
116 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
117 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
118 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
119 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
120 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
121 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
122 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
123 };
124
125 static uint32_t
crc32(const void * buf,size_t sz)126 crc32(const void *buf, size_t sz)
127 {
128 const uint8_t *p = (const uint8_t *)buf;
129 uint32_t crc = ~0U;
130
131 while (sz--)
132 crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
133 return (crc ^ ~0U);
134 }
135
136 /*
137 * Return the number of sectors needed to store the partition table.
138 */
139 static u_int
gpt_tblsz(void)140 gpt_tblsz(void)
141 {
142 u_int eps; /* Entries per Sector */
143
144 /*
145 * Count the number of sectors needed for the GPT Entry Array to store
146 * the number of partitions defined for this image. Enforce the 16kB
147 * minimum space for the GPT Entry Array per UEFI v2.10 Section 5.3.
148 */
149 eps = secsz / sizeof(struct gpt_ent);
150 return (MAX(howmany(GPT_MIN_RESERVED, secsz), howmany(nparts, eps)));
151 }
152
153 static lba_t
gpt_metadata(u_int where,lba_t blk)154 gpt_metadata(u_int where, lba_t blk)
155 {
156
157 if (where == SCHEME_META_IMG_START || where == SCHEME_META_IMG_END) {
158 blk += gpt_tblsz();
159 blk += (where == SCHEME_META_IMG_START) ? 2 : 1;
160 }
161 return (round_block(blk));
162 }
163
164 static int
gpt_write_pmbr(lba_t blks,void * bootcode)165 gpt_write_pmbr(lba_t blks, void *bootcode)
166 {
167 u_char *pmbr;
168 uint32_t secs;
169 int error;
170
171 secs = (blks > UINT32_MAX) ? UINT32_MAX : (uint32_t)blks - 1;
172
173 pmbr = malloc(secsz);
174 if (pmbr == NULL)
175 return (errno);
176 if (bootcode != NULL) {
177 memcpy(pmbr, bootcode, DOSPARTOFF);
178 memset(pmbr + DOSPARTOFF, 0, secsz - DOSPARTOFF);
179 } else
180 memset(pmbr, 0, secsz);
181 pmbr[DOSPARTOFF + 2] = 2;
182 pmbr[DOSPARTOFF + 4] = 0xee;
183 pmbr[DOSPARTOFF + 5] = 0xff;
184 pmbr[DOSPARTOFF + 6] = 0xff;
185 pmbr[DOSPARTOFF + 7] = 0xff;
186 le32enc(pmbr + DOSPARTOFF + 8, 1);
187 le32enc(pmbr + DOSPARTOFF + 12, secs);
188 le16enc(pmbr + DOSMAGICOFFSET, DOSMAGIC);
189 error = image_write(0, pmbr, 1);
190 free(pmbr);
191 return (error);
192 }
193
194 static struct gpt_ent *
gpt_mktbl(u_int tblsz)195 gpt_mktbl(u_int tblsz)
196 {
197 mkimg_uuid_t uuid;
198 struct gpt_ent *tbl, *ent;
199 struct part *part;
200 int c, idx;
201
202 tbl = calloc(tblsz, secsz);
203 if (tbl == NULL)
204 return (NULL);
205
206 TAILQ_FOREACH(part, &partlist, link) {
207 ent = tbl + part->index;
208 mkimg_uuid_enc(&ent->ent_type, ALIAS_TYPE2PTR(part->type));
209 mkimg_uuid(&uuid);
210 mkimg_uuid_enc(&ent->ent_uuid, &uuid);
211 le64enc(&ent->ent_lba_start, part->block);
212 le64enc(&ent->ent_lba_end, part->block + part->size - 1);
213 if (part->label != NULL) {
214 idx = 0;
215 while ((c = part->label[idx]) != '\0') {
216 le16enc(ent->ent_name + idx, c);
217 idx++;
218 }
219 }
220 }
221 return (tbl);
222 }
223
224 static int
gpt_write_hdr(struct gpt_hdr * hdr,uint64_t self,uint64_t alt,uint64_t tbl)225 gpt_write_hdr(struct gpt_hdr *hdr, uint64_t self, uint64_t alt, uint64_t tbl)
226 {
227 uint32_t crc;
228
229 le64enc(&hdr->hdr_lba_self, self);
230 le64enc(&hdr->hdr_lba_alt, alt);
231 le64enc(&hdr->hdr_lba_table, tbl);
232 hdr->hdr_crc_self = 0;
233 crc = crc32(hdr, offsetof(struct gpt_hdr, padding));
234 le64enc(&hdr->hdr_crc_self, crc);
235 return (image_write(self, hdr, 1));
236 }
237
238 static int
gpt_write(lba_t imgsz,void * bootcode)239 gpt_write(lba_t imgsz, void *bootcode)
240 {
241 mkimg_uuid_t uuid;
242 struct gpt_ent *tbl;
243 struct gpt_hdr *hdr;
244 uint32_t crc;
245 u_int tblsz;
246 int error;
247
248 /* PMBR */
249 error = gpt_write_pmbr(imgsz, bootcode);
250 if (error)
251 return (error);
252
253 /* GPT table(s) */
254 tblsz = gpt_tblsz();
255 tbl = gpt_mktbl(tblsz);
256 if (tbl == NULL)
257 return (errno);
258 error = image_write(2, tbl, tblsz);
259 if (error)
260 goto out;
261 error = image_write(imgsz - (tblsz + 1), tbl, tblsz);
262 if (error)
263 goto out;
264
265 /* GPT header(s) */
266 hdr = malloc(secsz);
267 if (hdr == NULL) {
268 error = errno;
269 goto out;
270 }
271 memset(hdr, 0, secsz);
272 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
273 le32enc(&hdr->hdr_revision, GPT_HDR_REVISION);
274 le32enc(&hdr->hdr_size, offsetof(struct gpt_hdr, padding));
275 le64enc(&hdr->hdr_lba_start, 2 + tblsz);
276 le64enc(&hdr->hdr_lba_end, imgsz - tblsz - 2);
277 mkimg_uuid(&uuid);
278 mkimg_uuid_enc(&hdr->hdr_uuid, &uuid);
279 le32enc(&hdr->hdr_entries, tblsz * secsz / sizeof(struct gpt_ent));
280 le32enc(&hdr->hdr_entsz, sizeof(struct gpt_ent));
281 crc = crc32(tbl, tblsz * secsz);
282 le32enc(&hdr->hdr_crc_table, crc);
283 error = gpt_write_hdr(hdr, 1, imgsz - 1, 2);
284 if (!error)
285 error = gpt_write_hdr(hdr, imgsz - 1, 1, imgsz - tblsz - 1);
286 free(hdr);
287
288 out:
289 free(tbl);
290 return (error);
291 }
292
293 static struct mkimg_scheme gpt_scheme = {
294 .name = "gpt",
295 .description = "GUID Partition Table",
296 .aliases = gpt_aliases,
297 .metadata = gpt_metadata,
298 .write = gpt_write,
299 .nparts = 4096,
300 .labellen = 36,
301 .bootcode = 512,
302 .maxsecsz = 4096
303 };
304
305 SCHEME_DEFINE(gpt_scheme);
306