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