]>
Commit | Line | Data |
---|---|---|
5c363129 BB |
1 | /* |
2 | * CDDL HEADER START | |
3 | * | |
4 | * The contents of this file are subject to the terms of the | |
5 | * Common Development and Distribution License (the "License"). | |
6 | * You may not use this file except in compliance with the License. | |
7 | * | |
8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
9 | * or http://www.opensolaris.org/os/licensing. | |
10 | * See the License for the specific language governing permissions | |
11 | * and limitations under the License. | |
12 | * | |
13 | * When distributing Covered Code, include this CDDL HEADER in each | |
14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
15 | * If applicable, add the following below this CDDL HEADER, with the | |
16 | * fields enclosed by brackets "[]" replaced with your own identifying | |
17 | * information: Portions Copyright [yyyy] [name of copyright owner] | |
18 | * | |
19 | * CDDL HEADER END | |
20 | */ | |
21 | ||
22 | /* | |
572e2857 | 23 | * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. |
fc803849 | 24 | * Copyright 2012 Nexenta Systems, Inc. All rights reserved. |
74d42600 | 25 | * Copyright (c) 2018 by Delphix. All rights reserved. |
5c363129 BB |
26 | */ |
27 | ||
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <errno.h> | |
93ce2b4c | 31 | #include <string.h> |
5c363129 BB |
32 | #include <strings.h> |
33 | #include <unistd.h> | |
34 | #include <uuid/uuid.h> | |
d603ed6c | 35 | #include <zlib.h> |
5c363129 BB |
36 | #include <libintl.h> |
37 | #include <sys/types.h> | |
38 | #include <sys/dkio.h> | |
39 | #include <sys/vtoc.h> | |
40 | #include <sys/mhd.h> | |
41 | #include <sys/param.h> | |
42 | #include <sys/dktp/fdisk.h> | |
43 | #include <sys/efi_partition.h> | |
44 | #include <sys/byteorder.h> | |
475ebd76 | 45 | #include <sys/vdev_disk.h> |
d603ed6c | 46 | #include <linux/fs.h> |
8e82ffba | 47 | #include <linux/blkpg.h> |
5c363129 BB |
48 | |
49 | static struct uuid_to_ptag { | |
50 | struct uuid uuid; | |
51 | } conversion_array[] = { | |
52 | { EFI_UNUSED }, | |
53 | { EFI_BOOT }, | |
54 | { EFI_ROOT }, | |
55 | { EFI_SWAP }, | |
56 | { EFI_USR }, | |
57 | { EFI_BACKUP }, | |
d603ed6c | 58 | { EFI_UNUSED }, /* STAND is never used */ |
5c363129 BB |
59 | { EFI_VAR }, |
60 | { EFI_HOME }, | |
61 | { EFI_ALTSCTR }, | |
d603ed6c | 62 | { EFI_UNUSED }, /* CACHE (cachefs) is never used */ |
5c363129 BB |
63 | { EFI_RESERVED }, |
64 | { EFI_SYSTEM }, | |
65 | { EFI_LEGACY_MBR }, | |
572e2857 BB |
66 | { EFI_SYMC_PUB }, |
67 | { EFI_SYMC_CDS }, | |
5c363129 BB |
68 | { EFI_MSFT_RESV }, |
69 | { EFI_DELL_BASIC }, | |
70 | { EFI_DELL_RAID }, | |
71 | { EFI_DELL_SWAP }, | |
72 | { EFI_DELL_LVM }, | |
73 | { EFI_DELL_RESV }, | |
74 | { EFI_AAPL_HFS }, | |
fc803849 YP |
75 | { EFI_AAPL_UFS }, |
76 | { EFI_FREEBSD_BOOT }, | |
77 | { EFI_FREEBSD_SWAP }, | |
78 | { EFI_FREEBSD_UFS }, | |
79 | { EFI_FREEBSD_VINUM }, | |
917b8c5c | 80 | { EFI_FREEBSD_ZFS }, |
81 | { EFI_BIOS_BOOT }, | |
82 | { EFI_INTC_RS }, | |
83 | { EFI_SNE_BOOT }, | |
84 | { EFI_LENOVO_BOOT }, | |
85 | { EFI_MSFT_LDMM }, | |
86 | { EFI_MSFT_LDMD }, | |
87 | { EFI_MSFT_RE }, | |
88 | { EFI_IBM_GPFS }, | |
89 | { EFI_MSFT_STORAGESPACES }, | |
90 | { EFI_HPQ_DATA }, | |
91 | { EFI_HPQ_SVC }, | |
92 | { EFI_RHT_DATA }, | |
93 | { EFI_RHT_HOME }, | |
94 | { EFI_RHT_SRV }, | |
95 | { EFI_RHT_DMCRYPT }, | |
96 | { EFI_RHT_LUKS }, | |
97 | { EFI_FREEBSD_DISKLABEL }, | |
98 | { EFI_AAPL_RAID }, | |
99 | { EFI_AAPL_RAIDOFFLINE }, | |
100 | { EFI_AAPL_BOOT }, | |
101 | { EFI_AAPL_LABEL }, | |
102 | { EFI_AAPL_TVRECOVERY }, | |
103 | { EFI_AAPL_CORESTORAGE }, | |
104 | { EFI_NETBSD_SWAP }, | |
105 | { EFI_NETBSD_FFS }, | |
106 | { EFI_NETBSD_LFS }, | |
107 | { EFI_NETBSD_RAID }, | |
108 | { EFI_NETBSD_CAT }, | |
109 | { EFI_NETBSD_CRYPT }, | |
110 | { EFI_GOOG_KERN }, | |
111 | { EFI_GOOG_ROOT }, | |
112 | { EFI_GOOG_RESV }, | |
113 | { EFI_HAIKU_BFS }, | |
114 | { EFI_MIDNIGHTBSD_BOOT }, | |
115 | { EFI_MIDNIGHTBSD_DATA }, | |
116 | { EFI_MIDNIGHTBSD_SWAP }, | |
117 | { EFI_MIDNIGHTBSD_UFS }, | |
118 | { EFI_MIDNIGHTBSD_VINUM }, | |
119 | { EFI_MIDNIGHTBSD_ZFS }, | |
120 | { EFI_CEPH_JOURNAL }, | |
121 | { EFI_CEPH_DMCRYPTJOURNAL }, | |
122 | { EFI_CEPH_OSD }, | |
123 | { EFI_CEPH_DMCRYPTOSD }, | |
124 | { EFI_CEPH_CREATE }, | |
125 | { EFI_CEPH_DMCRYPTCREATE }, | |
126 | { EFI_OPENBSD_DISKLABEL }, | |
127 | { EFI_BBRY_QNX }, | |
128 | { EFI_BELL_PLAN9 }, | |
129 | { EFI_VMW_KCORE }, | |
130 | { EFI_VMW_VMFS }, | |
131 | { EFI_VMW_RESV }, | |
132 | { EFI_RHT_ROOTX86 }, | |
133 | { EFI_RHT_ROOTAMD64 }, | |
134 | { EFI_RHT_ROOTARM }, | |
135 | { EFI_RHT_ROOTARM64 }, | |
136 | { EFI_ACRONIS_SECUREZONE }, | |
137 | { EFI_ONIE_BOOT }, | |
138 | { EFI_ONIE_CONFIG }, | |
139 | { EFI_IBM_PPRPBOOT }, | |
140 | { EFI_FREEDESKTOP_BOOT } | |
5c363129 BB |
141 | }; |
142 | ||
5c363129 | 143 | int efi_debug = 0; |
5c363129 | 144 | |
d603ed6c BB |
145 | static int efi_read(int, struct dk_gpt *); |
146 | ||
147 | /* | |
148 | * Return a 32-bit CRC of the contents of the buffer. Pre-and-post | |
149 | * one's conditioning will be handled by crc32() internally. | |
150 | */ | |
151 | static uint32_t | |
152 | efi_crc32(const unsigned char *buf, unsigned int size) | |
153 | { | |
154 | uint32_t crc = crc32(0, Z_NULL, 0); | |
155 | ||
156 | crc = crc32(crc, buf, size); | |
157 | ||
158 | return (crc); | |
159 | } | |
5c363129 BB |
160 | |
161 | static int | |
162 | read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize) | |
163 | { | |
d603ed6c BB |
164 | int sector_size; |
165 | unsigned long long capacity_size; | |
166 | ||
d1d7e268 MK |
167 | if (ioctl(fd, BLKSSZGET, §or_size) < 0) |
168 | return (-1); | |
d603ed6c BB |
169 | |
170 | if (ioctl(fd, BLKGETSIZE64, &capacity_size) < 0) | |
171 | return (-1); | |
172 | ||
173 | *lbsize = (uint_t)sector_size; | |
174 | *capacity = (diskaddr_t)(capacity_size / sector_size); | |
175 | ||
176 | return (0); | |
177 | } | |
5c363129 | 178 | |
8e82ffba GW |
179 | /* |
180 | * Return back the device name associated with the file descriptor. The | |
181 | * caller is responsible for freeing the memory associated with the | |
182 | * returned string. | |
183 | */ | |
184 | static char * | |
185 | efi_get_devname(int fd) | |
186 | { | |
e00aae4b | 187 | char path[32]; |
8e82ffba GW |
188 | |
189 | /* | |
190 | * The libefi API only provides the open fd and not the file path. | |
191 | * To handle this realpath(3) is used to resolve the block device | |
192 | * name from /proc/self/fd/<fd>. | |
193 | */ | |
e00aae4b AZ |
194 | (void) snprintf(path, sizeof (path), "/proc/self/fd/%d", fd); |
195 | return (realpath(path, NULL)); | |
8e82ffba GW |
196 | } |
197 | ||
d603ed6c BB |
198 | static int |
199 | efi_get_info(int fd, struct dk_cinfo *dki_info) | |
200 | { | |
d603ed6c BB |
201 | char *dev_path; |
202 | int rval = 0; | |
203 | ||
d1d7e268 | 204 | memset(dki_info, 0, sizeof (*dki_info)); |
d603ed6c | 205 | |
d603ed6c BB |
206 | /* |
207 | * The simplest way to get the partition number under linux is | |
78595377 | 208 | * to parse it out of the /dev/<disk><partition> block device name. |
d603ed6c BB |
209 | * The kernel creates this using the partition number when it |
210 | * populates /dev/ so it may be trusted. The tricky bit here is | |
211 | * that the naming convention is based on the block device type. | |
212 | * So we need to take this in to account when parsing out the | |
8e82ffba | 213 | * partition information. Aside from the partition number we collect |
d603ed6c BB |
214 | * some additional device info. |
215 | */ | |
8e82ffba | 216 | dev_path = efi_get_devname(fd); |
d603ed6c BB |
217 | if (dev_path == NULL) |
218 | goto error; | |
219 | ||
220 | if ((strncmp(dev_path, "/dev/sd", 7) == 0)) { | |
221 | strcpy(dki_info->dki_cname, "sd"); | |
222 | dki_info->dki_ctype = DKC_SCSI_CCS; | |
223 | rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu", | |
d1d7e268 MK |
224 | dki_info->dki_dname, |
225 | &dki_info->dki_partition); | |
d603ed6c BB |
226 | } else if ((strncmp(dev_path, "/dev/hd", 7) == 0)) { |
227 | strcpy(dki_info->dki_cname, "hd"); | |
228 | dki_info->dki_ctype = DKC_DIRECT; | |
229 | rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu", | |
d1d7e268 MK |
230 | dki_info->dki_dname, |
231 | &dki_info->dki_partition); | |
d603ed6c BB |
232 | } else if ((strncmp(dev_path, "/dev/md", 7) == 0)) { |
233 | strcpy(dki_info->dki_cname, "pseudo"); | |
234 | dki_info->dki_ctype = DKC_MD; | |
787c455e RY |
235 | strcpy(dki_info->dki_dname, "md"); |
236 | rval = sscanf(dev_path, "/dev/md%[0-9]p%hu", | |
237 | dki_info->dki_dname + 2, | |
d1d7e268 | 238 | &dki_info->dki_partition); |
2932b6a8 RL |
239 | } else if ((strncmp(dev_path, "/dev/vd", 7) == 0)) { |
240 | strcpy(dki_info->dki_cname, "vd"); | |
241 | dki_info->dki_ctype = DKC_MD; | |
242 | rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu", | |
d1d7e268 MK |
243 | dki_info->dki_dname, |
244 | &dki_info->dki_partition); | |
541da993 RY |
245 | } else if ((strncmp(dev_path, "/dev/xvd", 8) == 0)) { |
246 | strcpy(dki_info->dki_cname, "xvd"); | |
247 | dki_info->dki_ctype = DKC_MD; | |
248 | rval = sscanf(dev_path, "/dev/%[a-zA-Z]%hu", | |
249 | dki_info->dki_dname, | |
250 | &dki_info->dki_partition); | |
9f5ba90f RY |
251 | } else if ((strncmp(dev_path, "/dev/zd", 7) == 0)) { |
252 | strcpy(dki_info->dki_cname, "zd"); | |
253 | dki_info->dki_ctype = DKC_MD; | |
232dd8b9 BB |
254 | strcpy(dki_info->dki_dname, "zd"); |
255 | rval = sscanf(dev_path, "/dev/zd%[0-9]p%hu", | |
256 | dki_info->dki_dname + 2, | |
9f5ba90f | 257 | &dki_info->dki_partition); |
d603ed6c BB |
258 | } else if ((strncmp(dev_path, "/dev/dm-", 8) == 0)) { |
259 | strcpy(dki_info->dki_cname, "pseudo"); | |
260 | dki_info->dki_ctype = DKC_VBD; | |
787c455e RY |
261 | strcpy(dki_info->dki_dname, "dm-"); |
262 | rval = sscanf(dev_path, "/dev/dm-%[0-9]p%hu", | |
263 | dki_info->dki_dname + 3, | |
d1d7e268 | 264 | &dki_info->dki_partition); |
d603ed6c BB |
265 | } else if ((strncmp(dev_path, "/dev/ram", 8) == 0)) { |
266 | strcpy(dki_info->dki_cname, "pseudo"); | |
267 | dki_info->dki_ctype = DKC_PCMCIA_MEM; | |
787c455e RY |
268 | strcpy(dki_info->dki_dname, "ram"); |
269 | rval = sscanf(dev_path, "/dev/ram%[0-9]p%hu", | |
270 | dki_info->dki_dname + 3, | |
d1d7e268 | 271 | &dki_info->dki_partition); |
d603ed6c BB |
272 | } else if ((strncmp(dev_path, "/dev/loop", 9) == 0)) { |
273 | strcpy(dki_info->dki_cname, "pseudo"); | |
274 | dki_info->dki_ctype = DKC_VBD; | |
787c455e RY |
275 | strcpy(dki_info->dki_dname, "loop"); |
276 | rval = sscanf(dev_path, "/dev/loop%[0-9]p%hu", | |
277 | dki_info->dki_dname + 4, | |
d1d7e268 | 278 | &dki_info->dki_partition); |
c66e54e9 | 279 | } else if ((strncmp(dev_path, "/dev/nvme", 9) == 0)) { |
280 | strcpy(dki_info->dki_cname, "nvme"); | |
281 | dki_info->dki_ctype = DKC_SCSI_CCS; | |
282 | strcpy(dki_info->dki_dname, "nvme"); | |
283 | (void) sscanf(dev_path, "/dev/nvme%[0-9]", | |
284 | dki_info->dki_dname + 4); | |
285 | size_t controller_length = strlen( | |
286 | dki_info->dki_dname); | |
287 | strcpy(dki_info->dki_dname + controller_length, | |
288 | "n"); | |
289 | rval = sscanf(dev_path, | |
290 | "/dev/nvme%*[0-9]n%[0-9]p%hu", | |
291 | dki_info->dki_dname + controller_length + 1, | |
292 | &dki_info->dki_partition); | |
d603ed6c BB |
293 | } else { |
294 | strcpy(dki_info->dki_dname, "unknown"); | |
295 | strcpy(dki_info->dki_cname, "unknown"); | |
296 | dki_info->dki_ctype = DKC_UNKNOWN; | |
297 | } | |
298 | ||
299 | switch (rval) { | |
300 | case 0: | |
301 | errno = EINVAL; | |
302 | goto error; | |
303 | case 1: | |
304 | dki_info->dki_partition = 0; | |
305 | } | |
306 | ||
307 | free(dev_path); | |
7e0594a3 | 308 | |
5c363129 | 309 | return (0); |
d603ed6c BB |
310 | error: |
311 | if (efi_debug) | |
312 | (void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno); | |
313 | ||
314 | switch (errno) { | |
315 | case EIO: | |
316 | return (VT_EIO); | |
317 | case EINVAL: | |
318 | return (VT_EINVAL); | |
319 | default: | |
320 | return (VT_ERROR); | |
321 | } | |
5c363129 BB |
322 | } |
323 | ||
324 | /* | |
325 | * the number of blocks the EFI label takes up (round up to nearest | |
326 | * block) | |
327 | */ | |
328 | #define NBLOCKS(p, l) (1 + ((((p) * (int)sizeof (efi_gpe_t)) + \ | |
329 | ((l) - 1)) / (l))) | |
330 | /* number of partitions -- limited by what we can malloc */ | |
331 | #define MAX_PARTS ((4294967295UL - sizeof (struct dk_gpt)) / \ | |
332 | sizeof (struct dk_part)) | |
333 | ||
334 | int | |
335 | efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc) | |
336 | { | |
d603ed6c BB |
337 | diskaddr_t capacity = 0; |
338 | uint_t lbsize = 0; | |
5c363129 BB |
339 | uint_t nblocks; |
340 | size_t length; | |
341 | struct dk_gpt *vptr; | |
342 | struct uuid uuid; | |
d603ed6c | 343 | struct dk_cinfo dki_info; |
5c363129 | 344 | |
b3b4f547 | 345 | if (read_disk_info(fd, &capacity, &lbsize) != 0) |
5c363129 | 346 | return (-1); |
b3b4f547 | 347 | |
b3b4f547 | 348 | if (efi_get_info(fd, &dki_info) != 0) |
d603ed6c | 349 | return (-1); |
d603ed6c BB |
350 | |
351 | if (dki_info.dki_partition != 0) | |
352 | return (-1); | |
353 | ||
354 | if ((dki_info.dki_ctype == DKC_PCMCIA_MEM) || | |
355 | (dki_info.dki_ctype == DKC_VBD) || | |
356 | (dki_info.dki_ctype == DKC_UNKNOWN)) | |
357 | return (-1); | |
5c363129 BB |
358 | |
359 | nblocks = NBLOCKS(nparts, lbsize); | |
360 | if ((nblocks * lbsize) < EFI_MIN_ARRAY_SIZE + lbsize) { | |
361 | /* 16K plus one block for the GPT */ | |
362 | nblocks = EFI_MIN_ARRAY_SIZE / lbsize + 1; | |
363 | } | |
364 | ||
365 | if (nparts > MAX_PARTS) { | |
366 | if (efi_debug) { | |
367 | (void) fprintf(stderr, | |
368 | "the maximum number of partitions supported is %lu\n", | |
369 | MAX_PARTS); | |
370 | } | |
371 | return (-1); | |
372 | } | |
373 | ||
374 | length = sizeof (struct dk_gpt) + | |
375 | sizeof (struct dk_part) * (nparts - 1); | |
376 | ||
fe20400d BB |
377 | vptr = calloc(1, length); |
378 | if (vptr == NULL) | |
5c363129 BB |
379 | return (-1); |
380 | ||
fe20400d | 381 | *vtoc = vptr; |
5c363129 BB |
382 | |
383 | vptr->efi_version = EFI_VERSION_CURRENT; | |
384 | vptr->efi_lbasize = lbsize; | |
385 | vptr->efi_nparts = nparts; | |
386 | /* | |
387 | * add one block here for the PMBR; on disks with a 512 byte | |
388 | * block size and 128 or fewer partitions, efi_first_u_lba | |
389 | * should work out to "34" | |
390 | */ | |
391 | vptr->efi_first_u_lba = nblocks + 1; | |
392 | vptr->efi_last_lba = capacity - 1; | |
393 | vptr->efi_altern_lba = capacity -1; | |
394 | vptr->efi_last_u_lba = vptr->efi_last_lba - nblocks; | |
395 | ||
396 | (void) uuid_generate((uchar_t *)&uuid); | |
397 | UUID_LE_CONVERT(vptr->efi_disk_uguid, uuid); | |
398 | return (0); | |
399 | } | |
400 | ||
401 | /* | |
402 | * Read EFI - return partition number upon success. | |
403 | */ | |
404 | int | |
405 | efi_alloc_and_read(int fd, struct dk_gpt **vtoc) | |
406 | { | |
407 | int rval; | |
408 | uint32_t nparts; | |
409 | int length; | |
fe20400d | 410 | struct dk_gpt *vptr; |
5c363129 BB |
411 | |
412 | /* figure out the number of entries that would fit into 16K */ | |
413 | nparts = EFI_MIN_ARRAY_SIZE / sizeof (efi_gpe_t); | |
414 | length = (int) sizeof (struct dk_gpt) + | |
415 | (int) sizeof (struct dk_part) * (nparts - 1); | |
fe20400d BB |
416 | vptr = calloc(1, length); |
417 | ||
418 | if (vptr == NULL) | |
5c363129 BB |
419 | return (VT_ERROR); |
420 | ||
fe20400d BB |
421 | vptr->efi_nparts = nparts; |
422 | rval = efi_read(fd, vptr); | |
5c363129 | 423 | |
fe20400d | 424 | if ((rval == VT_EINVAL) && vptr->efi_nparts > nparts) { |
5c363129 BB |
425 | void *tmp; |
426 | length = (int) sizeof (struct dk_gpt) + | |
fe20400d BB |
427 | (int) sizeof (struct dk_part) * (vptr->efi_nparts - 1); |
428 | nparts = vptr->efi_nparts; | |
429 | if ((tmp = realloc(vptr, length)) == NULL) { | |
6fc1ce07 | 430 | /* cppcheck-suppress doubleFree */ |
fe20400d | 431 | free(vptr); |
5c363129 BB |
432 | *vtoc = NULL; |
433 | return (VT_ERROR); | |
434 | } else { | |
fe20400d BB |
435 | vptr = tmp; |
436 | rval = efi_read(fd, vptr); | |
5c363129 BB |
437 | } |
438 | } | |
439 | ||
440 | if (rval < 0) { | |
441 | if (efi_debug) { | |
442 | (void) fprintf(stderr, | |
443 | "read of EFI table failed, rval=%d\n", rval); | |
444 | } | |
fe20400d | 445 | free(vptr); |
5c363129 | 446 | *vtoc = NULL; |
fe20400d BB |
447 | } else { |
448 | *vtoc = vptr; | |
5c363129 BB |
449 | } |
450 | ||
451 | return (rval); | |
452 | } | |
453 | ||
454 | static int | |
455 | efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) | |
456 | { | |
457 | void *data = dk_ioc->dki_data; | |
458 | int error; | |
d603ed6c BB |
459 | diskaddr_t capacity; |
460 | uint_t lbsize; | |
461 | ||
462 | /* | |
463 | * When the IO is not being performed in kernel as an ioctl we need | |
464 | * to know the sector size so we can seek to the proper byte offset. | |
465 | */ | |
466 | if (read_disk_info(fd, &capacity, &lbsize) == -1) { | |
467 | if (efi_debug) | |
d1d7e268 | 468 | fprintf(stderr, "unable to read disk info: %d", errno); |
d603ed6c BB |
469 | |
470 | errno = EIO; | |
d1d7e268 | 471 | return (-1); |
d603ed6c BB |
472 | } |
473 | ||
474 | switch (cmd) { | |
475 | case DKIOCGETEFI: | |
476 | if (lbsize == 0) { | |
477 | if (efi_debug) | |
478 | (void) fprintf(stderr, "DKIOCGETEFI assuming " | |
d1d7e268 | 479 | "LBA %d bytes\n", DEV_BSIZE); |
d603ed6c BB |
480 | |
481 | lbsize = DEV_BSIZE; | |
482 | } | |
483 | ||
484 | error = lseek(fd, dk_ioc->dki_lba * lbsize, SEEK_SET); | |
485 | if (error == -1) { | |
486 | if (efi_debug) | |
487 | (void) fprintf(stderr, "DKIOCGETEFI lseek " | |
d1d7e268 MK |
488 | "error: %d\n", errno); |
489 | return (error); | |
d603ed6c BB |
490 | } |
491 | ||
492 | error = read(fd, data, dk_ioc->dki_length); | |
493 | if (error == -1) { | |
494 | if (efi_debug) | |
495 | (void) fprintf(stderr, "DKIOCGETEFI read " | |
d1d7e268 MK |
496 | "error: %d\n", errno); |
497 | return (error); | |
d603ed6c | 498 | } |
5c363129 | 499 | |
d603ed6c BB |
500 | if (error != dk_ioc->dki_length) { |
501 | if (efi_debug) | |
502 | (void) fprintf(stderr, "DKIOCGETEFI short " | |
d1d7e268 | 503 | "read of %d bytes\n", error); |
d603ed6c | 504 | errno = EIO; |
d1d7e268 | 505 | return (-1); |
d603ed6c BB |
506 | } |
507 | error = 0; | |
508 | break; | |
509 | ||
510 | case DKIOCSETEFI: | |
511 | if (lbsize == 0) { | |
512 | if (efi_debug) | |
513 | (void) fprintf(stderr, "DKIOCSETEFI unknown " | |
d1d7e268 | 514 | "LBA size\n"); |
d603ed6c | 515 | errno = EIO; |
d1d7e268 | 516 | return (-1); |
d603ed6c BB |
517 | } |
518 | ||
519 | error = lseek(fd, dk_ioc->dki_lba * lbsize, SEEK_SET); | |
520 | if (error == -1) { | |
521 | if (efi_debug) | |
522 | (void) fprintf(stderr, "DKIOCSETEFI lseek " | |
d1d7e268 MK |
523 | "error: %d\n", errno); |
524 | return (error); | |
d603ed6c BB |
525 | } |
526 | ||
527 | error = write(fd, data, dk_ioc->dki_length); | |
528 | if (error == -1) { | |
529 | if (efi_debug) | |
530 | (void) fprintf(stderr, "DKIOCSETEFI write " | |
d1d7e268 MK |
531 | "error: %d\n", errno); |
532 | return (error); | |
d603ed6c BB |
533 | } |
534 | ||
535 | if (error != dk_ioc->dki_length) { | |
536 | if (efi_debug) | |
537 | (void) fprintf(stderr, "DKIOCSETEFI short " | |
d1d7e268 | 538 | "write of %d bytes\n", error); |
d603ed6c | 539 | errno = EIO; |
d1d7e268 | 540 | return (-1); |
d603ed6c BB |
541 | } |
542 | ||
543 | /* Sync the new EFI table to disk */ | |
544 | error = fsync(fd); | |
545 | if (error == -1) | |
d1d7e268 | 546 | return (error); |
d603ed6c BB |
547 | |
548 | /* Ensure any local disk cache is also flushed */ | |
549 | if (ioctl(fd, BLKFLSBUF, 0) == -1) | |
d1d7e268 | 550 | return (error); |
d603ed6c BB |
551 | |
552 | error = 0; | |
553 | break; | |
554 | ||
555 | default: | |
556 | if (efi_debug) | |
557 | (void) fprintf(stderr, "unsupported ioctl()\n"); | |
558 | ||
559 | errno = EIO; | |
d1d7e268 | 560 | return (-1); |
d603ed6c | 561 | } |
7e0594a3 | 562 | |
5c363129 BB |
563 | return (error); |
564 | } | |
565 | ||
d1d7e268 MK |
566 | int |
567 | efi_rescan(int fd) | |
d603ed6c | 568 | { |
d09a99f9 | 569 | int retry = 10; |
d603ed6c BB |
570 | int error; |
571 | ||
572 | /* Notify the kernel a devices partition table has been updated */ | |
573 | while ((error = ioctl(fd, BLKRRPART)) != 0) { | |
d09a99f9 | 574 | if ((--retry == 0) || (errno != EBUSY)) { |
d603ed6c | 575 | (void) fprintf(stderr, "the kernel failed to rescan " |
d1d7e268 | 576 | "the partition table: %d\n", errno); |
d603ed6c BB |
577 | return (-1); |
578 | } | |
d09a99f9 | 579 | usleep(50000); |
d603ed6c BB |
580 | } |
581 | ||
582 | return (0); | |
583 | } | |
d603ed6c | 584 | |
5c363129 BB |
585 | static int |
586 | check_label(int fd, dk_efi_t *dk_ioc) | |
587 | { | |
588 | efi_gpt_t *efi; | |
589 | uint_t crc; | |
590 | ||
591 | if (efi_ioctl(fd, DKIOCGETEFI, dk_ioc) == -1) { | |
592 | switch (errno) { | |
593 | case EIO: | |
594 | return (VT_EIO); | |
595 | default: | |
596 | return (VT_ERROR); | |
597 | } | |
598 | } | |
599 | efi = dk_ioc->dki_data; | |
600 | if (efi->efi_gpt_Signature != LE_64(EFI_SIGNATURE)) { | |
601 | if (efi_debug) | |
602 | (void) fprintf(stderr, | |
603 | "Bad EFI signature: 0x%llx != 0x%llx\n", | |
604 | (long long)efi->efi_gpt_Signature, | |
605 | (long long)LE_64(EFI_SIGNATURE)); | |
606 | return (VT_EINVAL); | |
607 | } | |
608 | ||
609 | /* | |
610 | * check CRC of the header; the size of the header should | |
611 | * never be larger than one block | |
612 | */ | |
613 | crc = efi->efi_gpt_HeaderCRC32; | |
614 | efi->efi_gpt_HeaderCRC32 = 0; | |
7a023273 | 615 | len_t headerSize = (len_t)LE_32(efi->efi_gpt_HeaderSize); |
5c363129 | 616 | |
d1d7e268 | 617 | if (headerSize < EFI_MIN_LABEL_SIZE || headerSize > EFI_LABEL_SIZE) { |
7a023273 ZB |
618 | if (efi_debug) |
619 | (void) fprintf(stderr, | |
25df831b | 620 | "Invalid EFI HeaderSize %llu. Assuming %d.\n", |
621 | headerSize, EFI_MIN_LABEL_SIZE); | |
7a023273 ZB |
622 | } |
623 | ||
624 | if ((headerSize > dk_ioc->dki_length) || | |
625 | crc != LE_32(efi_crc32((unsigned char *)efi, headerSize))) { | |
5c363129 BB |
626 | if (efi_debug) |
627 | (void) fprintf(stderr, | |
628 | "Bad EFI CRC: 0x%x != 0x%x\n", | |
7a023273 ZB |
629 | crc, LE_32(efi_crc32((unsigned char *)efi, |
630 | headerSize))); | |
5c363129 BB |
631 | return (VT_EINVAL); |
632 | } | |
633 | ||
634 | return (0); | |
635 | } | |
636 | ||
637 | static int | |
638 | efi_read(int fd, struct dk_gpt *vtoc) | |
639 | { | |
640 | int i, j; | |
641 | int label_len; | |
642 | int rval = 0; | |
643 | int md_flag = 0; | |
644 | int vdc_flag = 0; | |
d603ed6c BB |
645 | diskaddr_t capacity = 0; |
646 | uint_t lbsize = 0; | |
5c363129 BB |
647 | struct dk_minfo disk_info; |
648 | dk_efi_t dk_ioc; | |
649 | efi_gpt_t *efi; | |
650 | efi_gpe_t *efi_parts; | |
651 | struct dk_cinfo dki_info; | |
652 | uint32_t user_length; | |
653 | boolean_t legacy_label = B_FALSE; | |
654 | ||
655 | /* | |
656 | * get the partition number for this file descriptor. | |
657 | */ | |
d603ed6c | 658 | if ((rval = efi_get_info(fd, &dki_info)) != 0) |
d1d7e268 | 659 | return (rval); |
d603ed6c | 660 | |
5c363129 BB |
661 | if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) && |
662 | (strncmp(dki_info.dki_dname, "md", 3) == 0)) { | |
663 | md_flag++; | |
664 | } else if ((strncmp(dki_info.dki_cname, "vdc", 4) == 0) && | |
665 | (strncmp(dki_info.dki_dname, "vdc", 4) == 0)) { | |
666 | /* | |
667 | * The controller and drive name "vdc" (virtual disk client) | |
668 | * indicates a LDoms virtual disk. | |
669 | */ | |
670 | vdc_flag++; | |
671 | } | |
672 | ||
673 | /* get the LBA size */ | |
d603ed6c | 674 | if (read_disk_info(fd, &capacity, &lbsize) == -1) { |
5c363129 BB |
675 | if (efi_debug) { |
676 | (void) fprintf(stderr, | |
d1d7e268 MK |
677 | "unable to read disk info: %d", |
678 | errno); | |
5c363129 | 679 | } |
d603ed6c | 680 | return (VT_EINVAL); |
5c363129 | 681 | } |
d603ed6c BB |
682 | |
683 | disk_info.dki_lbsize = lbsize; | |
684 | disk_info.dki_capacity = capacity; | |
685 | ||
5c363129 BB |
686 | if (disk_info.dki_lbsize == 0) { |
687 | if (efi_debug) { | |
688 | (void) fprintf(stderr, | |
689 | "efi_read: assuming LBA 512 bytes\n"); | |
690 | } | |
691 | disk_info.dki_lbsize = DEV_BSIZE; | |
692 | } | |
693 | /* | |
694 | * Read the EFI GPT to figure out how many partitions we need | |
695 | * to deal with. | |
696 | */ | |
697 | dk_ioc.dki_lba = 1; | |
698 | if (NBLOCKS(vtoc->efi_nparts, disk_info.dki_lbsize) < 34) { | |
699 | label_len = EFI_MIN_ARRAY_SIZE + disk_info.dki_lbsize; | |
700 | } else { | |
701 | label_len = vtoc->efi_nparts * (int) sizeof (efi_gpe_t) + | |
702 | disk_info.dki_lbsize; | |
703 | if (label_len % disk_info.dki_lbsize) { | |
704 | /* pad to physical sector size */ | |
705 | label_len += disk_info.dki_lbsize; | |
706 | label_len &= ~(disk_info.dki_lbsize - 1); | |
707 | } | |
708 | } | |
709 | ||
d603ed6c | 710 | if (posix_memalign((void **)&dk_ioc.dki_data, |
d1d7e268 | 711 | disk_info.dki_lbsize, label_len)) |
5c363129 BB |
712 | return (VT_ERROR); |
713 | ||
d603ed6c | 714 | memset(dk_ioc.dki_data, 0, label_len); |
5c363129 BB |
715 | dk_ioc.dki_length = disk_info.dki_lbsize; |
716 | user_length = vtoc->efi_nparts; | |
717 | efi = dk_ioc.dki_data; | |
718 | if (md_flag) { | |
719 | dk_ioc.dki_length = label_len; | |
720 | if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc) == -1) { | |
721 | switch (errno) { | |
722 | case EIO: | |
723 | return (VT_EIO); | |
724 | default: | |
725 | return (VT_ERROR); | |
726 | } | |
727 | } | |
728 | } else if ((rval = check_label(fd, &dk_ioc)) == VT_EINVAL) { | |
729 | /* | |
730 | * No valid label here; try the alternate. Note that here | |
731 | * we just read GPT header and save it into dk_ioc.data, | |
732 | * Later, we will read GUID partition entry array if we | |
733 | * can get valid GPT header. | |
734 | */ | |
735 | ||
736 | /* | |
737 | * This is a workaround for legacy systems. In the past, the | |
738 | * last sector of SCSI disk was invisible on x86 platform. At | |
739 | * that time, backup label was saved on the next to the last | |
740 | * sector. It is possible for users to move a disk from previous | |
741 | * solaris system to present system. Here, we attempt to search | |
742 | * legacy backup EFI label first. | |
743 | */ | |
744 | dk_ioc.dki_lba = disk_info.dki_capacity - 2; | |
745 | dk_ioc.dki_length = disk_info.dki_lbsize; | |
746 | rval = check_label(fd, &dk_ioc); | |
747 | if (rval == VT_EINVAL) { | |
748 | /* | |
749 | * we didn't find legacy backup EFI label, try to | |
750 | * search backup EFI label in the last block. | |
751 | */ | |
752 | dk_ioc.dki_lba = disk_info.dki_capacity - 1; | |
753 | dk_ioc.dki_length = disk_info.dki_lbsize; | |
754 | rval = check_label(fd, &dk_ioc); | |
755 | if (rval == 0) { | |
756 | legacy_label = B_TRUE; | |
757 | if (efi_debug) | |
758 | (void) fprintf(stderr, | |
759 | "efi_read: primary label corrupt; " | |
760 | "using EFI backup label located on" | |
761 | " the last block\n"); | |
762 | } | |
763 | } else { | |
764 | if ((efi_debug) && (rval == 0)) | |
765 | (void) fprintf(stderr, "efi_read: primary label" | |
766 | " corrupt; using legacy EFI backup label " | |
767 | " located on the next to last block\n"); | |
768 | } | |
769 | ||
770 | if (rval == 0) { | |
771 | dk_ioc.dki_lba = LE_64(efi->efi_gpt_PartitionEntryLBA); | |
772 | vtoc->efi_flags |= EFI_GPT_PRIMARY_CORRUPT; | |
773 | vtoc->efi_nparts = | |
774 | LE_32(efi->efi_gpt_NumberOfPartitionEntries); | |
775 | /* | |
776 | * Partition tables are between backup GPT header | |
777 | * table and ParitionEntryLBA (the starting LBA of | |
778 | * the GUID partition entries array). Now that we | |
779 | * already got valid GPT header and saved it in | |
780 | * dk_ioc.dki_data, we try to get GUID partition | |
781 | * entry array here. | |
782 | */ | |
783 | /* LINTED */ | |
784 | dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data | |
785 | + disk_info.dki_lbsize); | |
786 | if (legacy_label) | |
787 | dk_ioc.dki_length = disk_info.dki_capacity - 1 - | |
788 | dk_ioc.dki_lba; | |
789 | else | |
790 | dk_ioc.dki_length = disk_info.dki_capacity - 2 - | |
791 | dk_ioc.dki_lba; | |
792 | dk_ioc.dki_length *= disk_info.dki_lbsize; | |
793 | if (dk_ioc.dki_length > | |
794 | ((len_t)label_len - sizeof (*dk_ioc.dki_data))) { | |
795 | rval = VT_EINVAL; | |
796 | } else { | |
797 | /* | |
798 | * read GUID partition entry array | |
799 | */ | |
800 | rval = efi_ioctl(fd, DKIOCGETEFI, &dk_ioc); | |
801 | } | |
802 | } | |
803 | ||
804 | } else if (rval == 0) { | |
805 | ||
806 | dk_ioc.dki_lba = LE_64(efi->efi_gpt_PartitionEntryLBA); | |
807 | /* LINTED */ | |
808 | dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data | |
809 | + disk_info.dki_lbsize); | |
810 | dk_ioc.dki_length = label_len - disk_info.dki_lbsize; | |
811 | rval = efi_ioctl(fd, DKIOCGETEFI, &dk_ioc); | |
812 | ||
813 | } else if (vdc_flag && rval == VT_ERROR && errno == EINVAL) { | |
814 | /* | |
815 | * When the device is a LDoms virtual disk, the DKIOCGETEFI | |
816 | * ioctl can fail with EINVAL if the virtual disk backend | |
817 | * is a ZFS volume serviced by a domain running an old version | |
818 | * of Solaris. This is because the DKIOCGETEFI ioctl was | |
819 | * initially incorrectly implemented for a ZFS volume and it | |
820 | * expected the GPT and GPE to be retrieved with a single ioctl. | |
821 | * So we try to read the GPT and the GPE using that old style | |
822 | * ioctl. | |
823 | */ | |
824 | dk_ioc.dki_lba = 1; | |
825 | dk_ioc.dki_length = label_len; | |
826 | rval = check_label(fd, &dk_ioc); | |
827 | } | |
828 | ||
829 | if (rval < 0) { | |
830 | free(efi); | |
831 | return (rval); | |
832 | } | |
833 | ||
834 | /* LINTED -- always longlong aligned */ | |
835 | efi_parts = (efi_gpe_t *)(((char *)efi) + disk_info.dki_lbsize); | |
836 | ||
837 | /* | |
838 | * Assemble this into a "dk_gpt" struct for easier | |
839 | * digestibility by applications. | |
840 | */ | |
841 | vtoc->efi_version = LE_32(efi->efi_gpt_Revision); | |
842 | vtoc->efi_nparts = LE_32(efi->efi_gpt_NumberOfPartitionEntries); | |
843 | vtoc->efi_part_size = LE_32(efi->efi_gpt_SizeOfPartitionEntry); | |
844 | vtoc->efi_lbasize = disk_info.dki_lbsize; | |
845 | vtoc->efi_last_lba = disk_info.dki_capacity - 1; | |
846 | vtoc->efi_first_u_lba = LE_64(efi->efi_gpt_FirstUsableLBA); | |
847 | vtoc->efi_last_u_lba = LE_64(efi->efi_gpt_LastUsableLBA); | |
848 | vtoc->efi_altern_lba = LE_64(efi->efi_gpt_AlternateLBA); | |
849 | UUID_LE_CONVERT(vtoc->efi_disk_uguid, efi->efi_gpt_DiskGUID); | |
850 | ||
851 | /* | |
852 | * If the array the user passed in is too small, set the length | |
853 | * to what it needs to be and return | |
854 | */ | |
855 | if (user_length < vtoc->efi_nparts) { | |
856 | return (VT_EINVAL); | |
857 | } | |
858 | ||
859 | for (i = 0; i < vtoc->efi_nparts; i++) { | |
860 | ||
861 | UUID_LE_CONVERT(vtoc->efi_parts[i].p_guid, | |
862 | efi_parts[i].efi_gpe_PartitionTypeGUID); | |
863 | ||
864 | for (j = 0; | |
865 | j < sizeof (conversion_array) | |
866 | / sizeof (struct uuid_to_ptag); j++) { | |
867 | ||
868 | if (bcmp(&vtoc->efi_parts[i].p_guid, | |
869 | &conversion_array[j].uuid, | |
870 | sizeof (struct uuid)) == 0) { | |
871 | vtoc->efi_parts[i].p_tag = j; | |
872 | break; | |
873 | } | |
874 | } | |
875 | if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) | |
876 | continue; | |
877 | vtoc->efi_parts[i].p_flag = | |
878 | LE_16(efi_parts[i].efi_gpe_Attributes.PartitionAttrs); | |
879 | vtoc->efi_parts[i].p_start = | |
880 | LE_64(efi_parts[i].efi_gpe_StartingLBA); | |
881 | vtoc->efi_parts[i].p_size = | |
882 | LE_64(efi_parts[i].efi_gpe_EndingLBA) - | |
883 | vtoc->efi_parts[i].p_start + 1; | |
884 | for (j = 0; j < EFI_PART_NAME_LEN; j++) { | |
885 | vtoc->efi_parts[i].p_name[j] = | |
886 | (uchar_t)LE_16( | |
887 | efi_parts[i].efi_gpe_PartitionName[j]); | |
888 | } | |
889 | ||
890 | UUID_LE_CONVERT(vtoc->efi_parts[i].p_uguid, | |
891 | efi_parts[i].efi_gpe_UniquePartitionGUID); | |
892 | } | |
893 | free(efi); | |
894 | ||
895 | return (dki_info.dki_partition); | |
896 | } | |
897 | ||
898 | /* writes a "protective" MBR */ | |
899 | static int | |
900 | write_pmbr(int fd, struct dk_gpt *vtoc) | |
901 | { | |
902 | dk_efi_t dk_ioc; | |
903 | struct mboot mb; | |
904 | uchar_t *cp; | |
905 | diskaddr_t size_in_lba; | |
906 | uchar_t *buf; | |
907 | int len; | |
908 | ||
909 | len = (vtoc->efi_lbasize == 0) ? sizeof (mb) : vtoc->efi_lbasize; | |
d603ed6c BB |
910 | if (posix_memalign((void **)&buf, len, len)) |
911 | return (VT_ERROR); | |
5c363129 BB |
912 | |
913 | /* | |
914 | * Preserve any boot code and disk signature if the first block is | |
915 | * already an MBR. | |
916 | */ | |
d603ed6c | 917 | memset(buf, 0, len); |
5c363129 BB |
918 | dk_ioc.dki_lba = 0; |
919 | dk_ioc.dki_length = len; | |
920 | /* LINTED -- always longlong aligned */ | |
921 | dk_ioc.dki_data = (efi_gpt_t *)buf; | |
922 | if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc) == -1) { | |
f9f431cd | 923 | (void) memcpy(&mb, buf, sizeof (mb)); |
5c363129 BB |
924 | bzero(&mb, sizeof (mb)); |
925 | mb.signature = LE_16(MBB_MAGIC); | |
926 | } else { | |
f9f431cd | 927 | (void) memcpy(&mb, buf, sizeof (mb)); |
5c363129 BB |
928 | if (mb.signature != LE_16(MBB_MAGIC)) { |
929 | bzero(&mb, sizeof (mb)); | |
930 | mb.signature = LE_16(MBB_MAGIC); | |
931 | } | |
932 | } | |
933 | ||
934 | bzero(&mb.parts, sizeof (mb.parts)); | |
935 | cp = (uchar_t *)&mb.parts[0]; | |
936 | /* bootable or not */ | |
937 | *cp++ = 0; | |
938 | /* beginning CHS; 0xffffff if not representable */ | |
939 | *cp++ = 0xff; | |
940 | *cp++ = 0xff; | |
941 | *cp++ = 0xff; | |
942 | /* OS type */ | |
943 | *cp++ = EFI_PMBR; | |
944 | /* ending CHS; 0xffffff if not representable */ | |
945 | *cp++ = 0xff; | |
946 | *cp++ = 0xff; | |
947 | *cp++ = 0xff; | |
948 | /* starting LBA: 1 (little endian format) by EFI definition */ | |
949 | *cp++ = 0x01; | |
950 | *cp++ = 0x00; | |
951 | *cp++ = 0x00; | |
952 | *cp++ = 0x00; | |
953 | /* ending LBA: last block on the disk (little endian format) */ | |
954 | size_in_lba = vtoc->efi_last_lba; | |
955 | if (size_in_lba < 0xffffffff) { | |
956 | *cp++ = (size_in_lba & 0x000000ff); | |
957 | *cp++ = (size_in_lba & 0x0000ff00) >> 8; | |
958 | *cp++ = (size_in_lba & 0x00ff0000) >> 16; | |
959 | *cp++ = (size_in_lba & 0xff000000) >> 24; | |
960 | } else { | |
961 | *cp++ = 0xff; | |
962 | *cp++ = 0xff; | |
963 | *cp++ = 0xff; | |
964 | *cp++ = 0xff; | |
965 | } | |
966 | ||
f9f431cd | 967 | (void) memcpy(buf, &mb, sizeof (mb)); |
5c363129 BB |
968 | /* LINTED -- always longlong aligned */ |
969 | dk_ioc.dki_data = (efi_gpt_t *)buf; | |
970 | dk_ioc.dki_lba = 0; | |
971 | dk_ioc.dki_length = len; | |
972 | if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { | |
973 | free(buf); | |
974 | switch (errno) { | |
975 | case EIO: | |
976 | return (VT_EIO); | |
977 | case EINVAL: | |
978 | return (VT_EINVAL); | |
979 | default: | |
980 | return (VT_ERROR); | |
981 | } | |
982 | } | |
983 | free(buf); | |
984 | return (0); | |
985 | } | |
986 | ||
987 | /* make sure the user specified something reasonable */ | |
988 | static int | |
989 | check_input(struct dk_gpt *vtoc) | |
990 | { | |
991 | int resv_part = -1; | |
992 | int i, j; | |
993 | diskaddr_t istart, jstart, isize, jsize, endsect; | |
994 | ||
995 | /* | |
996 | * Sanity-check the input (make sure no partitions overlap) | |
997 | */ | |
998 | for (i = 0; i < vtoc->efi_nparts; i++) { | |
999 | /* It can't be unassigned and have an actual size */ | |
1000 | if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) && | |
1001 | (vtoc->efi_parts[i].p_size != 0)) { | |
1002 | if (efi_debug) { | |
d603ed6c BB |
1003 | (void) fprintf(stderr, "partition %d is " |
1004 | "\"unassigned\" but has a size of %llu", | |
1005 | i, vtoc->efi_parts[i].p_size); | |
5c363129 BB |
1006 | } |
1007 | return (VT_EINVAL); | |
1008 | } | |
1009 | if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) { | |
1010 | if (uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid)) | |
1011 | continue; | |
1012 | /* we have encountered an unknown uuid */ | |
1013 | vtoc->efi_parts[i].p_tag = 0xff; | |
1014 | } | |
1015 | if (vtoc->efi_parts[i].p_tag == V_RESERVED) { | |
1016 | if (resv_part != -1) { | |
1017 | if (efi_debug) { | |
d603ed6c BB |
1018 | (void) fprintf(stderr, "found " |
1019 | "duplicate reserved partition " | |
1020 | "at %d\n", i); | |
5c363129 BB |
1021 | } |
1022 | return (VT_EINVAL); | |
1023 | } | |
1024 | resv_part = i; | |
1025 | } | |
1026 | if ((vtoc->efi_parts[i].p_start < vtoc->efi_first_u_lba) || | |
1027 | (vtoc->efi_parts[i].p_start > vtoc->efi_last_u_lba)) { | |
1028 | if (efi_debug) { | |
1029 | (void) fprintf(stderr, | |
1030 | "Partition %d starts at %llu. ", | |
1031 | i, | |
1032 | vtoc->efi_parts[i].p_start); | |
1033 | (void) fprintf(stderr, | |
1034 | "It must be between %llu and %llu.\n", | |
1035 | vtoc->efi_first_u_lba, | |
1036 | vtoc->efi_last_u_lba); | |
1037 | } | |
1038 | return (VT_EINVAL); | |
1039 | } | |
1040 | if ((vtoc->efi_parts[i].p_start + | |
1041 | vtoc->efi_parts[i].p_size < | |
1042 | vtoc->efi_first_u_lba) || | |
1043 | (vtoc->efi_parts[i].p_start + | |
1044 | vtoc->efi_parts[i].p_size > | |
1045 | vtoc->efi_last_u_lba + 1)) { | |
1046 | if (efi_debug) { | |
1047 | (void) fprintf(stderr, | |
1048 | "Partition %d ends at %llu. ", | |
1049 | i, | |
1050 | vtoc->efi_parts[i].p_start + | |
1051 | vtoc->efi_parts[i].p_size); | |
1052 | (void) fprintf(stderr, | |
1053 | "It must be between %llu and %llu.\n", | |
1054 | vtoc->efi_first_u_lba, | |
1055 | vtoc->efi_last_u_lba); | |
1056 | } | |
1057 | return (VT_EINVAL); | |
1058 | } | |
1059 | ||
1060 | for (j = 0; j < vtoc->efi_nparts; j++) { | |
1061 | isize = vtoc->efi_parts[i].p_size; | |
1062 | jsize = vtoc->efi_parts[j].p_size; | |
1063 | istart = vtoc->efi_parts[i].p_start; | |
1064 | jstart = vtoc->efi_parts[j].p_start; | |
1065 | if ((i != j) && (isize != 0) && (jsize != 0)) { | |
1066 | endsect = jstart + jsize -1; | |
1067 | if ((jstart <= istart) && | |
1068 | (istart <= endsect)) { | |
1069 | if (efi_debug) { | |
1070 | (void) fprintf(stderr, | |
d603ed6c BB |
1071 | "Partition %d overlaps " |
1072 | "partition %d.", i, j); | |
5c363129 BB |
1073 | } |
1074 | return (VT_EINVAL); | |
1075 | } | |
1076 | } | |
1077 | } | |
1078 | } | |
1079 | /* just a warning for now */ | |
1080 | if ((resv_part == -1) && efi_debug) { | |
1081 | (void) fprintf(stderr, | |
1082 | "no reserved partition found\n"); | |
1083 | } | |
1084 | return (0); | |
1085 | } | |
1086 | ||
8e82ffba GW |
1087 | static int |
1088 | call_blkpg_ioctl(int fd, int command, diskaddr_t start, | |
1089 | diskaddr_t size, uint_t pno) | |
1090 | { | |
1091 | struct blkpg_ioctl_arg ioctl_arg; | |
1092 | struct blkpg_partition linux_part; | |
1093 | memset(&linux_part, 0, sizeof (linux_part)); | |
1094 | ||
1095 | char *path = efi_get_devname(fd); | |
1096 | if (path == NULL) { | |
1097 | (void) fprintf(stderr, "failed to retrieve device name\n"); | |
1098 | return (VT_EINVAL); | |
1099 | } | |
1100 | ||
1101 | linux_part.start = start; | |
1102 | linux_part.length = size; | |
1103 | linux_part.pno = pno; | |
1104 | snprintf(linux_part.devname, BLKPG_DEVNAMELTH - 1, "%s%u", path, pno); | |
1105 | linux_part.devname[BLKPG_DEVNAMELTH - 1] = '\0'; | |
1106 | free(path); | |
1107 | ||
1108 | ioctl_arg.op = command; | |
1109 | ioctl_arg.flags = 0; | |
1110 | ioctl_arg.datalen = sizeof (struct blkpg_partition); | |
1111 | ioctl_arg.data = &linux_part; | |
1112 | ||
1113 | return (ioctl(fd, BLKPG, &ioctl_arg)); | |
1114 | } | |
1115 | ||
5c363129 BB |
1116 | /* |
1117 | * add all the unallocated space to the current label | |
1118 | */ | |
1119 | int | |
1120 | efi_use_whole_disk(int fd) | |
1121 | { | |
8e82ffba GW |
1122 | struct dk_gpt *efi_label = NULL; |
1123 | int rval; | |
1124 | int i; | |
1125 | uint_t resv_index = 0, data_index = 0; | |
1126 | diskaddr_t resv_start = 0, data_start = 0; | |
1127 | diskaddr_t data_size, limit, difference; | |
1128 | boolean_t sync_needed = B_FALSE; | |
1129 | uint_t nblocks; | |
5c363129 BB |
1130 | |
1131 | rval = efi_alloc_and_read(fd, &efi_label); | |
1132 | if (rval < 0) { | |
a64f903b GN |
1133 | if (efi_label != NULL) |
1134 | efi_free(efi_label); | |
5c363129 BB |
1135 | return (rval); |
1136 | } | |
1137 | ||
475ebd76 PS |
1138 | /* |
1139 | * Find the last physically non-zero partition. | |
1140 | * This should be the reserved partition. | |
1141 | */ | |
1142 | for (i = 0; i < efi_label->efi_nparts; i ++) { | |
1143 | if (resv_start < efi_label->efi_parts[i].p_start) { | |
1144 | resv_start = efi_label->efi_parts[i].p_start; | |
1145 | resv_index = i; | |
1146 | } | |
1147 | } | |
1148 | ||
1149 | /* | |
1150 | * Find the last physically non-zero partition before that. | |
1151 | * This is the data partition. | |
1152 | */ | |
1153 | for (i = 0; i < resv_index; i ++) { | |
1154 | if (data_start < efi_label->efi_parts[i].p_start) { | |
1155 | data_start = efi_label->efi_parts[i].p_start; | |
1156 | data_index = i; | |
1157 | } | |
1158 | } | |
1159 | data_size = efi_label->efi_parts[data_index].p_size; | |
1160 | ||
1161 | /* | |
1162 | * See the "efi_alloc_and_init" function for more information | |
1163 | * about where this "nblocks" value comes from. | |
1164 | */ | |
1165 | nblocks = efi_label->efi_first_u_lba - 1; | |
1166 | ||
1167 | /* | |
1168 | * Determine if the EFI label is out of sync. We check that: | |
1169 | * | |
1170 | * 1. the data partition ends at the limit we set, and | |
1171 | * 2. the reserved partition starts at the limit we set. | |
1172 | * | |
1173 | * If either of these conditions is not met, then we need to | |
1174 | * resync the EFI label. | |
1175 | * | |
1176 | * The limit is the last usable LBA, determined by the last LBA | |
1177 | * and the first usable LBA fields on the EFI label of the disk | |
1178 | * (see the lines directly above). Additionally, we factor in | |
1179 | * EFI_MIN_RESV_SIZE (per its use in "zpool_label_disk") and | |
1180 | * P2ALIGN it to ensure the partition boundaries are aligned | |
1181 | * (for performance reasons). The alignment should match the | |
1182 | * alignment used by the "zpool_label_disk" function. | |
1183 | */ | |
1184 | limit = P2ALIGN(efi_label->efi_last_lba - nblocks - EFI_MIN_RESV_SIZE, | |
1185 | PARTITION_END_ALIGNMENT); | |
1186 | if (data_start + data_size != limit || resv_start != limit) | |
1187 | sync_needed = B_TRUE; | |
1188 | ||
1189 | if (efi_debug && sync_needed) | |
1190 | (void) fprintf(stderr, "efi_use_whole_disk: sync needed\n"); | |
1191 | ||
5c363129 BB |
1192 | /* |
1193 | * If alter_lba is 1, we are using the backup label. | |
1194 | * Since we can locate the backup label by disk capacity, | |
1195 | * there must be no unallocated space. | |
1196 | */ | |
1197 | if ((efi_label->efi_altern_lba == 1) || (efi_label->efi_altern_lba | |
475ebd76 | 1198 | >= efi_label->efi_last_lba && !sync_needed)) { |
5c363129 BB |
1199 | if (efi_debug) { |
1200 | (void) fprintf(stderr, | |
1201 | "efi_use_whole_disk: requested space not found\n"); | |
1202 | } | |
1203 | efi_free(efi_label); | |
1204 | return (VT_ENOSPC); | |
1205 | } | |
1206 | ||
74d42600 SH |
1207 | /* |
1208 | * Verify that we've found the reserved partition by checking | |
1209 | * that it looks the way it did when we created it in zpool_label_disk. | |
1210 | * If we've found the incorrect partition, then we know that this | |
78595377 | 1211 | * device was reformatted and no longer is solely used by ZFS. |
74d42600 SH |
1212 | */ |
1213 | if ((efi_label->efi_parts[resv_index].p_size != EFI_MIN_RESV_SIZE) || | |
1214 | (efi_label->efi_parts[resv_index].p_tag != V_RESERVED) || | |
1215 | (resv_index != 8)) { | |
1216 | if (efi_debug) { | |
1217 | (void) fprintf(stderr, | |
1218 | "efi_use_whole_disk: wholedisk not available\n"); | |
1219 | } | |
1220 | efi_free(efi_label); | |
1221 | return (VT_ENOSPC); | |
1222 | } | |
1223 | ||
475ebd76 PS |
1224 | if (data_start + data_size != resv_start) { |
1225 | if (efi_debug) { | |
1226 | (void) fprintf(stderr, | |
1227 | "efi_use_whole_disk: " | |
1228 | "data_start (%lli) + " | |
1229 | "data_size (%lli) != " | |
1230 | "resv_start (%lli)\n", | |
1231 | data_start, data_size, resv_start); | |
1232 | } | |
1233 | ||
1234 | return (VT_EINVAL); | |
1235 | } | |
1236 | ||
1237 | if (limit < resv_start) { | |
1238 | if (efi_debug) { | |
1239 | (void) fprintf(stderr, | |
1240 | "efi_use_whole_disk: " | |
1241 | "limit (%lli) < resv_start (%lli)\n", | |
1242 | limit, resv_start); | |
cee43a74 | 1243 | } |
475ebd76 PS |
1244 | |
1245 | return (VT_EINVAL); | |
5c363129 BB |
1246 | } |
1247 | ||
475ebd76 PS |
1248 | difference = limit - resv_start; |
1249 | ||
1250 | if (efi_debug) | |
1251 | (void) fprintf(stderr, | |
1252 | "efi_use_whole_disk: difference is %lli\n", difference); | |
1253 | ||
5c363129 BB |
1254 | /* |
1255 | * Move the reserved partition. There is currently no data in | |
1256 | * here except fabricated devids (which get generated via | |
1257 | * efi_write()). So there is no need to copy data. | |
1258 | */ | |
cee43a74 ED |
1259 | efi_label->efi_parts[data_index].p_size += difference; |
1260 | efi_label->efi_parts[resv_index].p_start += difference; | |
475ebd76 | 1261 | efi_label->efi_last_u_lba = efi_label->efi_last_lba - nblocks; |
5c363129 | 1262 | |
8e82ffba GW |
1263 | /* |
1264 | * Rescanning the partition table in the kernel can result | |
1265 | * in the device links to be removed (see comment in vdev_disk_open). | |
1266 | * If BLKPG_RESIZE_PARTITION is available, then we can resize | |
1267 | * the partition table online and avoid having to remove the device | |
1268 | * links used by the pool. This provides a very deterministic | |
1269 | * approach to resizing devices and does not require any | |
1270 | * loops waiting for devices to reappear. | |
1271 | */ | |
1272 | #ifdef BLKPG_RESIZE_PARTITION | |
1273 | /* | |
1274 | * Delete the reserved partition since we're about to expand | |
1275 | * the data partition and it would overlap with the reserved | |
1276 | * partition. | |
1277 | * NOTE: The starting index for the ioctl is 1 while for the | |
1278 | * EFI partitions it's 0. For that reason we have to add one | |
1279 | * whenever we make an ioctl call. | |
1280 | */ | |
1281 | rval = call_blkpg_ioctl(fd, BLKPG_DEL_PARTITION, 0, 0, resv_index + 1); | |
1282 | if (rval != 0) | |
1283 | goto out; | |
1284 | ||
1285 | /* | |
1286 | * Expand the data partition | |
1287 | */ | |
1288 | rval = call_blkpg_ioctl(fd, BLKPG_RESIZE_PARTITION, | |
1289 | efi_label->efi_parts[data_index].p_start * efi_label->efi_lbasize, | |
1290 | efi_label->efi_parts[data_index].p_size * efi_label->efi_lbasize, | |
1291 | data_index + 1); | |
1292 | if (rval != 0) { | |
1293 | (void) fprintf(stderr, "Unable to resize data " | |
1294 | "partition: %d\n", rval); | |
1295 | /* | |
1296 | * Since we failed to resize, we need to reset the start | |
1297 | * of the reserve partition and re-create it. | |
1298 | */ | |
1299 | efi_label->efi_parts[resv_index].p_start -= difference; | |
1300 | } | |
1301 | ||
1302 | /* | |
1303 | * Re-add the reserved partition. If we've expanded the data partition | |
1304 | * then we'll move the reserve partition to the end of the data | |
1305 | * partition. Otherwise, we'll recreate the partition in its original | |
1306 | * location. Note that we do this as best-effort and ignore any | |
1307 | * errors that may arise here. This will ensure that we finish writing | |
1308 | * the EFI label. | |
1309 | */ | |
1310 | (void) call_blkpg_ioctl(fd, BLKPG_ADD_PARTITION, | |
1311 | efi_label->efi_parts[resv_index].p_start * efi_label->efi_lbasize, | |
1312 | efi_label->efi_parts[resv_index].p_size * efi_label->efi_lbasize, | |
1313 | resv_index + 1); | |
1314 | #endif | |
1315 | ||
1316 | /* | |
1317 | * We're now ready to write the EFI label. | |
1318 | */ | |
1319 | if (rval == 0) { | |
1320 | rval = efi_write(fd, efi_label); | |
1321 | if (rval < 0 && efi_debug) { | |
1322 | (void) fprintf(stderr, "efi_use_whole_disk:fail " | |
1323 | "to write label, rval=%d\n", rval); | |
5c363129 | 1324 | } |
5c363129 BB |
1325 | } |
1326 | ||
8e82ffba | 1327 | out: |
5c363129 | 1328 | efi_free(efi_label); |
8e82ffba | 1329 | return (rval); |
5c363129 BB |
1330 | } |
1331 | ||
5c363129 BB |
1332 | /* |
1333 | * write EFI label and backup label | |
1334 | */ | |
1335 | int | |
1336 | efi_write(int fd, struct dk_gpt *vtoc) | |
1337 | { | |
1338 | dk_efi_t dk_ioc; | |
1339 | efi_gpt_t *efi; | |
1340 | efi_gpe_t *efi_parts; | |
1341 | int i, j; | |
1342 | struct dk_cinfo dki_info; | |
d603ed6c | 1343 | int rval; |
5c363129 BB |
1344 | int md_flag = 0; |
1345 | int nblocks; | |
1346 | diskaddr_t lba_backup_gpt_hdr; | |
1347 | ||
d603ed6c | 1348 | if ((rval = efi_get_info(fd, &dki_info)) != 0) |
d1d7e268 | 1349 | return (rval); |
5c363129 | 1350 | |
78595377 | 1351 | /* check if we are dealing with a metadevice */ |
5c363129 BB |
1352 | if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) && |
1353 | (strncmp(dki_info.dki_dname, "md", 3) == 0)) { | |
1354 | md_flag = 1; | |
1355 | } | |
1356 | ||
1357 | if (check_input(vtoc)) { | |
1358 | /* | |
1359 | * not valid; if it's a metadevice just pass it down | |
1360 | * because SVM will do its own checking | |
1361 | */ | |
1362 | if (md_flag == 0) { | |
1363 | return (VT_EINVAL); | |
1364 | } | |
1365 | } | |
1366 | ||
1367 | dk_ioc.dki_lba = 1; | |
1368 | if (NBLOCKS(vtoc->efi_nparts, vtoc->efi_lbasize) < 34) { | |
1369 | dk_ioc.dki_length = EFI_MIN_ARRAY_SIZE + vtoc->efi_lbasize; | |
1370 | } else { | |
1371 | dk_ioc.dki_length = NBLOCKS(vtoc->efi_nparts, | |
1372 | vtoc->efi_lbasize) * | |
1373 | vtoc->efi_lbasize; | |
1374 | } | |
1375 | ||
1376 | /* | |
1377 | * the number of blocks occupied by GUID partition entry array | |
1378 | */ | |
1379 | nblocks = dk_ioc.dki_length / vtoc->efi_lbasize - 1; | |
1380 | ||
1381 | /* | |
1382 | * Backup GPT header is located on the block after GUID | |
1383 | * partition entry array. Here, we calculate the address | |
1384 | * for backup GPT header. | |
1385 | */ | |
1386 | lba_backup_gpt_hdr = vtoc->efi_last_u_lba + 1 + nblocks; | |
d603ed6c | 1387 | if (posix_memalign((void **)&dk_ioc.dki_data, |
d1d7e268 | 1388 | vtoc->efi_lbasize, dk_ioc.dki_length)) |
5c363129 BB |
1389 | return (VT_ERROR); |
1390 | ||
d603ed6c | 1391 | memset(dk_ioc.dki_data, 0, dk_ioc.dki_length); |
5c363129 BB |
1392 | efi = dk_ioc.dki_data; |
1393 | ||
1394 | /* stuff user's input into EFI struct */ | |
1395 | efi->efi_gpt_Signature = LE_64(EFI_SIGNATURE); | |
1396 | efi->efi_gpt_Revision = LE_32(vtoc->efi_version); /* 0x02000100 */ | |
7a023273 | 1397 | efi->efi_gpt_HeaderSize = LE_32(sizeof (struct efi_gpt) - LEN_EFI_PAD); |
5c363129 BB |
1398 | efi->efi_gpt_Reserved1 = 0; |
1399 | efi->efi_gpt_MyLBA = LE_64(1ULL); | |
1400 | efi->efi_gpt_AlternateLBA = LE_64(lba_backup_gpt_hdr); | |
1401 | efi->efi_gpt_FirstUsableLBA = LE_64(vtoc->efi_first_u_lba); | |
1402 | efi->efi_gpt_LastUsableLBA = LE_64(vtoc->efi_last_u_lba); | |
1403 | efi->efi_gpt_PartitionEntryLBA = LE_64(2ULL); | |
1404 | efi->efi_gpt_NumberOfPartitionEntries = LE_32(vtoc->efi_nparts); | |
1405 | efi->efi_gpt_SizeOfPartitionEntry = LE_32(sizeof (struct efi_gpe)); | |
1406 | UUID_LE_CONVERT(efi->efi_gpt_DiskGUID, vtoc->efi_disk_uguid); | |
1407 | ||
1408 | /* LINTED -- always longlong aligned */ | |
1409 | efi_parts = (efi_gpe_t *)((char *)dk_ioc.dki_data + vtoc->efi_lbasize); | |
1410 | ||
1411 | for (i = 0; i < vtoc->efi_nparts; i++) { | |
1412 | for (j = 0; | |
1413 | j < sizeof (conversion_array) / | |
1414 | sizeof (struct uuid_to_ptag); j++) { | |
1415 | ||
1416 | if (vtoc->efi_parts[i].p_tag == j) { | |
1417 | UUID_LE_CONVERT( | |
1418 | efi_parts[i].efi_gpe_PartitionTypeGUID, | |
1419 | conversion_array[j].uuid); | |
1420 | break; | |
1421 | } | |
1422 | } | |
1423 | ||
1424 | if (j == sizeof (conversion_array) / | |
1425 | sizeof (struct uuid_to_ptag)) { | |
1426 | /* | |
1427 | * If we didn't have a matching uuid match, bail here. | |
1428 | * Don't write a label with unknown uuid. | |
1429 | */ | |
1430 | if (efi_debug) { | |
1431 | (void) fprintf(stderr, | |
1432 | "Unknown uuid for p_tag %d\n", | |
1433 | vtoc->efi_parts[i].p_tag); | |
1434 | } | |
1435 | return (VT_EINVAL); | |
1436 | } | |
1437 | ||
d603ed6c BB |
1438 | /* Zero's should be written for empty partitions */ |
1439 | if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) | |
1440 | continue; | |
1441 | ||
5c363129 BB |
1442 | efi_parts[i].efi_gpe_StartingLBA = |
1443 | LE_64(vtoc->efi_parts[i].p_start); | |
1444 | efi_parts[i].efi_gpe_EndingLBA = | |
1445 | LE_64(vtoc->efi_parts[i].p_start + | |
1446 | vtoc->efi_parts[i].p_size - 1); | |
1447 | efi_parts[i].efi_gpe_Attributes.PartitionAttrs = | |
1448 | LE_16(vtoc->efi_parts[i].p_flag); | |
1449 | for (j = 0; j < EFI_PART_NAME_LEN; j++) { | |
1450 | efi_parts[i].efi_gpe_PartitionName[j] = | |
1451 | LE_16((ushort_t)vtoc->efi_parts[i].p_name[j]); | |
1452 | } | |
1453 | if ((vtoc->efi_parts[i].p_tag != V_UNASSIGNED) && | |
1454 | uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_uguid)) { | |
1455 | (void) uuid_generate((uchar_t *) | |
1456 | &vtoc->efi_parts[i].p_uguid); | |
1457 | } | |
1458 | bcopy(&vtoc->efi_parts[i].p_uguid, | |
1459 | &efi_parts[i].efi_gpe_UniquePartitionGUID, | |
1460 | sizeof (uuid_t)); | |
1461 | } | |
1462 | efi->efi_gpt_PartitionEntryArrayCRC32 = | |
1463 | LE_32(efi_crc32((unsigned char *)efi_parts, | |
1464 | vtoc->efi_nparts * (int)sizeof (struct efi_gpe))); | |
1465 | efi->efi_gpt_HeaderCRC32 = | |
7a023273 ZB |
1466 | LE_32(efi_crc32((unsigned char *)efi, |
1467 | LE_32(efi->efi_gpt_HeaderSize))); | |
5c363129 BB |
1468 | |
1469 | if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { | |
1470 | free(dk_ioc.dki_data); | |
1471 | switch (errno) { | |
1472 | case EIO: | |
1473 | return (VT_EIO); | |
1474 | case EINVAL: | |
1475 | return (VT_EINVAL); | |
1476 | default: | |
1477 | return (VT_ERROR); | |
1478 | } | |
1479 | } | |
1480 | /* if it's a metadevice we're done */ | |
1481 | if (md_flag) { | |
1482 | free(dk_ioc.dki_data); | |
1483 | return (0); | |
1484 | } | |
1485 | ||
1486 | /* write backup partition array */ | |
1487 | dk_ioc.dki_lba = vtoc->efi_last_u_lba + 1; | |
1488 | dk_ioc.dki_length -= vtoc->efi_lbasize; | |
1489 | /* LINTED */ | |
1490 | dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data + | |
1491 | vtoc->efi_lbasize); | |
1492 | ||
1493 | if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { | |
1494 | /* | |
1495 | * we wrote the primary label okay, so don't fail | |
1496 | */ | |
1497 | if (efi_debug) { | |
1498 | (void) fprintf(stderr, | |
1499 | "write of backup partitions to block %llu " | |
1500 | "failed, errno %d\n", | |
1501 | vtoc->efi_last_u_lba + 1, | |
1502 | errno); | |
1503 | } | |
1504 | } | |
1505 | /* | |
1506 | * now swap MyLBA and AlternateLBA fields and write backup | |
1507 | * partition table header | |
1508 | */ | |
1509 | dk_ioc.dki_lba = lba_backup_gpt_hdr; | |
1510 | dk_ioc.dki_length = vtoc->efi_lbasize; | |
1511 | /* LINTED */ | |
1512 | dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data - | |
1513 | vtoc->efi_lbasize); | |
1514 | efi->efi_gpt_AlternateLBA = LE_64(1ULL); | |
1515 | efi->efi_gpt_MyLBA = LE_64(lba_backup_gpt_hdr); | |
1516 | efi->efi_gpt_PartitionEntryLBA = LE_64(vtoc->efi_last_u_lba + 1); | |
1517 | efi->efi_gpt_HeaderCRC32 = 0; | |
1518 | efi->efi_gpt_HeaderCRC32 = | |
1519 | LE_32(efi_crc32((unsigned char *)dk_ioc.dki_data, | |
7a023273 | 1520 | LE_32(efi->efi_gpt_HeaderSize))); |
5c363129 BB |
1521 | |
1522 | if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { | |
1523 | if (efi_debug) { | |
1524 | (void) fprintf(stderr, | |
1525 | "write of backup header to block %llu failed, " | |
1526 | "errno %d\n", | |
1527 | lba_backup_gpt_hdr, | |
1528 | errno); | |
1529 | } | |
1530 | } | |
1531 | /* write the PMBR */ | |
1532 | (void) write_pmbr(fd, vtoc); | |
1533 | free(dk_ioc.dki_data); | |
d603ed6c | 1534 | |
5c363129 BB |
1535 | return (0); |
1536 | } | |
1537 | ||
1538 | void | |
1539 | efi_free(struct dk_gpt *ptr) | |
1540 | { | |
1541 | free(ptr); | |
1542 | } | |
1543 | ||
1544 | /* | |
1545 | * Input: File descriptor | |
1546 | * Output: 1 if disk has an EFI label, or > 2TB with no VTOC or legacy MBR. | |
1547 | * Otherwise 0. | |
1548 | */ | |
1549 | int | |
1550 | efi_type(int fd) | |
1551 | { | |
d603ed6c | 1552 | #if 0 |
5c363129 BB |
1553 | struct vtoc vtoc; |
1554 | struct extvtoc extvtoc; | |
1555 | ||
1556 | if (ioctl(fd, DKIOCGEXTVTOC, &extvtoc) == -1) { | |
1557 | if (errno == ENOTSUP) | |
1558 | return (1); | |
1559 | else if (errno == ENOTTY) { | |
1560 | if (ioctl(fd, DKIOCGVTOC, &vtoc) == -1) | |
1561 | if (errno == ENOTSUP) | |
1562 | return (1); | |
1563 | } | |
1564 | } | |
1565 | return (0); | |
d603ed6c | 1566 | #else |
7fe47a23 | 1567 | (void) fd; |
d603ed6c BB |
1568 | return (ENOSYS); |
1569 | #endif | |
5c363129 BB |
1570 | } |
1571 | ||
1572 | void | |
1573 | efi_err_check(struct dk_gpt *vtoc) | |
1574 | { | |
1575 | int resv_part = -1; | |
1576 | int i, j; | |
1577 | diskaddr_t istart, jstart, isize, jsize, endsect; | |
1578 | int overlap = 0; | |
1579 | ||
1580 | /* | |
1581 | * make sure no partitions overlap | |
1582 | */ | |
1583 | for (i = 0; i < vtoc->efi_nparts; i++) { | |
1584 | /* It can't be unassigned and have an actual size */ | |
1585 | if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) && | |
1586 | (vtoc->efi_parts[i].p_size != 0)) { | |
1587 | (void) fprintf(stderr, | |
1588 | "partition %d is \"unassigned\" but has a size " | |
1589 | "of %llu\n", i, vtoc->efi_parts[i].p_size); | |
1590 | } | |
1591 | if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) { | |
1592 | continue; | |
1593 | } | |
1594 | if (vtoc->efi_parts[i].p_tag == V_RESERVED) { | |
1595 | if (resv_part != -1) { | |
1596 | (void) fprintf(stderr, | |
1597 | "found duplicate reserved partition at " | |
1598 | "%d\n", i); | |
1599 | } | |
1600 | resv_part = i; | |
1601 | if (vtoc->efi_parts[i].p_size != EFI_MIN_RESV_SIZE) | |
1602 | (void) fprintf(stderr, | |
1603 | "Warning: reserved partition size must " | |
1604 | "be %d sectors\n", EFI_MIN_RESV_SIZE); | |
1605 | } | |
1606 | if ((vtoc->efi_parts[i].p_start < vtoc->efi_first_u_lba) || | |
1607 | (vtoc->efi_parts[i].p_start > vtoc->efi_last_u_lba)) { | |
1608 | (void) fprintf(stderr, | |
1609 | "Partition %d starts at %llu\n", | |
1610 | i, | |
1611 | vtoc->efi_parts[i].p_start); | |
1612 | (void) fprintf(stderr, | |
1613 | "It must be between %llu and %llu.\n", | |
1614 | vtoc->efi_first_u_lba, | |
1615 | vtoc->efi_last_u_lba); | |
1616 | } | |
1617 | if ((vtoc->efi_parts[i].p_start + | |
1618 | vtoc->efi_parts[i].p_size < | |
1619 | vtoc->efi_first_u_lba) || | |
1620 | (vtoc->efi_parts[i].p_start + | |
1621 | vtoc->efi_parts[i].p_size > | |
1622 | vtoc->efi_last_u_lba + 1)) { | |
1623 | (void) fprintf(stderr, | |
1624 | "Partition %d ends at %llu\n", | |
1625 | i, | |
1626 | vtoc->efi_parts[i].p_start + | |
1627 | vtoc->efi_parts[i].p_size); | |
1628 | (void) fprintf(stderr, | |
1629 | "It must be between %llu and %llu.\n", | |
1630 | vtoc->efi_first_u_lba, | |
1631 | vtoc->efi_last_u_lba); | |
1632 | } | |
1633 | ||
1634 | for (j = 0; j < vtoc->efi_nparts; j++) { | |
1635 | isize = vtoc->efi_parts[i].p_size; | |
1636 | jsize = vtoc->efi_parts[j].p_size; | |
1637 | istart = vtoc->efi_parts[i].p_start; | |
1638 | jstart = vtoc->efi_parts[j].p_start; | |
1639 | if ((i != j) && (isize != 0) && (jsize != 0)) { | |
1640 | endsect = jstart + jsize -1; | |
1641 | if ((jstart <= istart) && | |
1642 | (istart <= endsect)) { | |
1643 | if (!overlap) { | |
1644 | (void) fprintf(stderr, | |
1645 | "label error: EFI Labels do not " | |
1646 | "support overlapping partitions\n"); | |
1647 | } | |
1648 | (void) fprintf(stderr, | |
1649 | "Partition %d overlaps partition " | |
1650 | "%d.\n", i, j); | |
1651 | overlap = 1; | |
1652 | } | |
1653 | } | |
1654 | } | |
1655 | } | |
1656 | /* make sure there is a reserved partition */ | |
1657 | if (resv_part == -1) { | |
1658 | (void) fprintf(stderr, | |
1659 | "no reserved partition found\n"); | |
1660 | } | |
1661 | } |