xref: /illumos-gate/usr/src/uts/sun4v/io/vdsk_common.c (revision b6c3f786)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/crc32.h>
30 #include <sys/cred.h>
31 #include <sys/ddi.h>
32 #include <sys/dkio.h>
33 #include <sys/file.h>
34 #include <sys/kmem.h>
35 #include <sys/sunddi.h>
36 #include <sys/sunldi.h>
37 #include <sys/types.h>
38 #include <sys/varargs.h>
39 #include <sys/vtoc.h>
40 
41 #include <sys/vdsk_common.h>
42 
43 /*
44  * Hooks for EFI support
45  */
46 
47 /*
48  * This code provides generic functions to the vds and vdc drivers to read
49  * EFI labels from the disk backend and to get the EFI GPT and GPE. This is
50  * inspired from the libefi userland library and the cmlb driver. We will
51  * certainly be able to remove that code if RFE 6213117 is ever implemented.
52  */
53 
54 #define	VD_EFI_DEBUG	if (vd_efi_debug) vd_efi_print
55 
56 #ifdef DEBUG
57 static int vd_efi_debug = 1;
58 #else
59 static int vd_efi_debug = 0;
60 #endif
61 
62 #define	VD_EFI_GPE_LEN(vdisk, nparts) \
63 	((((sizeof (efi_gpe_t) * (nparts) - 1) / (vdisk)->block_size) + 1) * \
64 	(vdisk)->block_size)
65 
66 static void
67 vd_efi_print(const char *format, ...)
68 {
69 	va_list args;
70 
71 	va_start(args, format);
72 	vcmn_err(CE_CONT, format, args);
73 	va_end(args);
74 }
75 
76 /*
77  * Return a 32-bit CRC of the contents of the buffer.
78  *
79  * The seed is 0xffffffff and the result is XORed with 0xffffffff
80  * because this is what the Itanium firmware expects.
81  */
82 unsigned int
83 vd_efi_crc32(const unsigned char *s, unsigned int len)
84 {
85 	unsigned int crc32val;
86 
87 	CRC32(crc32val, s, len, -1U, crc32_table);
88 
89 	return (crc32val ^ -1U);
90 }
91 
92 static int
93 vd_efi_ioctl(vd_efi_dev_t *dev, int cmd, void *arg)
94 {
95 	int status;
96 
97 	ASSERT(dev->vdisk_ioctl != NULL);
98 	ASSERT(dev->vdisk != NULL);
99 	status = (*dev->vdisk_ioctl)(dev->vdisk, cmd, (uintptr_t)arg);
100 
101 	return (status);
102 }
103 
104 /*
105  * Swap GPT data to match with the system endianness.
106  */
107 static void
108 vd_efi_swap_gpt(efi_gpt_t *gpt)
109 {
110 	gpt->efi_gpt_Signature = LE_64(gpt->efi_gpt_Signature);
111 	gpt->efi_gpt_Revision = LE_32(gpt->efi_gpt_Revision);
112 	gpt->efi_gpt_HeaderSize = LE_32(gpt->efi_gpt_HeaderSize);
113 	gpt->efi_gpt_HeaderCRC32 = LE_32(gpt->efi_gpt_HeaderCRC32);
114 	gpt->efi_gpt_MyLBA = LE_64(gpt->efi_gpt_MyLBA);
115 	gpt->efi_gpt_AlternateLBA = LE_64(gpt->efi_gpt_AlternateLBA);
116 	gpt->efi_gpt_FirstUsableLBA = LE_64(gpt->efi_gpt_FirstUsableLBA);
117 	gpt->efi_gpt_LastUsableLBA = LE_64(gpt->efi_gpt_LastUsableLBA);
118 	UUID_LE_CONVERT(gpt->efi_gpt_DiskGUID, gpt->efi_gpt_DiskGUID);
119 	gpt->efi_gpt_PartitionEntryLBA = LE_64(gpt->efi_gpt_PartitionEntryLBA);
120 	gpt->efi_gpt_NumberOfPartitionEntries =
121 	    LE_32(gpt->efi_gpt_NumberOfPartitionEntries);
122 	gpt->efi_gpt_SizeOfPartitionEntry =
123 	    LE_32(gpt->efi_gpt_SizeOfPartitionEntry);
124 	gpt->efi_gpt_PartitionEntryArrayCRC32 =
125 	    LE_32(gpt->efi_gpt_PartitionEntryArrayCRC32);
126 }
127 
128 /*
129  * Swap GPE data to match with the system endianness.
130  */
131 static void
132 vd_efi_swap_gpe(efi_gpe_t *gpe, int nparts)
133 {
134 	int i, j;
135 
136 	for (i = 0; i < nparts; i++) {
137 		UUID_LE_CONVERT(gpe[i].efi_gpe_PartitionTypeGUID,
138 		    gpe[i].efi_gpe_PartitionTypeGUID);
139 		UUID_LE_CONVERT(gpe[i].efi_gpe_UniquePartitionGUID,
140 		    gpe[i].efi_gpe_UniquePartitionGUID);
141 		gpe[i].efi_gpe_StartingLBA = LE_64(gpe[i].efi_gpe_StartingLBA);
142 		gpe[i].efi_gpe_EndingLBA = LE_64(gpe[i].efi_gpe_EndingLBA);
143 		gpe[i].efi_gpe_Attributes.PartitionAttrs =
144 		    LE_16(gpe[i].efi_gpe_Attributes.PartitionAttrs);
145 		for (j = 0; j < EFI_PART_NAME_LEN; j++) {
146 			gpe[i].efi_gpe_PartitionName[j] =
147 			    LE_16(gpe[i].efi_gpe_PartitionName[j]);
148 		}
149 	}
150 }
151 
152 /*
153  * Check that an EFI GPT is valid. This function should be called with a raw
154  * EFI GPT i.e. GPT data should be in little endian format as indicated in the
155  * EFI specification and they should not have been swapped to match with the
156  * system endianness.
157  */
158 static int
159 vd_efi_check_gpt(vd_efi_dev_t *dev, efi_gpt_t *gpt)
160 {
161 	uint_t crc_stored, crc_computed;
162 
163 	if (gpt->efi_gpt_Signature != LE_64(EFI_SIGNATURE)) {
164 		VD_EFI_DEBUG("Bad EFI signature: 0x%llx != 0x%llx\n",
165 		    (long long)gpt->efi_gpt_Signature,
166 		    (long long)LE_64(EFI_SIGNATURE));
167 		return (EINVAL);
168 	}
169 
170 	/*
171 	 * check CRC of the header; the size of the header should
172 	 * never be larger than one block
173 	 */
174 	if (LE_32(gpt->efi_gpt_HeaderSize) > dev->block_size) {
175 		VD_EFI_DEBUG("Header size (%u bytes) larger than one block"
176 		    "(%u bytes)\n", LE_32(gpt->efi_gpt_HeaderSize),
177 		    dev->block_size);
178 		return (EINVAL);
179 	}
180 
181 	crc_stored = LE_32(gpt->efi_gpt_HeaderCRC32);
182 	gpt->efi_gpt_HeaderCRC32 = LE_32(0);
183 	crc_computed = vd_efi_crc32((unsigned char *)gpt,
184 	    LE_32(gpt->efi_gpt_HeaderSize));
185 	gpt->efi_gpt_HeaderCRC32 = LE_32(crc_stored);
186 
187 	if (crc_stored != crc_computed) {
188 		VD_EFI_DEBUG("Bad EFI CRC: 0x%x != 0x%x\n",
189 		    crc_stored, crc_computed);
190 		return (EINVAL);
191 	}
192 
193 	return (0);
194 }
195 
196 /*
197  * Allocate and read the EFI GPT and GPE from the disk backend. Note that the
198  * on-disk GPT and GPE are stored in little endian format but this function
199  * returns them using the endianness of the system so that any field in the
200  * GPT/GPE structures can be directly accessible without any further conversion.
201  * The caller is responsible for freeing the allocated structures by calling
202  * vd_efi_free().
203  */
204 int
205 vd_efi_alloc_and_read(vd_efi_dev_t *dev, efi_gpt_t **efi_gpt,
206     efi_gpe_t **efi_gpe)
207 {
208 	dk_efi_t		dk_efi;
209 	efi_gpt_t		*gpt = NULL;
210 	efi_gpe_t		*gpe = NULL;
211 	size_t			gpt_len, gpe_len;
212 	int 			nparts, status;
213 
214 	ASSERT(dev->block_size >= sizeof (efi_gpt_t));
215 	gpt_len = dev->block_size;
216 	gpt = kmem_zalloc(gpt_len, KM_SLEEP);
217 
218 	/*
219 	 * Read the EFI GPT.
220 	 */
221 	dk_efi.dki_lba = 1;
222 	dk_efi.dki_data = gpt;
223 	dk_efi.dki_length = gpt_len;
224 
225 	if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
226 		VD_EFI_DEBUG("DKIOCGETEFI (GPT, LBA=1) error %d\n", status);
227 		goto errdone;
228 	}
229 
230 	if ((status = vd_efi_check_gpt(dev, gpt)) != 0) {
231 		/*
232 		 * No valid label here; try the alternate. The alternate GPT is
233 		 * located in the last block of the disk.
234 		 */
235 		dk_efi.dki_lba = dev->disk_size - 1;
236 		dk_efi.dki_data = gpt;
237 		dk_efi.dki_length = gpt_len;
238 
239 		if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
240 			VD_EFI_DEBUG("DKIOCGETEFI (LBA=%lu) error %d\n",
241 			    dev->disk_size - 1, status);
242 			goto errdone;
243 		}
244 
245 		if ((status = vd_efi_check_gpt(dev, gpt)) != 0)
246 			goto errdone;
247 
248 		VD_EFI_DEBUG("efi_read: primary label corrupt; using backup\n");
249 	}
250 
251 	/* swap GPT data after checking the GPT is valid */
252 	vd_efi_swap_gpt(gpt);
253 
254 	/*
255 	 * Read the EFI GPE.
256 	 */
257 	nparts = gpt->efi_gpt_NumberOfPartitionEntries;
258 
259 	if (nparts > NDKMAP + 1) {
260 		VD_EFI_DEBUG("Too many EFI partitions (%u)", nparts);
261 		status = EINVAL;
262 		goto errdone;
263 	}
264 
265 	if (nparts == 0) {
266 		VD_EFI_DEBUG("No partition defined");
267 		status = EINVAL;
268 		goto errdone;
269 	}
270 
271 	gpe_len = VD_EFI_GPE_LEN(dev, nparts);
272 	gpe = kmem_zalloc(gpe_len, KM_SLEEP);
273 
274 	dk_efi.dki_lba = gpt->efi_gpt_PartitionEntryLBA;
275 	dk_efi.dki_data = (efi_gpt_t *)gpe;
276 	dk_efi.dki_length = gpe_len;
277 
278 	if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
279 		VD_EFI_DEBUG("DKIOCGETEFI (GPE, LBA=%lu) error %d\n",
280 		    gpt->efi_gpt_PartitionEntryLBA, status);
281 		goto errdone;
282 	}
283 
284 	vd_efi_swap_gpe(gpe, nparts);
285 
286 	*efi_gpt = gpt;
287 	*efi_gpe = gpe;
288 
289 	return (0);
290 
291 errdone:
292 
293 	if (gpe != NULL)
294 		kmem_free(gpe, gpe_len);
295 	if (gpt != NULL)
296 		kmem_free(gpt, gpt_len);
297 
298 	return (status);
299 }
300 
301 /*
302  * Free the EFI GPE and GPT structures returned by vd_efi_alloc_and_read().
303  */
304 void
305 vd_efi_free(vd_efi_dev_t *dev, efi_gpt_t *gpt, efi_gpe_t *gpe)
306 {
307 	kmem_free(gpe, VD_EFI_GPE_LEN(dev,
308 	    gpt->efi_gpt_NumberOfPartitionEntries));
309 	kmem_free(gpt, dev->block_size);
310 }
311