diff options
author | Brian Behlendorf <[email protected]> | 2009-10-09 15:37:29 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2009-10-09 15:37:29 -0700 |
commit | 5c36312909256a886495b56fb0cd75ebaa25615b (patch) | |
tree | 65e90fd40a3f60e673f0e2f078f959d36262ee7c /lib | |
parent | 45d1cae3b8c949ecc391dd7a5b81963b34c71c29 (diff) |
Script update-zfs.sh updated to include libefi library
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libefi/include/sys/efi_partition.h | 241 | ||||
-rw-r--r-- | lib/libefi/include/sys/uuid.h | 100 | ||||
-rw-r--r-- | lib/libefi/rdwr_efi.c | 1211 |
3 files changed, 1552 insertions, 0 deletions
diff --git a/lib/libefi/include/sys/efi_partition.h b/lib/libefi/include/sys/efi_partition.h new file mode 100644 index 000000000..46fb424c0 --- /dev/null +++ b/lib/libefi/include/sys/efi_partition.h @@ -0,0 +1,241 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_EFI_PARTITION_H +#define _SYS_EFI_PARTITION_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/uuid.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * GUID Partition Table Header + */ + +#define EFI_LABEL_SIZE 512 +#define LEN_EFI_PAD (EFI_LABEL_SIZE - \ + ((5 * sizeof (diskaddr_t)) + \ + (7 * sizeof (uint_t)) + \ + (8 * sizeof (char)) + \ + (1 * (sizeof (struct uuid))))) + +#define EFI_SIGNATURE 0x5452415020494645ULL + +/* EFI Guid Partition Table Header -- little endian on-disk format */ +typedef struct efi_gpt { + uint64_t efi_gpt_Signature; + uint_t efi_gpt_Revision; + uint_t efi_gpt_HeaderSize; + uint_t efi_gpt_HeaderCRC32; + uint_t efi_gpt_Reserved1; + diskaddr_t efi_gpt_MyLBA; + diskaddr_t efi_gpt_AlternateLBA; + diskaddr_t efi_gpt_FirstUsableLBA; + diskaddr_t efi_gpt_LastUsableLBA; + struct uuid efi_gpt_DiskGUID; + diskaddr_t efi_gpt_PartitionEntryLBA; + uint_t efi_gpt_NumberOfPartitionEntries; + uint_t efi_gpt_SizeOfPartitionEntry; + uint_t efi_gpt_PartitionEntryArrayCRC32; + char efi_gpt_Reserved2[LEN_EFI_PAD]; +} efi_gpt_t; + +/* EFI Guid Partition Entry Attributes -- little endian format */ +typedef struct efi_gpe_Attrs { + uint32_t PartitionAttrs :16, + Reserved2 :16; + uint32_t Reserved1 :31, + RequiredPartition :1; +} efi_gpe_Attrs_t; + +/* + * 6a96237f-1dd2-11b2-99a6-080020736631 V_UNASSIGNED (not used as such) + * 6a82cb45-1dd2-11b2-99a6-080020736631 V_BOOT + * 6a85cf4d-1dd2-11b2-99a6-080020736631 V_ROOT + * 6a87c46f-1dd2-11b2-99a6-080020736631 V_SWAP + * 6a898cc3-1dd2-11b2-99a6-080020736631 V_USR + * 6a8b642b-1dd2-11b2-99a6-080020736631 V_BACKUP + * 6a8d2ac7-1dd2-11b2-99a6-080020736631 V_STAND (not used) + * 6a8ef2e9-1dd2-11b2-99a6-080020736631 V_VAR + * 6a90ba39-1dd2-11b2-99a6-080020736631 V_HOME + * 6a9283a5-1dd2-11b2-99a6-080020736631 V_ALTSCTR + * 6a945a3b-1dd2-11b2-99a6-080020736631 V_CACHE + */ + +#define EFI_UNUSED { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } +#define EFI_RESV1 { 0x6a96237f, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_BOOT { 0x6a82cb45, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_ROOT { 0x6a85cf4d, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_SWAP { 0x6a87c46f, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_USR { 0x6a898cc3, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_BACKUP { 0x6a8b642b, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_RESV2 { 0x6a8d2ac7, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_VAR { 0x6a8ef2e9, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_HOME { 0x6a90ba39, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_ALTSCTR { 0x6a9283a5, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_RESERVED { 0x6a945a3b, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_SYSTEM { 0xC12A7328, 0xF81F, 0x11d2, 0xBA, 0x4B, \ + { 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B } } +#define EFI_LEGACY_MBR { 0x024DEE41, 0x33E7, 0x11d3, 0x9D, 0x69, \ + { 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F } } +#define EFI_RESV3 { 0x6a9630d1, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_RESV4 { 0x6a980767, 0x1dd2, 0x11b2, 0x99, 0xa6, \ + { 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } } +#define EFI_MSFT_RESV { 0xE3C9E316, 0x0B5C, 0x4DB8, 0x81, 0x7D, \ + { 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE } } +#define EFI_DELL_BASIC { 0xebd0a0a2, 0xb9e5, 0x4433, 0x87, 0xc0, \ + { 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } } +#define EFI_DELL_RAID { 0xa19d880f, 0x05fc, 0x4d3b, 0xa0, 0x06, \ + { 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e } } +#define EFI_DELL_SWAP { 0x0657fd6d, 0xa4ab, 0x43c4, 0x84, 0xe5, \ + { 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f } } +#define EFI_DELL_LVM { 0xe6d6d379, 0xf507, 0x44c2, 0xa2, 0x3c, \ + { 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28 } } +#define EFI_DELL_RESV { 0x8da63339, 0x0007, 0x60c0, 0xc4, 0x36, \ + { 0x08, 0x3a, 0xc8, 0x23, 0x09, 0x08 } } +#define EFI_AAPL_HFS { 0x48465300, 0x0000, 0x11aa, 0xaa, 0x11, \ + { 0x00, 0x30, 0x65, 0x43, 0xec, 0xac } } +#define EFI_AAPL_UFS { 0x55465300, 0x0000, 0x11aa, 0xaa, 0x11, \ + { 0x00, 0x30, 0x65, 0x43, 0xec, 0xac } } + +/* minimum # of bytes for partition table entires, per EFI spec */ +#define EFI_MIN_ARRAY_SIZE (16 * 1024) + +#define EFI_PART_NAME_LEN 36 + +/* size of the "reserved" partition, in blocks */ +#define EFI_MIN_RESV_SIZE (16 * 1024) + +/* EFI Guid Partition Entry */ +typedef struct efi_gpe { + struct uuid efi_gpe_PartitionTypeGUID; + struct uuid efi_gpe_UniquePartitionGUID; + diskaddr_t efi_gpe_StartingLBA; + diskaddr_t efi_gpe_EndingLBA; + efi_gpe_Attrs_t efi_gpe_Attributes; + ushort_t efi_gpe_PartitionName[EFI_PART_NAME_LEN]; +} efi_gpe_t; + +/* + * passed to the useful (we hope) routines (efi_alloc_and_read and + * efi_write) that take this VTOC-like struct. These routines handle + * converting this struct into the EFI struct, generate UUIDs and + * checksums, and perform any necessary byte-swapping to the on-disk + * format. + */ +/* Solaris library abstraction for EFI partitons */ +typedef struct dk_part { + diskaddr_t p_start; /* starting LBA */ + diskaddr_t p_size; /* size in blocks */ + struct uuid p_guid; /* partion type GUID */ + ushort_t p_tag; /* converted to part'n type GUID */ + ushort_t p_flag; /* attributes */ + char p_name[EFI_PART_NAME_LEN]; /* partition name */ + struct uuid p_uguid; /* unique partition GUID */ + uint_t p_resv[8]; /* future use - set to zero */ +} dk_part_t; + +/* Solaris library abstraction for an EFI GPT */ +#define EFI_VERSION102 0x00010002 +#define EFI_VERSION100 0x00010000 +#define EFI_VERSION_CURRENT EFI_VERSION100 +typedef struct dk_gpt { + uint_t efi_version; /* set to EFI_VERSION_CURRENT */ + uint_t efi_nparts; /* number of partitions below */ + uint_t efi_part_size; /* size of each partition entry */ + /* efi_part_size is unused */ + uint_t efi_lbasize; /* size of block in bytes */ + diskaddr_t efi_last_lba; /* last block on the disk */ + diskaddr_t efi_first_u_lba; /* first block after labels */ + diskaddr_t efi_last_u_lba; /* last block before backup labels */ + struct uuid efi_disk_uguid; /* unique disk GUID */ + uint_t efi_flags; + uint_t efi_reserved1; /* future use - set to zero */ + diskaddr_t efi_altern_lba; /* lba of alternate GPT header */ + uint_t efi_reserved[12]; /* future use - set to zero */ + struct dk_part efi_parts[1]; /* array of partitions */ +} dk_gpt_t; + +/* possible values for "efi_flags" */ +#define EFI_GPT_PRIMARY_CORRUPT 0x1 /* primary label corrupt */ + +/* the private ioctl between libefi and the driver */ +typedef struct dk_efi { + diskaddr_t dki_lba; /* starting block */ + len_t dki_length; /* length in bytes */ + union { + efi_gpt_t *_dki_data; + uint64_t _dki_data_64; + } dki_un; +#define dki_data dki_un._dki_data +#define dki_data_64 dki_un._dki_data_64 +} dk_efi_t; + +struct partition64 { + struct uuid p_type; + uint_t p_partno; + uint_t p_resv1; + diskaddr_t p_start; + diskaddr_t p_size; +}; + +/* + * Number of EFI partitions + */ +#define EFI_NUMPAR 9 + +#ifndef _KERNEL +extern int efi_alloc_and_init(int, uint32_t, struct dk_gpt **); +extern int efi_alloc_and_read(int, struct dk_gpt **); +extern int efi_write(int, struct dk_gpt *); +extern void efi_free(struct dk_gpt *); +extern int efi_type(int); +extern void efi_err_check(struct dk_gpt *); +extern int efi_auto_sense(int fd, struct dk_gpt **); +extern int efi_use_whole_disk(int fd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_EFI_PARTITION_H */ diff --git a/lib/libefi/include/sys/uuid.h b/lib/libefi/include/sys/uuid.h new file mode 100644 index 000000000..683643e31 --- /dev/null +++ b/lib/libefi/include/sys/uuid.h @@ -0,0 +1,100 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_UUID_H +#define _SYS_UUID_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The copyright in this file is taken from the original Leach + * & Salz UUID specification, from which this implementation + * is derived. + */ + +/* + * Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. + * Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & + * Digital Equipment Corporation, Maynard, Mass. Copyright (c) 1998 + * Microsoft. To anyone who acknowledges that this file is provided + * "AS IS" without any express or implied warranty: permission to use, + * copy, modify, and distribute this file for any purpose is hereby + * granted without fee, provided that the above copyright notices and + * this notice appears in all source code copies, and that none of the + * names of Open Software Foundation, Inc., Hewlett-Packard Company, + * or Digital Equipment Corporation be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. Neither Open Software + * Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital + * Equipment Corporation makes any representations about the + * suitability of this software for any purpose. + */ + +#include <sys/types.h> +#include <sys/byteorder.h> + +typedef struct { + uint8_t nodeID[6]; +} uuid_node_t; + +/* + * The uuid type used throughout when referencing uuids themselves + */ +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node_addr[6]; +}; + +#define UUID_LEN 16 + +#define UUID_PRINTABLE_STRING_LENGTH 37 + +typedef uchar_t uuid_t[UUID_LEN]; + +/* + * Convert a uuid to/from little-endian format + */ +#define UUID_LE_CONVERT(dest, src) \ +{ \ + (dest) = (src); \ + (dest).time_low = LE_32((dest).time_low); \ + (dest).time_mid = LE_16((dest).time_mid); \ + (dest).time_hi_and_version = LE_16((dest).time_hi_and_version); \ +} + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_UUID_H */ diff --git a/lib/libefi/rdwr_efi.c b/lib/libefi/rdwr_efi.c new file mode 100644 index 000000000..31eb3d3f6 --- /dev/null +++ b/lib/libefi/rdwr_efi.c @@ -0,0 +1,1211 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <strings.h> +#include <unistd.h> +#include <uuid/uuid.h> +#include <libintl.h> +#include <sys/types.h> +#include <sys/dkio.h> +#include <sys/vtoc.h> +#include <sys/mhd.h> +#include <sys/param.h> +#include <sys/dktp/fdisk.h> +#include <sys/efi_partition.h> +#include <sys/byteorder.h> +#include <sys/ddi.h> + +static struct uuid_to_ptag { + struct uuid uuid; +} conversion_array[] = { + { EFI_UNUSED }, + { EFI_BOOT }, + { EFI_ROOT }, + { EFI_SWAP }, + { EFI_USR }, + { EFI_BACKUP }, + { 0 }, /* STAND is never used */ + { EFI_VAR }, + { EFI_HOME }, + { EFI_ALTSCTR }, + { 0 }, /* CACHE (cachefs) is never used */ + { EFI_RESERVED }, + { EFI_SYSTEM }, + { EFI_LEGACY_MBR }, + { EFI_RESV3 }, + { EFI_RESV4 }, + { EFI_MSFT_RESV }, + { EFI_DELL_BASIC }, + { EFI_DELL_RAID }, + { EFI_DELL_SWAP }, + { EFI_DELL_LVM }, + { EFI_DELL_RESV }, + { EFI_AAPL_HFS }, + { EFI_AAPL_UFS } +}; + +/* + * Default vtoc information for non-SVr4 partitions + */ +struct dk_map2 default_vtoc_map[NDKMAP] = { + { V_ROOT, 0 }, /* a - 0 */ + { V_SWAP, V_UNMNT }, /* b - 1 */ + { V_BACKUP, V_UNMNT }, /* c - 2 */ + { V_UNASSIGNED, 0 }, /* d - 3 */ + { V_UNASSIGNED, 0 }, /* e - 4 */ + { V_UNASSIGNED, 0 }, /* f - 5 */ + { V_USR, 0 }, /* g - 6 */ + { V_UNASSIGNED, 0 }, /* h - 7 */ + +#if defined(_SUNOS_VTOC_16) + +#if defined(i386) || defined(__amd64) + { V_BOOT, V_UNMNT }, /* i - 8 */ + { V_ALTSCTR, 0 }, /* j - 9 */ + +#else +#error No VTOC format defined. +#endif /* defined(i386) */ + + { V_UNASSIGNED, 0 }, /* k - 10 */ + { V_UNASSIGNED, 0 }, /* l - 11 */ + { V_UNASSIGNED, 0 }, /* m - 12 */ + { V_UNASSIGNED, 0 }, /* n - 13 */ + { V_UNASSIGNED, 0 }, /* o - 14 */ + { V_UNASSIGNED, 0 }, /* p - 15 */ +#endif /* defined(_SUNOS_VTOC_16) */ +}; + +#ifdef DEBUG +int efi_debug = 1; +#else +int efi_debug = 0; +#endif + +extern unsigned int efi_crc32(const unsigned char *, unsigned int); +static int efi_read(int, struct dk_gpt *); + +static int +read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize) +{ + struct dk_minfo disk_info; + + if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1) + return (errno); + *capacity = disk_info.dki_capacity; + *lbsize = disk_info.dki_lbsize; + return (0); +} + +/* + * the number of blocks the EFI label takes up (round up to nearest + * block) + */ +#define NBLOCKS(p, l) (1 + ((((p) * (int)sizeof (efi_gpe_t)) + \ + ((l) - 1)) / (l))) +/* number of partitions -- limited by what we can malloc */ +#define MAX_PARTS ((4294967295UL - sizeof (struct dk_gpt)) / \ + sizeof (struct dk_part)) + +int +efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc) +{ + diskaddr_t capacity; + uint_t lbsize; + uint_t nblocks; + size_t length; + struct dk_gpt *vptr; + struct uuid uuid; + + if (read_disk_info(fd, &capacity, &lbsize) != 0) { + if (efi_debug) + (void) fprintf(stderr, + "couldn't read disk information\n"); + return (-1); + } + + nblocks = NBLOCKS(nparts, lbsize); + if ((nblocks * lbsize) < EFI_MIN_ARRAY_SIZE + lbsize) { + /* 16K plus one block for the GPT */ + nblocks = EFI_MIN_ARRAY_SIZE / lbsize + 1; + } + + if (nparts > MAX_PARTS) { + if (efi_debug) { + (void) fprintf(stderr, + "the maximum number of partitions supported is %lu\n", + MAX_PARTS); + } + return (-1); + } + + length = sizeof (struct dk_gpt) + + sizeof (struct dk_part) * (nparts - 1); + + if ((*vtoc = calloc(length, 1)) == NULL) + return (-1); + + vptr = *vtoc; + + vptr->efi_version = EFI_VERSION_CURRENT; + vptr->efi_lbasize = lbsize; + vptr->efi_nparts = nparts; + /* + * add one block here for the PMBR; on disks with a 512 byte + * block size and 128 or fewer partitions, efi_first_u_lba + * should work out to "34" + */ + vptr->efi_first_u_lba = nblocks + 1; + vptr->efi_last_lba = capacity - 1; + vptr->efi_altern_lba = capacity -1; + vptr->efi_last_u_lba = vptr->efi_last_lba - nblocks; + + (void) uuid_generate((uchar_t *)&uuid); + UUID_LE_CONVERT(vptr->efi_disk_uguid, uuid); + return (0); +} + +/* + * Read EFI - return partition number upon success. + */ +int +efi_alloc_and_read(int fd, struct dk_gpt **vtoc) +{ + int rval; + uint32_t nparts; + int length; + + /* figure out the number of entries that would fit into 16K */ + nparts = EFI_MIN_ARRAY_SIZE / sizeof (efi_gpe_t); + length = (int) sizeof (struct dk_gpt) + + (int) sizeof (struct dk_part) * (nparts - 1); + if ((*vtoc = calloc(length, 1)) == NULL) + return (VT_ERROR); + + (*vtoc)->efi_nparts = nparts; + rval = efi_read(fd, *vtoc); + + if ((rval == VT_EINVAL) && (*vtoc)->efi_nparts > nparts) { + void *tmp; + length = (int) sizeof (struct dk_gpt) + + (int) sizeof (struct dk_part) * + ((*vtoc)->efi_nparts - 1); + nparts = (*vtoc)->efi_nparts; + if ((tmp = realloc(*vtoc, length)) == NULL) { + free (*vtoc); + *vtoc = NULL; + return (VT_ERROR); + } else { + *vtoc = tmp; + rval = efi_read(fd, *vtoc); + } + } + + if (rval < 0) { + if (efi_debug) { + (void) fprintf(stderr, + "read of EFI table failed, rval=%d\n", rval); + } + free (*vtoc); + *vtoc = NULL; + } + + return (rval); +} + +static int +efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) +{ + void *data = dk_ioc->dki_data; + int error; + + dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data; + error = ioctl(fd, cmd, (void *)dk_ioc); + dk_ioc->dki_data = data; + + return (error); +} + +static int +check_label(int fd, dk_efi_t *dk_ioc) +{ + efi_gpt_t *efi; + uint_t crc; + + if (efi_ioctl(fd, DKIOCGETEFI, dk_ioc) == -1) { + switch (errno) { + case EIO: + return (VT_EIO); + default: + return (VT_ERROR); + } + } + efi = dk_ioc->dki_data; + if (efi->efi_gpt_Signature != LE_64(EFI_SIGNATURE)) { + if (efi_debug) + (void) fprintf(stderr, + "Bad EFI signature: 0x%llx != 0x%llx\n", + (long long)efi->efi_gpt_Signature, + (long long)LE_64(EFI_SIGNATURE)); + return (VT_EINVAL); + } + + /* + * check CRC of the header; the size of the header should + * never be larger than one block + */ + crc = efi->efi_gpt_HeaderCRC32; + efi->efi_gpt_HeaderCRC32 = 0; + + if (((len_t)LE_32(efi->efi_gpt_HeaderSize) > dk_ioc->dki_length) || + crc != LE_32(efi_crc32((unsigned char *)efi, + LE_32(efi->efi_gpt_HeaderSize)))) { + if (efi_debug) + (void) fprintf(stderr, + "Bad EFI CRC: 0x%x != 0x%x\n", + crc, + LE_32(efi_crc32((unsigned char *)efi, + sizeof (struct efi_gpt)))); + return (VT_EINVAL); + } + + return (0); +} + +static int +efi_read(int fd, struct dk_gpt *vtoc) +{ + int i, j; + int label_len; + int rval = 0; + int md_flag = 0; + int vdc_flag = 0; + struct dk_minfo disk_info; + dk_efi_t dk_ioc; + efi_gpt_t *efi; + efi_gpe_t *efi_parts; + struct dk_cinfo dki_info; + uint32_t user_length; + boolean_t legacy_label = B_FALSE; + + /* + * get the partition number for this file descriptor. + */ + if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { + if (efi_debug) { + (void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno); + } + switch (errno) { + case EIO: + return (VT_EIO); + case EINVAL: + return (VT_EINVAL); + default: + return (VT_ERROR); + } + } + if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) && + (strncmp(dki_info.dki_dname, "md", 3) == 0)) { + md_flag++; + } else if ((strncmp(dki_info.dki_cname, "vdc", 4) == 0) && + (strncmp(dki_info.dki_dname, "vdc", 4) == 0)) { + /* + * The controller and drive name "vdc" (virtual disk client) + * indicates a LDoms virtual disk. + */ + vdc_flag++; + } + + /* get the LBA size */ + if (ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) { + if (efi_debug) { + (void) fprintf(stderr, + "assuming LBA 512 bytes %d\n", + errno); + } + disk_info.dki_lbsize = DEV_BSIZE; + } + if (disk_info.dki_lbsize == 0) { + if (efi_debug) { + (void) fprintf(stderr, + "efi_read: assuming LBA 512 bytes\n"); + } + disk_info.dki_lbsize = DEV_BSIZE; + } + /* + * Read the EFI GPT to figure out how many partitions we need + * to deal with. + */ + dk_ioc.dki_lba = 1; + if (NBLOCKS(vtoc->efi_nparts, disk_info.dki_lbsize) < 34) { + label_len = EFI_MIN_ARRAY_SIZE + disk_info.dki_lbsize; + } else { + label_len = vtoc->efi_nparts * (int) sizeof (efi_gpe_t) + + disk_info.dki_lbsize; + if (label_len % disk_info.dki_lbsize) { + /* pad to physical sector size */ + label_len += disk_info.dki_lbsize; + label_len &= ~(disk_info.dki_lbsize - 1); + } + } + + if ((dk_ioc.dki_data = calloc(label_len, 1)) == NULL) + return (VT_ERROR); + + dk_ioc.dki_length = disk_info.dki_lbsize; + user_length = vtoc->efi_nparts; + efi = dk_ioc.dki_data; + if (md_flag) { + dk_ioc.dki_length = label_len; + if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc) == -1) { + switch (errno) { + case EIO: + return (VT_EIO); + default: + return (VT_ERROR); + } + } + } else if ((rval = check_label(fd, &dk_ioc)) == VT_EINVAL) { + /* + * No valid label here; try the alternate. Note that here + * we just read GPT header and save it into dk_ioc.data, + * Later, we will read GUID partition entry array if we + * can get valid GPT header. + */ + + /* + * This is a workaround for legacy systems. In the past, the + * last sector of SCSI disk was invisible on x86 platform. At + * that time, backup label was saved on the next to the last + * sector. It is possible for users to move a disk from previous + * solaris system to present system. Here, we attempt to search + * legacy backup EFI label first. + */ + dk_ioc.dki_lba = disk_info.dki_capacity - 2; + dk_ioc.dki_length = disk_info.dki_lbsize; + rval = check_label(fd, &dk_ioc); + if (rval == VT_EINVAL) { + /* + * we didn't find legacy backup EFI label, try to + * search backup EFI label in the last block. + */ + dk_ioc.dki_lba = disk_info.dki_capacity - 1; + dk_ioc.dki_length = disk_info.dki_lbsize; + rval = check_label(fd, &dk_ioc); + if (rval == 0) { + legacy_label = B_TRUE; + if (efi_debug) + (void) fprintf(stderr, + "efi_read: primary label corrupt; " + "using EFI backup label located on" + " the last block\n"); + } + } else { + if ((efi_debug) && (rval == 0)) + (void) fprintf(stderr, "efi_read: primary label" + " corrupt; using legacy EFI backup label " + " located on the next to last block\n"); + } + + if (rval == 0) { + dk_ioc.dki_lba = LE_64(efi->efi_gpt_PartitionEntryLBA); + vtoc->efi_flags |= EFI_GPT_PRIMARY_CORRUPT; + vtoc->efi_nparts = + LE_32(efi->efi_gpt_NumberOfPartitionEntries); + /* + * Partition tables are between backup GPT header + * table and ParitionEntryLBA (the starting LBA of + * the GUID partition entries array). Now that we + * already got valid GPT header and saved it in + * dk_ioc.dki_data, we try to get GUID partition + * entry array here. + */ + /* LINTED */ + dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data + + disk_info.dki_lbsize); + if (legacy_label) + dk_ioc.dki_length = disk_info.dki_capacity - 1 - + dk_ioc.dki_lba; + else + dk_ioc.dki_length = disk_info.dki_capacity - 2 - + dk_ioc.dki_lba; + dk_ioc.dki_length *= disk_info.dki_lbsize; + if (dk_ioc.dki_length > + ((len_t)label_len - sizeof (*dk_ioc.dki_data))) { + rval = VT_EINVAL; + } else { + /* + * read GUID partition entry array + */ + rval = efi_ioctl(fd, DKIOCGETEFI, &dk_ioc); + } + } + + } else if (rval == 0) { + + dk_ioc.dki_lba = LE_64(efi->efi_gpt_PartitionEntryLBA); + /* LINTED */ + dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data + + disk_info.dki_lbsize); + dk_ioc.dki_length = label_len - disk_info.dki_lbsize; + rval = efi_ioctl(fd, DKIOCGETEFI, &dk_ioc); + + } else if (vdc_flag && rval == VT_ERROR && errno == EINVAL) { + /* + * When the device is a LDoms virtual disk, the DKIOCGETEFI + * ioctl can fail with EINVAL if the virtual disk backend + * is a ZFS volume serviced by a domain running an old version + * of Solaris. This is because the DKIOCGETEFI ioctl was + * initially incorrectly implemented for a ZFS volume and it + * expected the GPT and GPE to be retrieved with a single ioctl. + * So we try to read the GPT and the GPE using that old style + * ioctl. + */ + dk_ioc.dki_lba = 1; + dk_ioc.dki_length = label_len; + rval = check_label(fd, &dk_ioc); + } + + if (rval < 0) { + free(efi); + return (rval); + } + + /* LINTED -- always longlong aligned */ + efi_parts = (efi_gpe_t *)(((char *)efi) + disk_info.dki_lbsize); + + /* + * Assemble this into a "dk_gpt" struct for easier + * digestibility by applications. + */ + vtoc->efi_version = LE_32(efi->efi_gpt_Revision); + vtoc->efi_nparts = LE_32(efi->efi_gpt_NumberOfPartitionEntries); + vtoc->efi_part_size = LE_32(efi->efi_gpt_SizeOfPartitionEntry); + vtoc->efi_lbasize = disk_info.dki_lbsize; + vtoc->efi_last_lba = disk_info.dki_capacity - 1; + vtoc->efi_first_u_lba = LE_64(efi->efi_gpt_FirstUsableLBA); + vtoc->efi_last_u_lba = LE_64(efi->efi_gpt_LastUsableLBA); + vtoc->efi_altern_lba = LE_64(efi->efi_gpt_AlternateLBA); + UUID_LE_CONVERT(vtoc->efi_disk_uguid, efi->efi_gpt_DiskGUID); + + /* + * If the array the user passed in is too small, set the length + * to what it needs to be and return + */ + if (user_length < vtoc->efi_nparts) { + return (VT_EINVAL); + } + + for (i = 0; i < vtoc->efi_nparts; i++) { + + UUID_LE_CONVERT(vtoc->efi_parts[i].p_guid, + efi_parts[i].efi_gpe_PartitionTypeGUID); + + for (j = 0; + j < sizeof (conversion_array) + / sizeof (struct uuid_to_ptag); j++) { + + if (bcmp(&vtoc->efi_parts[i].p_guid, + &conversion_array[j].uuid, + sizeof (struct uuid)) == 0) { + vtoc->efi_parts[i].p_tag = j; + break; + } + } + if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) + continue; + vtoc->efi_parts[i].p_flag = + LE_16(efi_parts[i].efi_gpe_Attributes.PartitionAttrs); + vtoc->efi_parts[i].p_start = + LE_64(efi_parts[i].efi_gpe_StartingLBA); + vtoc->efi_parts[i].p_size = + LE_64(efi_parts[i].efi_gpe_EndingLBA) - + vtoc->efi_parts[i].p_start + 1; + for (j = 0; j < EFI_PART_NAME_LEN; j++) { + vtoc->efi_parts[i].p_name[j] = + (uchar_t)LE_16( + efi_parts[i].efi_gpe_PartitionName[j]); + } + + UUID_LE_CONVERT(vtoc->efi_parts[i].p_uguid, + efi_parts[i].efi_gpe_UniquePartitionGUID); + } + free(efi); + + return (dki_info.dki_partition); +} + +/* writes a "protective" MBR */ +static int +write_pmbr(int fd, struct dk_gpt *vtoc) +{ + dk_efi_t dk_ioc; + struct mboot mb; + uchar_t *cp; + diskaddr_t size_in_lba; + uchar_t *buf; + int len; + + len = (vtoc->efi_lbasize == 0) ? sizeof (mb) : vtoc->efi_lbasize; + buf = calloc(len, 1); + + /* + * Preserve any boot code and disk signature if the first block is + * already an MBR. + */ + dk_ioc.dki_lba = 0; + dk_ioc.dki_length = len; + /* LINTED -- always longlong aligned */ + dk_ioc.dki_data = (efi_gpt_t *)buf; + if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc) == -1) { + (void *) memcpy(&mb, buf, sizeof (mb)); + bzero(&mb, sizeof (mb)); + mb.signature = LE_16(MBB_MAGIC); + } else { + (void *) memcpy(&mb, buf, sizeof (mb)); + if (mb.signature != LE_16(MBB_MAGIC)) { + bzero(&mb, sizeof (mb)); + mb.signature = LE_16(MBB_MAGIC); + } + } + + bzero(&mb.parts, sizeof (mb.parts)); + cp = (uchar_t *)&mb.parts[0]; + /* bootable or not */ + *cp++ = 0; + /* beginning CHS; 0xffffff if not representable */ + *cp++ = 0xff; + *cp++ = 0xff; + *cp++ = 0xff; + /* OS type */ + *cp++ = EFI_PMBR; + /* ending CHS; 0xffffff if not representable */ + *cp++ = 0xff; + *cp++ = 0xff; + *cp++ = 0xff; + /* starting LBA: 1 (little endian format) by EFI definition */ + *cp++ = 0x01; + *cp++ = 0x00; + *cp++ = 0x00; + *cp++ = 0x00; + /* ending LBA: last block on the disk (little endian format) */ + size_in_lba = vtoc->efi_last_lba; + if (size_in_lba < 0xffffffff) { + *cp++ = (size_in_lba & 0x000000ff); + *cp++ = (size_in_lba & 0x0000ff00) >> 8; + *cp++ = (size_in_lba & 0x00ff0000) >> 16; + *cp++ = (size_in_lba & 0xff000000) >> 24; + } else { + *cp++ = 0xff; + *cp++ = 0xff; + *cp++ = 0xff; + *cp++ = 0xff; + } + + (void *) memcpy(buf, &mb, sizeof (mb)); + /* LINTED -- always longlong aligned */ + dk_ioc.dki_data = (efi_gpt_t *)buf; + dk_ioc.dki_lba = 0; + dk_ioc.dki_length = len; + if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { + free(buf); + switch (errno) { + case EIO: + return (VT_EIO); + case EINVAL: + return (VT_EINVAL); + default: + return (VT_ERROR); + } + } + free(buf); + return (0); +} + +/* make sure the user specified something reasonable */ +static int +check_input(struct dk_gpt *vtoc) +{ + int resv_part = -1; + int i, j; + diskaddr_t istart, jstart, isize, jsize, endsect; + + /* + * Sanity-check the input (make sure no partitions overlap) + */ + for (i = 0; i < vtoc->efi_nparts; i++) { + /* It can't be unassigned and have an actual size */ + if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) && + (vtoc->efi_parts[i].p_size != 0)) { + if (efi_debug) { + (void) fprintf(stderr, +"partition %d is \"unassigned\" but has a size of %llu", + i, + vtoc->efi_parts[i].p_size); + } + return (VT_EINVAL); + } + if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) { + if (uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid)) + continue; + /* we have encountered an unknown uuid */ + vtoc->efi_parts[i].p_tag = 0xff; + } + if (vtoc->efi_parts[i].p_tag == V_RESERVED) { + if (resv_part != -1) { + if (efi_debug) { + (void) fprintf(stderr, +"found duplicate reserved partition at %d\n", + i); + } + return (VT_EINVAL); + } + resv_part = i; + } + if ((vtoc->efi_parts[i].p_start < vtoc->efi_first_u_lba) || + (vtoc->efi_parts[i].p_start > vtoc->efi_last_u_lba)) { + if (efi_debug) { + (void) fprintf(stderr, + "Partition %d starts at %llu. ", + i, + vtoc->efi_parts[i].p_start); + (void) fprintf(stderr, + "It must be between %llu and %llu.\n", + vtoc->efi_first_u_lba, + vtoc->efi_last_u_lba); + } + return (VT_EINVAL); + } + if ((vtoc->efi_parts[i].p_start + + vtoc->efi_parts[i].p_size < + vtoc->efi_first_u_lba) || + (vtoc->efi_parts[i].p_start + + vtoc->efi_parts[i].p_size > + vtoc->efi_last_u_lba + 1)) { + if (efi_debug) { + (void) fprintf(stderr, + "Partition %d ends at %llu. ", + i, + vtoc->efi_parts[i].p_start + + vtoc->efi_parts[i].p_size); + (void) fprintf(stderr, + "It must be between %llu and %llu.\n", + vtoc->efi_first_u_lba, + vtoc->efi_last_u_lba); + } + return (VT_EINVAL); + } + + for (j = 0; j < vtoc->efi_nparts; j++) { + isize = vtoc->efi_parts[i].p_size; + jsize = vtoc->efi_parts[j].p_size; + istart = vtoc->efi_parts[i].p_start; + jstart = vtoc->efi_parts[j].p_start; + if ((i != j) && (isize != 0) && (jsize != 0)) { + endsect = jstart + jsize -1; + if ((jstart <= istart) && + (istart <= endsect)) { + if (efi_debug) { + (void) fprintf(stderr, +"Partition %d overlaps partition %d.", + i, j); + } + return (VT_EINVAL); + } + } + } + } + /* just a warning for now */ + if ((resv_part == -1) && efi_debug) { + (void) fprintf(stderr, + "no reserved partition found\n"); + } + return (0); +} + +/* + * add all the unallocated space to the current label + */ +int +efi_use_whole_disk(int fd) +{ + struct dk_gpt *efi_label; + int rval; + int i; + uint_t phy_last_slice = 0; + diskaddr_t pl_start = 0; + diskaddr_t pl_size; + + rval = efi_alloc_and_read(fd, &efi_label); + if (rval < 0) { + return (rval); + } + + /* find the last physically non-zero partition */ + for (i = 0; i < efi_label->efi_nparts - 2; i ++) { + if (pl_start < efi_label->efi_parts[i].p_start) { + pl_start = efi_label->efi_parts[i].p_start; + phy_last_slice = i; + } + } + pl_size = efi_label->efi_parts[phy_last_slice].p_size; + + /* + * If alter_lba is 1, we are using the backup label. + * Since we can locate the backup label by disk capacity, + * there must be no unallocated space. + */ + if ((efi_label->efi_altern_lba == 1) || (efi_label->efi_altern_lba + >= efi_label->efi_last_lba)) { + if (efi_debug) { + (void) fprintf(stderr, + "efi_use_whole_disk: requested space not found\n"); + } + efi_free(efi_label); + return (VT_ENOSPC); + } + + /* + * If there is space between the last physically non-zero partition + * and the reserved partition, just add the unallocated space to this + * area. Otherwise, the unallocated space is added to the last + * physically non-zero partition. + */ + if (pl_start + pl_size - 1 == efi_label->efi_last_u_lba - + EFI_MIN_RESV_SIZE) { + efi_label->efi_parts[phy_last_slice].p_size += + efi_label->efi_last_lba - efi_label->efi_altern_lba; + } + + /* + * Move the reserved partition. There is currently no data in + * here except fabricated devids (which get generated via + * efi_write()). So there is no need to copy data. + */ + efi_label->efi_parts[efi_label->efi_nparts - 1].p_start += + efi_label->efi_last_lba - efi_label->efi_altern_lba; + efi_label->efi_last_u_lba += efi_label->efi_last_lba + - efi_label->efi_altern_lba; + + rval = efi_write(fd, efi_label); + if (rval < 0) { + if (efi_debug) { + (void) fprintf(stderr, + "efi_use_whole_disk:fail to write label, rval=%d\n", + rval); + } + efi_free(efi_label); + return (rval); + } + + efi_free(efi_label); + return (0); +} + + +/* + * write EFI label and backup label + */ +int +efi_write(int fd, struct dk_gpt *vtoc) +{ + dk_efi_t dk_ioc; + efi_gpt_t *efi; + efi_gpe_t *efi_parts; + int i, j; + struct dk_cinfo dki_info; + int md_flag = 0; + int nblocks; + diskaddr_t lba_backup_gpt_hdr; + + if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { + if (efi_debug) + (void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno); + switch (errno) { + case EIO: + return (VT_EIO); + case EINVAL: + return (VT_EINVAL); + default: + return (VT_ERROR); + } + } + + /* check if we are dealing wih a metadevice */ + if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) && + (strncmp(dki_info.dki_dname, "md", 3) == 0)) { + md_flag = 1; + } + + if (check_input(vtoc)) { + /* + * not valid; if it's a metadevice just pass it down + * because SVM will do its own checking + */ + if (md_flag == 0) { + return (VT_EINVAL); + } + } + + dk_ioc.dki_lba = 1; + if (NBLOCKS(vtoc->efi_nparts, vtoc->efi_lbasize) < 34) { + dk_ioc.dki_length = EFI_MIN_ARRAY_SIZE + vtoc->efi_lbasize; + } else { + dk_ioc.dki_length = NBLOCKS(vtoc->efi_nparts, + vtoc->efi_lbasize) * + vtoc->efi_lbasize; + } + + /* + * the number of blocks occupied by GUID partition entry array + */ + nblocks = dk_ioc.dki_length / vtoc->efi_lbasize - 1; + + /* + * Backup GPT header is located on the block after GUID + * partition entry array. Here, we calculate the address + * for backup GPT header. + */ + lba_backup_gpt_hdr = vtoc->efi_last_u_lba + 1 + nblocks; + if ((dk_ioc.dki_data = calloc(dk_ioc.dki_length, 1)) == NULL) + return (VT_ERROR); + + efi = dk_ioc.dki_data; + + /* stuff user's input into EFI struct */ + efi->efi_gpt_Signature = LE_64(EFI_SIGNATURE); + efi->efi_gpt_Revision = LE_32(vtoc->efi_version); /* 0x02000100 */ + efi->efi_gpt_HeaderSize = LE_32(sizeof (struct efi_gpt)); + efi->efi_gpt_Reserved1 = 0; + efi->efi_gpt_MyLBA = LE_64(1ULL); + efi->efi_gpt_AlternateLBA = LE_64(lba_backup_gpt_hdr); + efi->efi_gpt_FirstUsableLBA = LE_64(vtoc->efi_first_u_lba); + efi->efi_gpt_LastUsableLBA = LE_64(vtoc->efi_last_u_lba); + efi->efi_gpt_PartitionEntryLBA = LE_64(2ULL); + efi->efi_gpt_NumberOfPartitionEntries = LE_32(vtoc->efi_nparts); + efi->efi_gpt_SizeOfPartitionEntry = LE_32(sizeof (struct efi_gpe)); + UUID_LE_CONVERT(efi->efi_gpt_DiskGUID, vtoc->efi_disk_uguid); + + /* LINTED -- always longlong aligned */ + efi_parts = (efi_gpe_t *)((char *)dk_ioc.dki_data + vtoc->efi_lbasize); + + for (i = 0; i < vtoc->efi_nparts; i++) { + for (j = 0; + j < sizeof (conversion_array) / + sizeof (struct uuid_to_ptag); j++) { + + if (vtoc->efi_parts[i].p_tag == j) { + UUID_LE_CONVERT( + efi_parts[i].efi_gpe_PartitionTypeGUID, + conversion_array[j].uuid); + break; + } + } + + if (j == sizeof (conversion_array) / + sizeof (struct uuid_to_ptag)) { + /* + * If we didn't have a matching uuid match, bail here. + * Don't write a label with unknown uuid. + */ + if (efi_debug) { + (void) fprintf(stderr, + "Unknown uuid for p_tag %d\n", + vtoc->efi_parts[i].p_tag); + } + return (VT_EINVAL); + } + + efi_parts[i].efi_gpe_StartingLBA = + LE_64(vtoc->efi_parts[i].p_start); + efi_parts[i].efi_gpe_EndingLBA = + LE_64(vtoc->efi_parts[i].p_start + + vtoc->efi_parts[i].p_size - 1); + efi_parts[i].efi_gpe_Attributes.PartitionAttrs = + LE_16(vtoc->efi_parts[i].p_flag); + for (j = 0; j < EFI_PART_NAME_LEN; j++) { + efi_parts[i].efi_gpe_PartitionName[j] = + LE_16((ushort_t)vtoc->efi_parts[i].p_name[j]); + } + if ((vtoc->efi_parts[i].p_tag != V_UNASSIGNED) && + uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_uguid)) { + (void) uuid_generate((uchar_t *) + &vtoc->efi_parts[i].p_uguid); + } + bcopy(&vtoc->efi_parts[i].p_uguid, + &efi_parts[i].efi_gpe_UniquePartitionGUID, + sizeof (uuid_t)); + } + efi->efi_gpt_PartitionEntryArrayCRC32 = + LE_32(efi_crc32((unsigned char *)efi_parts, + vtoc->efi_nparts * (int)sizeof (struct efi_gpe))); + efi->efi_gpt_HeaderCRC32 = + LE_32(efi_crc32((unsigned char *)efi, sizeof (struct efi_gpt))); + + if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { + free(dk_ioc.dki_data); + switch (errno) { + case EIO: + return (VT_EIO); + case EINVAL: + return (VT_EINVAL); + default: + return (VT_ERROR); + } + } + /* if it's a metadevice we're done */ + if (md_flag) { + free(dk_ioc.dki_data); + return (0); + } + + /* write backup partition array */ + dk_ioc.dki_lba = vtoc->efi_last_u_lba + 1; + dk_ioc.dki_length -= vtoc->efi_lbasize; + /* LINTED */ + dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data + + vtoc->efi_lbasize); + + if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { + /* + * we wrote the primary label okay, so don't fail + */ + if (efi_debug) { + (void) fprintf(stderr, + "write of backup partitions to block %llu " + "failed, errno %d\n", + vtoc->efi_last_u_lba + 1, + errno); + } + } + /* + * now swap MyLBA and AlternateLBA fields and write backup + * partition table header + */ + dk_ioc.dki_lba = lba_backup_gpt_hdr; + dk_ioc.dki_length = vtoc->efi_lbasize; + /* LINTED */ + dk_ioc.dki_data = (efi_gpt_t *)((char *)dk_ioc.dki_data - + vtoc->efi_lbasize); + efi->efi_gpt_AlternateLBA = LE_64(1ULL); + efi->efi_gpt_MyLBA = LE_64(lba_backup_gpt_hdr); + efi->efi_gpt_PartitionEntryLBA = LE_64(vtoc->efi_last_u_lba + 1); + efi->efi_gpt_HeaderCRC32 = 0; + efi->efi_gpt_HeaderCRC32 = + LE_32(efi_crc32((unsigned char *)dk_ioc.dki_data, + sizeof (struct efi_gpt))); + + if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { + if (efi_debug) { + (void) fprintf(stderr, + "write of backup header to block %llu failed, " + "errno %d\n", + lba_backup_gpt_hdr, + errno); + } + } + /* write the PMBR */ + (void) write_pmbr(fd, vtoc); + free(dk_ioc.dki_data); + return (0); +} + +void +efi_free(struct dk_gpt *ptr) +{ + free(ptr); +} + +/* + * Input: File descriptor + * Output: 1 if disk has an EFI label, or > 2TB with no VTOC or legacy MBR. + * Otherwise 0. + */ +int +efi_type(int fd) +{ + struct vtoc vtoc; + struct extvtoc extvtoc; + + if (ioctl(fd, DKIOCGEXTVTOC, &extvtoc) == -1) { + if (errno == ENOTSUP) + return (1); + else if (errno == ENOTTY) { + if (ioctl(fd, DKIOCGVTOC, &vtoc) == -1) + if (errno == ENOTSUP) + return (1); + } + } + return (0); +} + +void +efi_err_check(struct dk_gpt *vtoc) +{ + int resv_part = -1; + int i, j; + diskaddr_t istart, jstart, isize, jsize, endsect; + int overlap = 0; + + /* + * make sure no partitions overlap + */ + for (i = 0; i < vtoc->efi_nparts; i++) { + /* It can't be unassigned and have an actual size */ + if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) && + (vtoc->efi_parts[i].p_size != 0)) { + (void) fprintf(stderr, + "partition %d is \"unassigned\" but has a size " + "of %llu\n", i, vtoc->efi_parts[i].p_size); + } + if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) { + continue; + } + if (vtoc->efi_parts[i].p_tag == V_RESERVED) { + if (resv_part != -1) { + (void) fprintf(stderr, + "found duplicate reserved partition at " + "%d\n", i); + } + resv_part = i; + if (vtoc->efi_parts[i].p_size != EFI_MIN_RESV_SIZE) + (void) fprintf(stderr, + "Warning: reserved partition size must " + "be %d sectors\n", EFI_MIN_RESV_SIZE); + } + if ((vtoc->efi_parts[i].p_start < vtoc->efi_first_u_lba) || + (vtoc->efi_parts[i].p_start > vtoc->efi_last_u_lba)) { + (void) fprintf(stderr, + "Partition %d starts at %llu\n", + i, + vtoc->efi_parts[i].p_start); + (void) fprintf(stderr, + "It must be between %llu and %llu.\n", + vtoc->efi_first_u_lba, + vtoc->efi_last_u_lba); + } + if ((vtoc->efi_parts[i].p_start + + vtoc->efi_parts[i].p_size < + vtoc->efi_first_u_lba) || + (vtoc->efi_parts[i].p_start + + vtoc->efi_parts[i].p_size > + vtoc->efi_last_u_lba + 1)) { + (void) fprintf(stderr, + "Partition %d ends at %llu\n", + i, + vtoc->efi_parts[i].p_start + + vtoc->efi_parts[i].p_size); + (void) fprintf(stderr, + "It must be between %llu and %llu.\n", + vtoc->efi_first_u_lba, + vtoc->efi_last_u_lba); + } + + for (j = 0; j < vtoc->efi_nparts; j++) { + isize = vtoc->efi_parts[i].p_size; + jsize = vtoc->efi_parts[j].p_size; + istart = vtoc->efi_parts[i].p_start; + jstart = vtoc->efi_parts[j].p_start; + if ((i != j) && (isize != 0) && (jsize != 0)) { + endsect = jstart + jsize -1; + if ((jstart <= istart) && + (istart <= endsect)) { + if (!overlap) { + (void) fprintf(stderr, + "label error: EFI Labels do not " + "support overlapping partitions\n"); + } + (void) fprintf(stderr, + "Partition %d overlaps partition " + "%d.\n", i, j); + overlap = 1; + } + } + } + } + /* make sure there is a reserved partition */ + if (resv_part == -1) { + (void) fprintf(stderr, + "no reserved partition found\n"); + } +} + +/* + * We need to get information necessary to construct a *new* efi + * label type + */ +int +efi_auto_sense(int fd, struct dk_gpt **vtoc) +{ + + int i; + + /* + * Now build the default partition table + */ + if (efi_alloc_and_init(fd, EFI_NUMPAR, vtoc) != 0) { + if (efi_debug) { + (void) fprintf(stderr, "efi_alloc_and_init failed.\n"); + } + return (-1); + } + + for (i = 0; i < min((*vtoc)->efi_nparts, V_NUMPAR); i++) { + (*vtoc)->efi_parts[i].p_tag = default_vtoc_map[i].p_tag; + (*vtoc)->efi_parts[i].p_flag = default_vtoc_map[i].p_flag; + (*vtoc)->efi_parts[i].p_start = 0; + (*vtoc)->efi_parts[i].p_size = 0; + } + /* + * Make constants first + * and variable partitions later + */ + + /* root partition - s0 128 MB */ + (*vtoc)->efi_parts[0].p_start = 34; + (*vtoc)->efi_parts[0].p_size = 262144; + + /* partition - s1 128 MB */ + (*vtoc)->efi_parts[1].p_start = 262178; + (*vtoc)->efi_parts[1].p_size = 262144; + + /* partition -s2 is NOT the Backup disk */ + (*vtoc)->efi_parts[2].p_tag = V_UNASSIGNED; + + /* partition -s6 /usr partition - HOG */ + (*vtoc)->efi_parts[6].p_start = 524322; + (*vtoc)->efi_parts[6].p_size = (*vtoc)->efi_last_u_lba - 524322 + - (1024 * 16); + + /* efi reserved partition - s9 16K */ + (*vtoc)->efi_parts[8].p_start = (*vtoc)->efi_last_u_lba - (1024 * 16); + (*vtoc)->efi_parts[8].p_size = (1024 * 16); + (*vtoc)->efi_parts[8].p_tag = V_RESERVED; + return (0); +} |