xref: /titanic_44/usr/src/uts/sun4v/io/vdsk_common.c (revision d84f0041660230c140a3895b8bfc7e428c7ccb1d)
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*d84f0041SAlexandre Chartre  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
244bac2208Snarayan  * Use is subject to license terms.
254bac2208Snarayan  */
264bac2208Snarayan 
274bac2208Snarayan #include <sys/crc32.h>
284bac2208Snarayan #include <sys/cred.h>
294bac2208Snarayan #include <sys/ddi.h>
304bac2208Snarayan #include <sys/dkio.h>
314bac2208Snarayan #include <sys/file.h>
324bac2208Snarayan #include <sys/kmem.h>
334bac2208Snarayan #include <sys/sunddi.h>
344bac2208Snarayan #include <sys/sunldi.h>
354bac2208Snarayan #include <sys/types.h>
364bac2208Snarayan #include <sys/varargs.h>
374bac2208Snarayan #include <sys/vtoc.h>
384bac2208Snarayan 
394bac2208Snarayan #include <sys/vdsk_common.h>
404bac2208Snarayan 
414bac2208Snarayan /*
424bac2208Snarayan  * Hooks for EFI support
434bac2208Snarayan  */
444bac2208Snarayan 
454bac2208Snarayan /*
46edcc0754Sachartre  * This code provides generic functions to the vds and vdc drivers to read
47edcc0754Sachartre  * EFI labels from the disk backend and to get the EFI GPT and GPE. This is
48edcc0754Sachartre  * inspired from the libefi userland library and the cmlb driver. We will
49edcc0754Sachartre  * certainly be able to remove that code if RFE 6213117 is ever implemented.
504bac2208Snarayan  */
514bac2208Snarayan 
5211f54b6eSAlexandre Chartre #ifdef DEBUG
5311f54b6eSAlexandre Chartre 
544bac2208Snarayan #define	VD_EFI_DEBUG	if (vd_efi_debug) vd_efi_print
554bac2208Snarayan 
56edcc0754Sachartre static int vd_efi_debug = 0;
5711f54b6eSAlexandre Chartre 
5811f54b6eSAlexandre Chartre #else
5911f54b6eSAlexandre Chartre 
6011f54b6eSAlexandre Chartre #define	VD_EFI_DEBUG(...)
6111f54b6eSAlexandre Chartre 
62edcc0754Sachartre #endif
634bac2208Snarayan 
64edcc0754Sachartre #define	VD_EFI_GPE_LEN(vdisk, nparts) \
65edcc0754Sachartre 	((((sizeof (efi_gpe_t) * (nparts) - 1) / (vdisk)->block_size) + 1) * \
66edcc0754Sachartre 	(vdisk)->block_size)
674bac2208Snarayan 
684bac2208Snarayan static void
vd_efi_print(const char * format,...)694bac2208Snarayan vd_efi_print(const char *format, ...)
704bac2208Snarayan {
714bac2208Snarayan 	va_list args;
724bac2208Snarayan 
734bac2208Snarayan 	va_start(args, format);
744bac2208Snarayan 	vcmn_err(CE_CONT, format, args);
754bac2208Snarayan 	va_end(args);
764bac2208Snarayan }
774bac2208Snarayan 
784bac2208Snarayan /*
794bac2208Snarayan  * Return a 32-bit CRC of the contents of the buffer.
804bac2208Snarayan  *
814bac2208Snarayan  * The seed is 0xffffffff and the result is XORed with 0xffffffff
824bac2208Snarayan  * because this is what the Itanium firmware expects.
834bac2208Snarayan  */
844bac2208Snarayan unsigned int
vd_efi_crc32(const unsigned char * s,unsigned int len)854bac2208Snarayan vd_efi_crc32(const unsigned char *s, unsigned int len)
864bac2208Snarayan {
874bac2208Snarayan 	unsigned int crc32val;
884bac2208Snarayan 
894bac2208Snarayan 	CRC32(crc32val, s, len, -1U, crc32_table);
904bac2208Snarayan 
914bac2208Snarayan 	return (crc32val ^ -1U);
924bac2208Snarayan }
934bac2208Snarayan 
944bac2208Snarayan static int
vd_efi_ioctl(vd_efi_dev_t * dev,int cmd,void * arg)95edcc0754Sachartre vd_efi_ioctl(vd_efi_dev_t *dev, int cmd, void *arg)
964bac2208Snarayan {
974bac2208Snarayan 	int status;
984bac2208Snarayan 
99edcc0754Sachartre 	ASSERT(dev->vdisk_ioctl != NULL);
100edcc0754Sachartre 	ASSERT(dev->vdisk != NULL);
101edcc0754Sachartre 	status = (*dev->vdisk_ioctl)(dev->vdisk, cmd, (uintptr_t)arg);
1024bac2208Snarayan 
103edcc0754Sachartre 	return (status);
104edcc0754Sachartre }
105edcc0754Sachartre 
106edcc0754Sachartre /*
107edcc0754Sachartre  * Swap GPT data to match with the system endianness.
108edcc0754Sachartre  */
109edcc0754Sachartre static void
vd_efi_swap_gpt(efi_gpt_t * gpt)110edcc0754Sachartre vd_efi_swap_gpt(efi_gpt_t *gpt)
111edcc0754Sachartre {
112edcc0754Sachartre 	gpt->efi_gpt_Signature = LE_64(gpt->efi_gpt_Signature);
113edcc0754Sachartre 	gpt->efi_gpt_Revision = LE_32(gpt->efi_gpt_Revision);
114edcc0754Sachartre 	gpt->efi_gpt_HeaderSize = LE_32(gpt->efi_gpt_HeaderSize);
115edcc0754Sachartre 	gpt->efi_gpt_HeaderCRC32 = LE_32(gpt->efi_gpt_HeaderCRC32);
116edcc0754Sachartre 	gpt->efi_gpt_MyLBA = LE_64(gpt->efi_gpt_MyLBA);
117edcc0754Sachartre 	gpt->efi_gpt_AlternateLBA = LE_64(gpt->efi_gpt_AlternateLBA);
118edcc0754Sachartre 	gpt->efi_gpt_FirstUsableLBA = LE_64(gpt->efi_gpt_FirstUsableLBA);
119edcc0754Sachartre 	gpt->efi_gpt_LastUsableLBA = LE_64(gpt->efi_gpt_LastUsableLBA);
120edcc0754Sachartre 	UUID_LE_CONVERT(gpt->efi_gpt_DiskGUID, gpt->efi_gpt_DiskGUID);
121edcc0754Sachartre 	gpt->efi_gpt_PartitionEntryLBA = LE_64(gpt->efi_gpt_PartitionEntryLBA);
122edcc0754Sachartre 	gpt->efi_gpt_NumberOfPartitionEntries =
123edcc0754Sachartre 	    LE_32(gpt->efi_gpt_NumberOfPartitionEntries);
124edcc0754Sachartre 	gpt->efi_gpt_SizeOfPartitionEntry =
125edcc0754Sachartre 	    LE_32(gpt->efi_gpt_SizeOfPartitionEntry);
126edcc0754Sachartre 	gpt->efi_gpt_PartitionEntryArrayCRC32 =
127edcc0754Sachartre 	    LE_32(gpt->efi_gpt_PartitionEntryArrayCRC32);
128edcc0754Sachartre }
129edcc0754Sachartre 
130edcc0754Sachartre /*
131edcc0754Sachartre  * Swap GPE data to match with the system endianness.
132edcc0754Sachartre  */
133edcc0754Sachartre static void
vd_efi_swap_gpe(efi_gpe_t * gpe,int nparts)134edcc0754Sachartre vd_efi_swap_gpe(efi_gpe_t *gpe, int nparts)
135edcc0754Sachartre {
136edcc0754Sachartre 	int i, j;
137edcc0754Sachartre 
138edcc0754Sachartre 	for (i = 0; i < nparts; i++) {
139edcc0754Sachartre 		UUID_LE_CONVERT(gpe[i].efi_gpe_PartitionTypeGUID,
140edcc0754Sachartre 		    gpe[i].efi_gpe_PartitionTypeGUID);
141edcc0754Sachartre 		UUID_LE_CONVERT(gpe[i].efi_gpe_UniquePartitionGUID,
142edcc0754Sachartre 		    gpe[i].efi_gpe_UniquePartitionGUID);
143edcc0754Sachartre 		gpe[i].efi_gpe_StartingLBA = LE_64(gpe[i].efi_gpe_StartingLBA);
144edcc0754Sachartre 		gpe[i].efi_gpe_EndingLBA = LE_64(gpe[i].efi_gpe_EndingLBA);
145edcc0754Sachartre 		gpe[i].efi_gpe_Attributes.PartitionAttrs =
146edcc0754Sachartre 		    LE_16(gpe[i].efi_gpe_Attributes.PartitionAttrs);
147edcc0754Sachartre 		for (j = 0; j < EFI_PART_NAME_LEN; j++) {
148edcc0754Sachartre 			gpe[i].efi_gpe_PartitionName[j] =
149edcc0754Sachartre 			    LE_16(gpe[i].efi_gpe_PartitionName[j]);
150edcc0754Sachartre 		}
151edcc0754Sachartre 	}
152edcc0754Sachartre }
153edcc0754Sachartre 
154edcc0754Sachartre /*
155edcc0754Sachartre  * Check that an EFI GPT is valid. This function should be called with a raw
156edcc0754Sachartre  * EFI GPT i.e. GPT data should be in little endian format as indicated in the
157edcc0754Sachartre  * EFI specification and they should not have been swapped to match with the
158edcc0754Sachartre  * system endianness.
159edcc0754Sachartre  */
160edcc0754Sachartre static int
vd_efi_check_gpt(vd_efi_dev_t * dev,efi_gpt_t * gpt)161edcc0754Sachartre vd_efi_check_gpt(vd_efi_dev_t *dev, efi_gpt_t *gpt)
162edcc0754Sachartre {
163edcc0754Sachartre 	uint_t crc_stored, crc_computed;
164edcc0754Sachartre 
165edcc0754Sachartre 	if (gpt->efi_gpt_Signature != LE_64(EFI_SIGNATURE)) {
1664bac2208Snarayan 		VD_EFI_DEBUG("Bad EFI signature: 0x%llx != 0x%llx\n",
167edcc0754Sachartre 		    (long long)gpt->efi_gpt_Signature,
1684bac2208Snarayan 		    (long long)LE_64(EFI_SIGNATURE));
1694bac2208Snarayan 		return (EINVAL);
1704bac2208Snarayan 	}
1714bac2208Snarayan 
1724bac2208Snarayan 	/*
1734bac2208Snarayan 	 * check CRC of the header; the size of the header should
1744bac2208Snarayan 	 * never be larger than one block
1754bac2208Snarayan 	 */
176edcc0754Sachartre 	if (LE_32(gpt->efi_gpt_HeaderSize) > dev->block_size) {
177edcc0754Sachartre 		VD_EFI_DEBUG("Header size (%u bytes) larger than one block"
178edcc0754Sachartre 		    "(%u bytes)\n", LE_32(gpt->efi_gpt_HeaderSize),
179edcc0754Sachartre 		    dev->block_size);
180edcc0754Sachartre 		return (EINVAL);
181edcc0754Sachartre 	}
1824bac2208Snarayan 
183edcc0754Sachartre 	crc_stored = LE_32(gpt->efi_gpt_HeaderCRC32);
184edcc0754Sachartre 	gpt->efi_gpt_HeaderCRC32 = LE_32(0);
185edcc0754Sachartre 	crc_computed = vd_efi_crc32((unsigned char *)gpt,
186edcc0754Sachartre 	    LE_32(gpt->efi_gpt_HeaderSize));
187edcc0754Sachartre 	gpt->efi_gpt_HeaderCRC32 = LE_32(crc_stored);
188edcc0754Sachartre 
189edcc0754Sachartre 	if (crc_stored != crc_computed) {
1904bac2208Snarayan 		VD_EFI_DEBUG("Bad EFI CRC: 0x%x != 0x%x\n",
191edcc0754Sachartre 		    crc_stored, crc_computed);
1924bac2208Snarayan 			return (EINVAL);
1934bac2208Snarayan 	}
1944bac2208Snarayan 
1954bac2208Snarayan 	return (0);
1964bac2208Snarayan }
1974bac2208Snarayan 
198edcc0754Sachartre /*
199edcc0754Sachartre  * Allocate and read the EFI GPT and GPE from the disk backend. Note that the
200edcc0754Sachartre  * on-disk GPT and GPE are stored in little endian format but this function
201edcc0754Sachartre  * returns them using the endianness of the system so that any field in the
202edcc0754Sachartre  * GPT/GPE structures can be directly accessible without any further conversion.
203edcc0754Sachartre  * The caller is responsible for freeing the allocated structures by calling
204edcc0754Sachartre  * vd_efi_free().
205edcc0754Sachartre  */
206edcc0754Sachartre int
vd_efi_alloc_and_read(vd_efi_dev_t * dev,efi_gpt_t ** efi_gpt,efi_gpe_t ** efi_gpe)207edcc0754Sachartre vd_efi_alloc_and_read(vd_efi_dev_t *dev, efi_gpt_t **efi_gpt,
208edcc0754Sachartre     efi_gpe_t **efi_gpe)
2094bac2208Snarayan {
210edcc0754Sachartre 	dk_efi_t		dk_efi;
211edcc0754Sachartre 	efi_gpt_t		*gpt = NULL;
212edcc0754Sachartre 	efi_gpe_t		*gpe = NULL;
213*d84f0041SAlexandre Chartre 	efi_gpt_t		*data = NULL;
214*d84f0041SAlexandre Chartre 	size_t			gpt_len, gpe_len, data_len;
215edcc0754Sachartre 	int 			nparts, status;
216edcc0754Sachartre 
217edcc0754Sachartre 	ASSERT(dev->block_size >= sizeof (efi_gpt_t));
218edcc0754Sachartre 	gpt_len = dev->block_size;
219edcc0754Sachartre 	gpt = kmem_zalloc(gpt_len, KM_SLEEP);
2204bac2208Snarayan 
2214bac2208Snarayan 	/*
222edcc0754Sachartre 	 * Read the EFI GPT.
2234bac2208Snarayan 	 */
224edcc0754Sachartre 	dk_efi.dki_lba = 1;
225edcc0754Sachartre 	dk_efi.dki_data = gpt;
226edcc0754Sachartre 	dk_efi.dki_length = gpt_len;
227edcc0754Sachartre 
228*d84f0041SAlexandre Chartre 	status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi);
229*d84f0041SAlexandre Chartre 
230*d84f0041SAlexandre Chartre 	if (status == EINVAL) {
231*d84f0041SAlexandre Chartre 		/*
232*d84f0041SAlexandre Chartre 		 * Because the DKIOCGETEFI ioctl was initially incorrectly
233*d84f0041SAlexandre Chartre 		 * implemented for a ZFS volume, the ioctl can fail with
234*d84f0041SAlexandre Chartre 		 * EINVAL if it is done on a ZFS volume managed by an old
235*d84f0041SAlexandre Chartre 		 * version of Solaris. This can happen if a ZFS volume is
236*d84f0041SAlexandre Chartre 		 * exported as a single-slice disk by a service domain
237*d84f0041SAlexandre Chartre 		 * running Solaris older than Solaris 10 Update 6.
238*d84f0041SAlexandre Chartre 		 *
239*d84f0041SAlexandre Chartre 		 * So we retry the ioctl to read both the GPT and the GPE at
240*d84f0041SAlexandre Chartre 		 * the same time accordingly to the old implementation.
241*d84f0041SAlexandre Chartre 		 */
242*d84f0041SAlexandre Chartre 		data_len = sizeof (efi_gpt_t) + sizeof (efi_gpe_t);
243*d84f0041SAlexandre Chartre 		data = kmem_zalloc(data_len, KM_SLEEP);
244*d84f0041SAlexandre Chartre 
245*d84f0041SAlexandre Chartre 		dk_efi.dki_lba = 1;
246*d84f0041SAlexandre Chartre 		dk_efi.dki_data = data;
247*d84f0041SAlexandre Chartre 		dk_efi.dki_length = data_len;
248*d84f0041SAlexandre Chartre 		status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi);
249*d84f0041SAlexandre Chartre 
250*d84f0041SAlexandre Chartre 		if (status == 0)
251*d84f0041SAlexandre Chartre 			bcopy(data, gpt, sizeof (efi_gpt_t));
252*d84f0041SAlexandre Chartre 	}
253*d84f0041SAlexandre Chartre 
254*d84f0041SAlexandre Chartre 	if (status != 0) {
255edcc0754Sachartre 		VD_EFI_DEBUG("DKIOCGETEFI (GPT, LBA=1) error %d\n", status);
256edcc0754Sachartre 		goto errdone;
2574bac2208Snarayan 	}
2584bac2208Snarayan 
259edcc0754Sachartre 	if ((status = vd_efi_check_gpt(dev, gpt)) != 0) {
2604bac2208Snarayan 		/*
261edcc0754Sachartre 		 * No valid label here; try the alternate. The alternate GPT is
262edcc0754Sachartre 		 * located in the last block of the disk.
2634bac2208Snarayan 		 */
264edcc0754Sachartre 		dk_efi.dki_lba = dev->disk_size - 1;
265edcc0754Sachartre 		dk_efi.dki_data = gpt;
266edcc0754Sachartre 		dk_efi.dki_length = gpt_len;
267edcc0754Sachartre 
268edcc0754Sachartre 		if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
269edcc0754Sachartre 			VD_EFI_DEBUG("DKIOCGETEFI (LBA=%lu) error %d\n",
270edcc0754Sachartre 			    dev->disk_size - 1, status);
271edcc0754Sachartre 			goto errdone;
272edcc0754Sachartre 		}
273edcc0754Sachartre 
274edcc0754Sachartre 		if ((status = vd_efi_check_gpt(dev, gpt)) != 0)
275edcc0754Sachartre 			goto errdone;
276edcc0754Sachartre 
277edcc0754Sachartre 		VD_EFI_DEBUG("efi_read: primary label corrupt; using backup\n");
278edcc0754Sachartre 	}
279edcc0754Sachartre 
280edcc0754Sachartre 	/* swap GPT data after checking the GPT is valid */
281edcc0754Sachartre 	vd_efi_swap_gpt(gpt);
282edcc0754Sachartre 
283edcc0754Sachartre 	/*
284edcc0754Sachartre 	 * Read the EFI GPE.
285edcc0754Sachartre 	 */
286edcc0754Sachartre 	nparts = gpt->efi_gpt_NumberOfPartitionEntries;
287edcc0754Sachartre 
288edcc0754Sachartre 	if (nparts > NDKMAP + 1) {
289edcc0754Sachartre 		VD_EFI_DEBUG("Too many EFI partitions (%u)", nparts);
2904bac2208Snarayan 		status = EINVAL;
291edcc0754Sachartre 		goto errdone;
2924bac2208Snarayan 	}
293edcc0754Sachartre 
294edcc0754Sachartre 	if (nparts == 0) {
295edcc0754Sachartre 		VD_EFI_DEBUG("No partition defined");
296edcc0754Sachartre 		status = EINVAL;
297edcc0754Sachartre 		goto errdone;
2984bac2208Snarayan 	}
299edcc0754Sachartre 
300edcc0754Sachartre 	gpe_len = VD_EFI_GPE_LEN(dev, nparts);
301edcc0754Sachartre 	gpe = kmem_zalloc(gpe_len, KM_SLEEP);
302edcc0754Sachartre 
303*d84f0041SAlexandre Chartre 	if (data != NULL) {
304*d84f0041SAlexandre Chartre 		/*
305*d84f0041SAlexandre Chartre 		 * The data variable is not NULL if we have used the old ioctl
306*d84f0041SAlexandre Chartre 		 * implementation for a ZFS volume. In that case, we only expect
307*d84f0041SAlexandre Chartre 		 * one partition and GPE data are already available in the data
308*d84f0041SAlexandre Chartre 		 * buffer, right after GPT data.
309*d84f0041SAlexandre Chartre 		 */
310*d84f0041SAlexandre Chartre 		if (nparts != 1) {
311*d84f0041SAlexandre Chartre 			VD_EFI_DEBUG("Unexpected number of partitions (%u)",
312*d84f0041SAlexandre Chartre 			    nparts);
313*d84f0041SAlexandre Chartre 			status = EINVAL;
314*d84f0041SAlexandre Chartre 			goto errdone;
315*d84f0041SAlexandre Chartre 		}
316*d84f0041SAlexandre Chartre 
317*d84f0041SAlexandre Chartre 		bcopy(data + 1, gpe, sizeof (efi_gpe_t));
318*d84f0041SAlexandre Chartre 
319*d84f0041SAlexandre Chartre 	} else {
320edcc0754Sachartre 		dk_efi.dki_lba = gpt->efi_gpt_PartitionEntryLBA;
321edcc0754Sachartre 		dk_efi.dki_data = (efi_gpt_t *)gpe;
322edcc0754Sachartre 		dk_efi.dki_length = gpe_len;
323edcc0754Sachartre 
324edcc0754Sachartre 		if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
325edcc0754Sachartre 			VD_EFI_DEBUG("DKIOCGETEFI (GPE, LBA=%lu) error %d\n",
326edcc0754Sachartre 			    gpt->efi_gpt_PartitionEntryLBA, status);
327edcc0754Sachartre 			goto errdone;
3284bac2208Snarayan 		}
329*d84f0041SAlexandre Chartre 	}
330edcc0754Sachartre 
331edcc0754Sachartre 	vd_efi_swap_gpe(gpe, nparts);
332edcc0754Sachartre 
333edcc0754Sachartre 	*efi_gpt = gpt;
334edcc0754Sachartre 	*efi_gpe = gpe;
335edcc0754Sachartre 
336edcc0754Sachartre errdone:
337edcc0754Sachartre 
338*d84f0041SAlexandre Chartre 	if (data != NULL)
339*d84f0041SAlexandre Chartre 		kmem_free(data, data_len);
340*d84f0041SAlexandre Chartre 
341*d84f0041SAlexandre Chartre 	if (status != 0) {
342edcc0754Sachartre 		if (gpe != NULL)
343edcc0754Sachartre 			kmem_free(gpe, gpe_len);
344edcc0754Sachartre 		if (gpt != NULL)
345edcc0754Sachartre 			kmem_free(gpt, gpt_len);
346*d84f0041SAlexandre Chartre 	}
347edcc0754Sachartre 
3484bac2208Snarayan 	return (status);
3494bac2208Snarayan }
3504bac2208Snarayan 
3514bac2208Snarayan /*
352edcc0754Sachartre  * Free the EFI GPE and GPT structures returned by vd_efi_alloc_and_read().
3534bac2208Snarayan  */
3544bac2208Snarayan void
vd_efi_free(vd_efi_dev_t * dev,efi_gpt_t * gpt,efi_gpe_t * gpe)355edcc0754Sachartre vd_efi_free(vd_efi_dev_t *dev, efi_gpt_t *gpt, efi_gpe_t *gpe)
3564bac2208Snarayan {
357edcc0754Sachartre 	kmem_free(gpe, VD_EFI_GPE_LEN(dev,
358edcc0754Sachartre 	    gpt->efi_gpt_NumberOfPartitionEntries));
359edcc0754Sachartre 	kmem_free(gpt, dev->block_size);
3604bac2208Snarayan }
361