rdwr_efi.c revision 4f0dea16c412dcd8b2569402b08b2b58c0e0219b
/*
* 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
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <strings.h>
#include <unistd.h>
#include <libintl.h>
#include <sys/efi_partition.h>
#include <sys/byteorder.h>
static struct uuid_to_ptag {
} 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 }
};
/*
* Default vtoc information for non-SVr4 partitions
*/
{ V_ROOT, 0 }, /* a - 0 */
{ 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)
{ V_ALTSCTR, 0 }, /* j - 9 */
#else
#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) */
};
/*
* This is the size of the reserved partition.
* Valid in case of EFI labels.
*/
#ifdef DEBUG
int efi_debug = 1;
#else
int efi_debug = 0;
#endif
extern unsigned int efi_crc32(const unsigned char *, unsigned int);
static int
{
return (errno);
return (0);
}
/*
* the number of blocks the EFI label takes up (round up to nearest
* block)
*/
((l) - 1)) / (l)))
/* number of partitions -- limited by what we can malloc */
sizeof (struct dk_part))
int
{
if (efi_debug)
"couldn't read disk information\n");
return (-1);
}
/* 16K plus one block for the GPT */
}
if (efi_debug) {
"the maximum number of partitions supported is %lu\n",
}
return (-1);
}
return (-1);
/*
* 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"
*/
return (0);
}
/*
* Read EFI - return partition number upon success.
*/
int
{
int rval;
int length;
/* figure out the number of entries that would fit into 16K */
return (VT_ERROR);
void *tmp;
(int) sizeof (struct dk_part) *
return (VT_ERROR);
} else {
}
}
if (rval < 0) {
if (efi_debug) {
"read of EFI table failed, rval=%d\n", rval);
}
}
return (rval);
}
static int
{
int error;
return (error);
}
static int
{
switch (errno) {
case EIO:
return (VT_EIO);
default:
return (VT_ERROR);
}
}
if (efi_debug)
"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
*/
efi->efi_gpt_HeaderCRC32 = 0;
if (efi_debug)
"Bad EFI CRC: 0x%x != 0x%x\n",
crc,
sizeof (struct efi_gpt))));
return (VT_EINVAL);
}
return (0);
}
static int
{
int i, j;
int label_len;
int rval = 0;
int md_flag = 0;
/*
* get the partition number for this file descriptor.
*/
if (efi_debug)
switch (errno) {
case EIO:
return (VT_EIO);
case EINVAL:
return (VT_EINVAL);
default:
return (VT_ERROR);
}
}
md_flag++;
}
/* get the LBA size */
if (efi_debug) {
"assuming LBA 512 bytes %d\n",
errno);
}
}
if (disk_info.dki_lbsize == 0) {
if (efi_debug) {
"efi_read: assuming LBA 512 bytes\n");
}
}
/*
* Read the EFI GPT to figure out how many partitions we need
* to deal with.
*/
} else {
/* pad to physical sector size */
}
}
return (VT_ERROR);
if (md_flag) {
switch (errno) {
case EIO:
return (VT_EIO);
default:
return (VT_ERROR);
}
}
/* no valid label here; try the alternate */
if (rval != 0) {
/*
* Refer to bug6342431. This is a workaround for
* legacy.
*
* 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.
*/
"efi_read: primary label corrupt; "
"using legacy EFI backup label\n");
}
}
if (rval == 0) {
if (efi_debug) {
"efi_read: primary label corrupt; "
"using backup\n");
}
vtoc->efi_nparts =
/*
* partitions are between last usable LBA and
* backup partition header
*/
} else {
}
}
}
if (rval < 0) {
return (rval);
}
/* partitions start in the next block */
/* LINTED -- always longlong aligned */
/*
* Assemble this into a "dk_gpt" struct for easier
* digestibility by applications.
*/
/*
* If the array the user passed in is too small, set the length
* to what it needs to be and return
*/
return (VT_EINVAL);
}
for (i = 0; i < vtoc->efi_nparts; i++) {
for (j = 0;
j < sizeof (conversion_array) / sizeof (struct uuid_to_ptag);
j++) {
&conversion_array[j].uuid,
sizeof (struct uuid)) == 0) {
break;
}
}
continue;
for (j = 0; j < EFI_PART_NAME_LEN; j++) {
}
}
return (dki_info.dki_partition);
}
/* writes a "protective" MBR */
static int
{
/* bootable or not */
*cp++ = 0;
/* beginning CHS; 0xffffff if not representable */
*cp++ = 0xff;
*cp++ = 0xff;
*cp++ = 0xff;
/* OS type */
/* 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) */
if (size_in_lba < 0xffffffff) {
} else {
*cp++ = 0xff;
*cp++ = 0xff;
*cp++ = 0xff;
*cp++ = 0xff;
}
/* LINTED -- always longlong aligned */
switch (errno) {
case EIO:
return (VT_EIO);
case EINVAL:
return (VT_EINVAL);
default:
return (VT_ERROR);
}
}
return (0);
}
/* make sure the user specified something reasonable */
static int
{
int resv_part = -1;
int i, j;
/*
* 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 (efi_debug) {
"partition %d is \"unassigned\" but has a size of %llu",
i,
}
return (VT_EINVAL);
}
continue;
}
if (resv_part != -1) {
if (efi_debug) {
"found duplicate reserved partition at %d\n",
i);
}
return (VT_EINVAL);
}
resv_part = i;
}
if (efi_debug) {
"Partition %d starts at %llu. ",
i,
"It must be between %llu and %llu.\n",
}
return (VT_EINVAL);
}
vtoc->efi_first_u_lba) ||
if (efi_debug) {
"Partition %d ends at %llu. ",
i,
"It must be between %llu and %llu.\n",
}
return (VT_EINVAL);
}
for (j = 0; j < vtoc->efi_nparts; j++) {
if (efi_debug) {
"Partition %d overlaps partition %d.",
i, j);
}
return (VT_EINVAL);
}
}
}
}
/* just a warning for now */
"no reserved partition found\n");
}
return (0);
}
/*
* write EFI label and backup label
*/
int
{
int i, j;
int md_flag = 0;
if (efi_debug)
switch (errno) {
case EIO:
return (VT_EIO);
case EINVAL:
return (VT_EINVAL);
default:
return (VT_ERROR);
}
}
/* check if we are dealing wih a metadevice */
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);
}
}
} else {
vtoc->efi_lbasize) *
}
return (VT_ERROR);
/* stuff user's input into EFI struct */
efi->efi_gpt_Reserved1 = 0;
/* LINTED -- always longlong aligned */
for (i = 0; i < vtoc->efi_nparts; i++) {
for (j = 0;
j < sizeof (conversion_array) / sizeof (struct uuid_to_ptag);
j++) {
conversion_array[j].uuid);
}
}
for (j = 0; j < EFI_PART_NAME_LEN; j++) {
efi_parts[i].efi_gpe_PartitionName[j] =
}
(void) uuid_generate((uchar_t *)
}
sizeof (uuid_t));
}
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) {
return (0);
}
/* write backup partition array */
/*
* we wrote the primary label okay, so don't fail
*/
if (efi_debug) {
"write of backup partitions to block %llu "
"failed, errno %d\n",
errno);
}
}
/*
* now swap MyLBA and AlternateLBA fields and write backup
* partition table header
*/
efi->efi_gpt_HeaderCRC32 = 0;
sizeof (struct efi_gpt)));
if (efi_debug) {
"write of backup header to block %llu failed, "
"errno %d\n",
errno);
}
}
/* write the PMBR */
if (efi_debug) {
"MHIOCREREGISTERDEVID failed %d\n",
errno);
}
}
return (0);
}
void
{
}
/*
* Input: File descriptor
* Output: 1 if disk is >1TB OR has an EFI label, 0 otherwise.
*/
int
{
return (1);
}
}
return (0);
}
void
{
int resv_part = -1;
int i, j;
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 */
"partition %d is \"unassigned\" but has a size "
}
continue;
}
if (resv_part != -1) {
"found duplicate reserved partition at "
"%d\n", i);
}
resv_part = i;
"Warning: reserved partition size must "
"be %d sectors\n", EFI_MIN_RESV_SIZE);
}
"Partition %d starts at %llu\n",
i,
"It must be between %llu and %llu.\n",
}
vtoc->efi_first_u_lba) ||
"Partition %d ends at %llu\n",
i,
"It must be between %llu and %llu.\n",
}
for (j = 0; j < vtoc->efi_nparts; j++) {
if (!overlap) {
"label error: EFI Labels do not "
"support overlapping partitions\n");
}
"Partition %d overlaps partition "
"%d.\n", i, j);
overlap = 1;
}
}
}
}
/* make sure there is a reserved partition */
if (resv_part == -1) {
"no reserved partition found\n");
}
}
/*
* We need to get information necessary to construct a *new* efi
* label type
*/
int
{
int i;
/*
* Now build the default partition table
*/
if (efi_debug) {
}
return (-1);
}
}
/*
* Make constants first
* and variable partitions later
*/
/* root partition - s0 128 MB */
/* partition - s1 128 MB */
/* partition -s2 is NOT the Backup disk */
/* partition -s6 /usr partition - HOG */
- (1024 * 16);
/* efi reserved partition - s9 16K */
return (0);
}