xref: /illumos-gate/usr/src/uts/sun4v/io/vdsk_common.c (revision edcc07547a39d6570197493a9836083bd6b2a197)
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