14bac2208Snarayan /* 24bac2208Snarayan * CDDL HEADER START 34bac2208Snarayan * 44bac2208Snarayan * The contents of this file are subject to the terms of the 54bac2208Snarayan * Common Development and Distribution License (the "License"). 64bac2208Snarayan * You may not use this file except in compliance with the License. 74bac2208Snarayan * 84bac2208Snarayan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94bac2208Snarayan * or http://www.opensolaris.org/os/licensing. 104bac2208Snarayan * See the License for the specific language governing permissions 114bac2208Snarayan * and limitations under the License. 124bac2208Snarayan * 134bac2208Snarayan * When distributing Covered Code, include this CDDL HEADER in each 144bac2208Snarayan * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154bac2208Snarayan * If applicable, add the following below this CDDL HEADER, with the 164bac2208Snarayan * fields enclosed by brackets "[]" replaced with your own identifying 174bac2208Snarayan * information: Portions Copyright [yyyy] [name of copyright owner] 184bac2208Snarayan * 194bac2208Snarayan * CDDL HEADER END 204bac2208Snarayan */ 214bac2208Snarayan 224bac2208Snarayan /* 23*edcc0754Sachartre * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 244bac2208Snarayan * Use is subject to license terms. 254bac2208Snarayan */ 264bac2208Snarayan 274bac2208Snarayan #pragma ident "%Z%%M% %I% %E% SMI" 284bac2208Snarayan 294bac2208Snarayan #include <sys/crc32.h> 304bac2208Snarayan #include <sys/cred.h> 314bac2208Snarayan #include <sys/ddi.h> 324bac2208Snarayan #include <sys/dkio.h> 334bac2208Snarayan #include <sys/file.h> 344bac2208Snarayan #include <sys/kmem.h> 354bac2208Snarayan #include <sys/sunddi.h> 364bac2208Snarayan #include <sys/sunldi.h> 374bac2208Snarayan #include <sys/types.h> 384bac2208Snarayan #include <sys/varargs.h> 394bac2208Snarayan #include <sys/vtoc.h> 404bac2208Snarayan 414bac2208Snarayan #include <sys/vdsk_common.h> 424bac2208Snarayan 434bac2208Snarayan /* 444bac2208Snarayan * Hooks for EFI support 454bac2208Snarayan */ 464bac2208Snarayan 474bac2208Snarayan /* 48*edcc0754Sachartre * This code provides generic functions to the vds and vdc drivers to read 49*edcc0754Sachartre * EFI labels from the disk backend and to get the EFI GPT and GPE. This is 50*edcc0754Sachartre * inspired from the libefi userland library and the cmlb driver. We will 51*edcc0754Sachartre * certainly be able to remove that code if RFE 6213117 is ever implemented. 524bac2208Snarayan */ 534bac2208Snarayan 544bac2208Snarayan #define VD_EFI_DEBUG if (vd_efi_debug) vd_efi_print 554bac2208Snarayan 56*edcc0754Sachartre #ifdef DEBUG 574bac2208Snarayan static int vd_efi_debug = 1; 58*edcc0754Sachartre #else 59*edcc0754Sachartre static int vd_efi_debug = 0; 60*edcc0754Sachartre #endif 614bac2208Snarayan 62*edcc0754Sachartre #define VD_EFI_GPE_LEN(vdisk, nparts) \ 63*edcc0754Sachartre ((((sizeof (efi_gpe_t) * (nparts) - 1) / (vdisk)->block_size) + 1) * \ 64*edcc0754Sachartre (vdisk)->block_size) 654bac2208Snarayan 664bac2208Snarayan static void 674bac2208Snarayan vd_efi_print(const char *format, ...) 684bac2208Snarayan { 694bac2208Snarayan va_list args; 704bac2208Snarayan 714bac2208Snarayan va_start(args, format); 724bac2208Snarayan vcmn_err(CE_CONT, format, args); 734bac2208Snarayan va_end(args); 744bac2208Snarayan } 754bac2208Snarayan 764bac2208Snarayan /* 774bac2208Snarayan * Return a 32-bit CRC of the contents of the buffer. 784bac2208Snarayan * 794bac2208Snarayan * The seed is 0xffffffff and the result is XORed with 0xffffffff 804bac2208Snarayan * because this is what the Itanium firmware expects. 814bac2208Snarayan */ 824bac2208Snarayan unsigned int 834bac2208Snarayan vd_efi_crc32(const unsigned char *s, unsigned int len) 844bac2208Snarayan { 854bac2208Snarayan unsigned int crc32val; 864bac2208Snarayan 874bac2208Snarayan CRC32(crc32val, s, len, -1U, crc32_table); 884bac2208Snarayan 894bac2208Snarayan return (crc32val ^ -1U); 904bac2208Snarayan } 914bac2208Snarayan 924bac2208Snarayan static int 93*edcc0754Sachartre vd_efi_ioctl(vd_efi_dev_t *dev, int cmd, void *arg) 944bac2208Snarayan { 954bac2208Snarayan int status; 964bac2208Snarayan 97*edcc0754Sachartre ASSERT(dev->vdisk_ioctl != NULL); 98*edcc0754Sachartre ASSERT(dev->vdisk != NULL); 99*edcc0754Sachartre status = (*dev->vdisk_ioctl)(dev->vdisk, cmd, (uintptr_t)arg); 1004bac2208Snarayan 101*edcc0754Sachartre return (status); 102*edcc0754Sachartre } 103*edcc0754Sachartre 104*edcc0754Sachartre /* 105*edcc0754Sachartre * Swap GPT data to match with the system endianness. 106*edcc0754Sachartre */ 107*edcc0754Sachartre static void 108*edcc0754Sachartre vd_efi_swap_gpt(efi_gpt_t *gpt) 109*edcc0754Sachartre { 110*edcc0754Sachartre gpt->efi_gpt_Signature = LE_64(gpt->efi_gpt_Signature); 111*edcc0754Sachartre gpt->efi_gpt_Revision = LE_32(gpt->efi_gpt_Revision); 112*edcc0754Sachartre gpt->efi_gpt_HeaderSize = LE_32(gpt->efi_gpt_HeaderSize); 113*edcc0754Sachartre gpt->efi_gpt_HeaderCRC32 = LE_32(gpt->efi_gpt_HeaderCRC32); 114*edcc0754Sachartre gpt->efi_gpt_MyLBA = LE_64(gpt->efi_gpt_MyLBA); 115*edcc0754Sachartre gpt->efi_gpt_AlternateLBA = LE_64(gpt->efi_gpt_AlternateLBA); 116*edcc0754Sachartre gpt->efi_gpt_FirstUsableLBA = LE_64(gpt->efi_gpt_FirstUsableLBA); 117*edcc0754Sachartre gpt->efi_gpt_LastUsableLBA = LE_64(gpt->efi_gpt_LastUsableLBA); 118*edcc0754Sachartre UUID_LE_CONVERT(gpt->efi_gpt_DiskGUID, gpt->efi_gpt_DiskGUID); 119*edcc0754Sachartre gpt->efi_gpt_PartitionEntryLBA = LE_64(gpt->efi_gpt_PartitionEntryLBA); 120*edcc0754Sachartre gpt->efi_gpt_NumberOfPartitionEntries = 121*edcc0754Sachartre LE_32(gpt->efi_gpt_NumberOfPartitionEntries); 122*edcc0754Sachartre gpt->efi_gpt_SizeOfPartitionEntry = 123*edcc0754Sachartre LE_32(gpt->efi_gpt_SizeOfPartitionEntry); 124*edcc0754Sachartre gpt->efi_gpt_PartitionEntryArrayCRC32 = 125*edcc0754Sachartre LE_32(gpt->efi_gpt_PartitionEntryArrayCRC32); 126*edcc0754Sachartre } 127*edcc0754Sachartre 128*edcc0754Sachartre /* 129*edcc0754Sachartre * Swap GPE data to match with the system endianness. 130*edcc0754Sachartre */ 131*edcc0754Sachartre static void 132*edcc0754Sachartre vd_efi_swap_gpe(efi_gpe_t *gpe, int nparts) 133*edcc0754Sachartre { 134*edcc0754Sachartre int i, j; 135*edcc0754Sachartre 136*edcc0754Sachartre for (i = 0; i < nparts; i++) { 137*edcc0754Sachartre UUID_LE_CONVERT(gpe[i].efi_gpe_PartitionTypeGUID, 138*edcc0754Sachartre gpe[i].efi_gpe_PartitionTypeGUID); 139*edcc0754Sachartre UUID_LE_CONVERT(gpe[i].efi_gpe_UniquePartitionGUID, 140*edcc0754Sachartre gpe[i].efi_gpe_UniquePartitionGUID); 141*edcc0754Sachartre gpe[i].efi_gpe_StartingLBA = LE_64(gpe[i].efi_gpe_StartingLBA); 142*edcc0754Sachartre gpe[i].efi_gpe_EndingLBA = LE_64(gpe[i].efi_gpe_EndingLBA); 143*edcc0754Sachartre gpe[i].efi_gpe_Attributes.PartitionAttrs = 144*edcc0754Sachartre LE_16(gpe[i].efi_gpe_Attributes.PartitionAttrs); 145*edcc0754Sachartre for (j = 0; j < EFI_PART_NAME_LEN; j++) { 146*edcc0754Sachartre gpe[i].efi_gpe_PartitionName[j] = 147*edcc0754Sachartre LE_16(gpe[i].efi_gpe_PartitionName[j]); 148*edcc0754Sachartre } 149*edcc0754Sachartre } 150*edcc0754Sachartre } 151*edcc0754Sachartre 152*edcc0754Sachartre /* 153*edcc0754Sachartre * Check that an EFI GPT is valid. This function should be called with a raw 154*edcc0754Sachartre * EFI GPT i.e. GPT data should be in little endian format as indicated in the 155*edcc0754Sachartre * EFI specification and they should not have been swapped to match with the 156*edcc0754Sachartre * system endianness. 157*edcc0754Sachartre */ 158*edcc0754Sachartre static int 159*edcc0754Sachartre vd_efi_check_gpt(vd_efi_dev_t *dev, efi_gpt_t *gpt) 160*edcc0754Sachartre { 161*edcc0754Sachartre uint_t crc_stored, crc_computed; 162*edcc0754Sachartre 163*edcc0754Sachartre if (gpt->efi_gpt_Signature != LE_64(EFI_SIGNATURE)) { 1644bac2208Snarayan VD_EFI_DEBUG("Bad EFI signature: 0x%llx != 0x%llx\n", 165*edcc0754Sachartre (long long)gpt->efi_gpt_Signature, 1664bac2208Snarayan (long long)LE_64(EFI_SIGNATURE)); 1674bac2208Snarayan return (EINVAL); 1684bac2208Snarayan } 1694bac2208Snarayan 1704bac2208Snarayan /* 1714bac2208Snarayan * check CRC of the header; the size of the header should 1724bac2208Snarayan * never be larger than one block 1734bac2208Snarayan */ 174*edcc0754Sachartre if (LE_32(gpt->efi_gpt_HeaderSize) > dev->block_size) { 175*edcc0754Sachartre VD_EFI_DEBUG("Header size (%u bytes) larger than one block" 176*edcc0754Sachartre "(%u bytes)\n", LE_32(gpt->efi_gpt_HeaderSize), 177*edcc0754Sachartre dev->block_size); 178*edcc0754Sachartre return (EINVAL); 179*edcc0754Sachartre } 1804bac2208Snarayan 181*edcc0754Sachartre crc_stored = LE_32(gpt->efi_gpt_HeaderCRC32); 182*edcc0754Sachartre gpt->efi_gpt_HeaderCRC32 = LE_32(0); 183*edcc0754Sachartre crc_computed = vd_efi_crc32((unsigned char *)gpt, 184*edcc0754Sachartre LE_32(gpt->efi_gpt_HeaderSize)); 185*edcc0754Sachartre gpt->efi_gpt_HeaderCRC32 = LE_32(crc_stored); 186*edcc0754Sachartre 187*edcc0754Sachartre if (crc_stored != crc_computed) { 1884bac2208Snarayan VD_EFI_DEBUG("Bad EFI CRC: 0x%x != 0x%x\n", 189*edcc0754Sachartre crc_stored, crc_computed); 1904bac2208Snarayan return (EINVAL); 1914bac2208Snarayan } 1924bac2208Snarayan 1934bac2208Snarayan return (0); 1944bac2208Snarayan } 1954bac2208Snarayan 196*edcc0754Sachartre /* 197*edcc0754Sachartre * Allocate and read the EFI GPT and GPE from the disk backend. Note that the 198*edcc0754Sachartre * on-disk GPT and GPE are stored in little endian format but this function 199*edcc0754Sachartre * returns them using the endianness of the system so that any field in the 200*edcc0754Sachartre * GPT/GPE structures can be directly accessible without any further conversion. 201*edcc0754Sachartre * The caller is responsible for freeing the allocated structures by calling 202*edcc0754Sachartre * vd_efi_free(). 203*edcc0754Sachartre */ 204*edcc0754Sachartre int 205*edcc0754Sachartre vd_efi_alloc_and_read(vd_efi_dev_t *dev, efi_gpt_t **efi_gpt, 206*edcc0754Sachartre efi_gpe_t **efi_gpe) 2074bac2208Snarayan { 208*edcc0754Sachartre dk_efi_t dk_efi; 209*edcc0754Sachartre efi_gpt_t *gpt = NULL; 210*edcc0754Sachartre efi_gpe_t *gpe = NULL; 211*edcc0754Sachartre size_t gpt_len, gpe_len; 212*edcc0754Sachartre int nparts, status; 213*edcc0754Sachartre 214*edcc0754Sachartre ASSERT(dev->block_size >= sizeof (efi_gpt_t)); 215*edcc0754Sachartre gpt_len = dev->block_size; 216*edcc0754Sachartre gpt = kmem_zalloc(gpt_len, KM_SLEEP); 2174bac2208Snarayan 2184bac2208Snarayan /* 219*edcc0754Sachartre * Read the EFI GPT. 2204bac2208Snarayan */ 221*edcc0754Sachartre dk_efi.dki_lba = 1; 222*edcc0754Sachartre dk_efi.dki_data = gpt; 223*edcc0754Sachartre dk_efi.dki_length = gpt_len; 224*edcc0754Sachartre 225*edcc0754Sachartre if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) { 226*edcc0754Sachartre VD_EFI_DEBUG("DKIOCGETEFI (GPT, LBA=1) error %d\n", status); 227*edcc0754Sachartre goto errdone; 2284bac2208Snarayan } 2294bac2208Snarayan 230*edcc0754Sachartre if ((status = vd_efi_check_gpt(dev, gpt)) != 0) { 2314bac2208Snarayan /* 232*edcc0754Sachartre * No valid label here; try the alternate. The alternate GPT is 233*edcc0754Sachartre * located in the last block of the disk. 2344bac2208Snarayan */ 235*edcc0754Sachartre dk_efi.dki_lba = dev->disk_size - 1; 236*edcc0754Sachartre dk_efi.dki_data = gpt; 237*edcc0754Sachartre dk_efi.dki_length = gpt_len; 238*edcc0754Sachartre 239*edcc0754Sachartre if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) { 240*edcc0754Sachartre VD_EFI_DEBUG("DKIOCGETEFI (LBA=%lu) error %d\n", 241*edcc0754Sachartre dev->disk_size - 1, status); 242*edcc0754Sachartre goto errdone; 243*edcc0754Sachartre } 244*edcc0754Sachartre 245*edcc0754Sachartre if ((status = vd_efi_check_gpt(dev, gpt)) != 0) 246*edcc0754Sachartre goto errdone; 247*edcc0754Sachartre 248*edcc0754Sachartre VD_EFI_DEBUG("efi_read: primary label corrupt; using backup\n"); 249*edcc0754Sachartre } 250*edcc0754Sachartre 251*edcc0754Sachartre /* swap GPT data after checking the GPT is valid */ 252*edcc0754Sachartre vd_efi_swap_gpt(gpt); 253*edcc0754Sachartre 254*edcc0754Sachartre /* 255*edcc0754Sachartre * Read the EFI GPE. 256*edcc0754Sachartre */ 257*edcc0754Sachartre nparts = gpt->efi_gpt_NumberOfPartitionEntries; 258*edcc0754Sachartre 259*edcc0754Sachartre if (nparts > NDKMAP + 1) { 260*edcc0754Sachartre VD_EFI_DEBUG("Too many EFI partitions (%u)", nparts); 2614bac2208Snarayan status = EINVAL; 262*edcc0754Sachartre goto errdone; 2634bac2208Snarayan } 264*edcc0754Sachartre 265*edcc0754Sachartre if (nparts == 0) { 266*edcc0754Sachartre VD_EFI_DEBUG("No partition defined"); 267*edcc0754Sachartre status = EINVAL; 268*edcc0754Sachartre goto errdone; 2694bac2208Snarayan } 270*edcc0754Sachartre 271*edcc0754Sachartre gpe_len = VD_EFI_GPE_LEN(dev, nparts); 272*edcc0754Sachartre gpe = kmem_zalloc(gpe_len, KM_SLEEP); 273*edcc0754Sachartre 274*edcc0754Sachartre dk_efi.dki_lba = gpt->efi_gpt_PartitionEntryLBA; 275*edcc0754Sachartre dk_efi.dki_data = (efi_gpt_t *)gpe; 276*edcc0754Sachartre dk_efi.dki_length = gpe_len; 277*edcc0754Sachartre 278*edcc0754Sachartre if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) { 279*edcc0754Sachartre VD_EFI_DEBUG("DKIOCGETEFI (GPE, LBA=%lu) error %d\n", 280*edcc0754Sachartre gpt->efi_gpt_PartitionEntryLBA, status); 281*edcc0754Sachartre goto errdone; 2824bac2208Snarayan } 283*edcc0754Sachartre 284*edcc0754Sachartre vd_efi_swap_gpe(gpe, nparts); 285*edcc0754Sachartre 286*edcc0754Sachartre *efi_gpt = gpt; 287*edcc0754Sachartre *efi_gpe = gpe; 288*edcc0754Sachartre 289*edcc0754Sachartre return (0); 290*edcc0754Sachartre 291*edcc0754Sachartre errdone: 292*edcc0754Sachartre 293*edcc0754Sachartre if (gpe != NULL) 294*edcc0754Sachartre kmem_free(gpe, gpe_len); 295*edcc0754Sachartre if (gpt != NULL) 296*edcc0754Sachartre kmem_free(gpt, gpt_len); 297*edcc0754Sachartre 2984bac2208Snarayan return (status); 2994bac2208Snarayan } 3004bac2208Snarayan 3014bac2208Snarayan /* 302*edcc0754Sachartre * Free the EFI GPE and GPT structures returned by vd_efi_alloc_and_read(). 3034bac2208Snarayan */ 3044bac2208Snarayan void 305*edcc0754Sachartre vd_efi_free(vd_efi_dev_t *dev, efi_gpt_t *gpt, efi_gpe_t *gpe) 3064bac2208Snarayan { 307*edcc0754Sachartre kmem_free(gpe, VD_EFI_GPE_LEN(dev, 308*edcc0754Sachartre gpt->efi_gpt_NumberOfPartitionEntries)); 309*edcc0754Sachartre kmem_free(gpt, dev->block_size); 3104bac2208Snarayan } 311