xref: /netbsd-src/sys/dev/dkwedge/dkwedge_gpt.c (revision 7f21db1c0118155e0dd40b75182e30c589d9f63e)
1 /*	$NetBSD: dkwedge_gpt.c,v 1.11 2010/01/25 14:51:03 mlelstv Exp $	*/
2 
3 /*-
4  * Copyright (c) 2004 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * EFI GUID Partition Table support for disk wedges
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: dkwedge_gpt.c,v 1.11 2010/01/25 14:51:03 mlelstv Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/proc.h>
42 #include <sys/errno.h>
43 #include <sys/disk.h>
44 #include <sys/vnode.h>
45 #include <sys/malloc.h>
46 
47 #include <sys/disklabel_gpt.h>
48 #include <sys/uuid.h>
49 
50 /*
51  * GUID to dkw_ptype mapping information.
52  *
53  * GPT_ENT_TYPE_MS_BASIC_DATA is not suited to mapping.  Aside from being
54  * used for multiple Microsoft file systems, Linux uses it for it's own
55  * set of native file systems.  Treating this GUID as unknown seems best.
56  */
57 
58 static const struct {
59 	struct uuid ptype_guid;
60 	const char *ptype_str;
61 } gpt_ptype_guid_to_str_tab[] = {
62 	{ GPT_ENT_TYPE_EFI,			DKW_PTYPE_FAT },
63 	{ GPT_ENT_TYPE_NETBSD_SWAP,		DKW_PTYPE_SWAP },
64 	{ GPT_ENT_TYPE_FREEBSD_SWAP,		DKW_PTYPE_SWAP },
65 	{ GPT_ENT_TYPE_NETBSD_FFS,		DKW_PTYPE_FFS },
66 	{ GPT_ENT_TYPE_FREEBSD_UFS,		DKW_PTYPE_FFS },
67 	{ GPT_ENT_TYPE_APPLE_UFS,		DKW_PTYPE_FFS },
68 	{ GPT_ENT_TYPE_NETBSD_LFS,		DKW_PTYPE_LFS },
69 	{ GPT_ENT_TYPE_NETBSD_RAIDFRAME,	DKW_PTYPE_RAIDFRAME },
70 	{ GPT_ENT_TYPE_NETBSD_CCD,		DKW_PTYPE_CCD },
71 	{ GPT_ENT_TYPE_NETBSD_CGD,		DKW_PTYPE_CGD },
72 	{ GPT_ENT_TYPE_APPLE_HFS,		DKW_PTYPE_APPLEHFS },
73 };
74 
75 static const char *
76 gpt_ptype_guid_to_str(const struct uuid *guid)
77 {
78 	int i;
79 
80 	for (i = 0; i < __arraycount(gpt_ptype_guid_to_str_tab); i++) {
81 		if (memcmp(&gpt_ptype_guid_to_str_tab[i].ptype_guid,
82 			   guid, sizeof(*guid)) == 0)
83 			return (gpt_ptype_guid_to_str_tab[i].ptype_str);
84 	}
85 
86 	return (DKW_PTYPE_UNKNOWN);
87 }
88 
89 static const uint32_t gpt_crc_tab[16] = {
90 	0x00000000U, 0x1db71064U, 0x3b6e20c8U, 0x26d930acU,
91 	0x76dc4190U, 0x6b6b51f4U, 0x4db26158U, 0x5005713cU,
92 	0xedb88320U, 0xf00f9344U, 0xd6d6a3e8U, 0xcb61b38cU,
93 	0x9b64c2b0U, 0x86d3d2d4U, 0xa00ae278U, 0xbdbdf21cU
94 };
95 
96 static uint32_t
97 gpt_crc32(const void *vbuf, size_t len)
98 {
99 	const uint8_t *buf = vbuf;
100 	uint32_t crc;
101 
102 	crc = 0xffffffffU;
103 	while (len--) {
104 		crc ^= *buf++;
105 		crc = (crc >> 4) ^ gpt_crc_tab[crc & 0xf];
106 		crc = (crc >> 4) ^ gpt_crc_tab[crc & 0xf];
107 	}
108 
109 	return (crc ^ 0xffffffffU);
110 }
111 
112 static int
113 gpt_verify_header_crc(struct gpt_hdr *hdr)
114 {
115 	uint32_t crc;
116 	int rv;
117 
118 	crc = hdr->hdr_crc_self;
119 	hdr->hdr_crc_self = 0;
120 	rv = le32toh(crc) == gpt_crc32(hdr, le32toh(hdr->hdr_size));
121 	hdr->hdr_crc_self = crc;
122 
123 	return (rv);
124 }
125 
126 static int
127 dkwedge_discover_gpt(struct disk *pdk, struct vnode *vp)
128 {
129 	static const struct uuid ent_type_unused = GPT_ENT_TYPE_UNUSED;
130 	static const char gpt_hdr_sig[] = GPT_HDR_SIG;
131 	struct dkwedge_info dkw;
132 	void *buf;
133 	uint32_t secsize;
134 	struct gpt_hdr *hdr;
135 	struct gpt_ent *ent;
136 	uint32_t entries, entsz;
137 	daddr_t lba_start, lba_end, lba_table;
138 	uint32_t gpe_crc;
139 	int error;
140 	u_int i;
141 
142 	secsize = DEV_BSIZE << pdk->dk_blkshift;
143 	buf = malloc(secsize, M_DEVBUF, M_WAITOK);
144 
145 	/*
146 	 * Note: We don't bother with a Legacy or Protective MBR
147 	 * here.  If a GPT is found, then the search stops, and
148 	 * the GPT is authoritative.
149 	 */
150 
151 	/* Read in the GPT Header. */
152 	error = dkwedge_read(pdk, vp, GPT_HDR_BLKNO << pdk->dk_blkshift, buf, secsize);
153 	if (error)
154 		goto out;
155 	hdr = buf;
156 
157 	/* Validate it. */
158 	if (memcmp(gpt_hdr_sig, hdr->hdr_sig, sizeof(hdr->hdr_sig)) != 0) {
159 		/* XXX Should check at end-of-disk. */
160 		error = ESRCH;
161 		goto out;
162 	}
163 	if (hdr->hdr_revision != htole32(GPT_HDR_REVISION)) {
164 		/* XXX Should check at end-of-disk. */
165 		error = ESRCH;
166 		goto out;
167 	}
168 	if (le32toh(hdr->hdr_size) > secsize) {
169 		/* XXX Should check at end-of-disk. */
170 		error = ESRCH;
171 		goto out;
172 	}
173 	if (gpt_verify_header_crc(hdr) == 0) {
174 		/* XXX Should check at end-of-disk. */
175 		error = ESRCH;
176 		goto out;
177 	}
178 
179 	/* XXX Now that we found it, should we validate the backup? */
180 
181 	{
182 		struct uuid disk_guid;
183 		char guid_str[UUID_STR_LEN];
184 		uuid_dec_le(hdr->hdr_guid, &disk_guid);
185 		uuid_snprintf(guid_str, sizeof(guid_str), &disk_guid);
186 		aprint_verbose("%s: GPT GUID: %s\n", pdk->dk_name, guid_str);
187 	}
188 
189 	entries = le32toh(hdr->hdr_entries);
190 	entsz = roundup(le32toh(hdr->hdr_entsz), 8);
191 	if (entsz > roundup(sizeof(struct gpt_ent), 8)) {
192 		aprint_error("%s: bogus GPT entry size: %u\n",
193 		    pdk->dk_name, le32toh(hdr->hdr_entsz));
194 		error = EINVAL;
195 		goto out;
196 	}
197 	gpe_crc = le32toh(hdr->hdr_crc_table);
198 
199 	/* XXX Clamp entries at 128 for now. */
200 	if (entries > 128) {
201 		aprint_error("%s: WARNING: clamping number of GPT entries to "
202 		    "128 (was %u)\n", pdk->dk_name, entries);
203 		entries = 128;
204 	}
205 
206 	lba_start = le64toh(hdr->hdr_lba_start);
207 	lba_end = le64toh(hdr->hdr_lba_end);
208 	lba_table = le64toh(hdr->hdr_lba_table);
209 	if (lba_start < 0 || lba_end < 0 || lba_table < 0) {
210 		aprint_error("%s: GPT block numbers out of range\n",
211 		    pdk->dk_name);
212 		error = EINVAL;
213 		goto out;
214 	}
215 
216 	free(buf, M_DEVBUF);
217 	buf = malloc(roundup(entries * entsz, secsize), M_DEVBUF, M_WAITOK);
218 	error = dkwedge_read(pdk, vp, lba_table << pdk->dk_blkshift, buf,
219 			     roundup(entries * entsz, secsize));
220 	if (error) {
221 		/* XXX Should check alternate location. */
222 		aprint_error("%s: unable to read GPT partition array, "
223 		    "error = %d\n", pdk->dk_name, error);
224 		goto out;
225 	}
226 
227 	if (gpt_crc32(buf, entries * entsz) != gpe_crc) {
228 		/* XXX Should check alternate location. */
229 		aprint_error("%s: bad GPT partition array CRC\n",
230 		    pdk->dk_name);
231 		error = EINVAL;
232 		goto out;
233 	}
234 
235 	/*
236 	 * Walk the partitions, adding a wedge for each type we know about.
237 	 */
238 	for (i = 0; i < entries; i++) {
239 		struct uuid ptype_guid, ent_guid;
240 		const char *ptype;
241 		int j;
242 		char ptype_guid_str[UUID_STR_LEN], ent_guid_str[UUID_STR_LEN];
243 
244 		ent = (struct gpt_ent *)((char *)buf + (i * entsz));
245 
246 		uuid_dec_le(ent->ent_type, &ptype_guid);
247 		if (memcmp(&ptype_guid, &ent_type_unused,
248 			   sizeof(ptype_guid)) == 0)
249 			continue;
250 
251 		uuid_dec_le(ent->ent_guid, &ent_guid);
252 
253 		uuid_snprintf(ptype_guid_str, sizeof(ptype_guid_str),
254 		    &ptype_guid);
255 		uuid_snprintf(ent_guid_str, sizeof(ent_guid_str),
256 		    &ent_guid);
257 
258 		/* figure out the type */
259 		ptype = gpt_ptype_guid_to_str(&ptype_guid);
260 		strcpy(dkw.dkw_ptype, ptype);
261 
262 		strcpy(dkw.dkw_parent, pdk->dk_name);
263 		dkw.dkw_offset = le64toh(ent->ent_lba_start);
264 		dkw.dkw_size = le64toh(ent->ent_lba_end) - dkw.dkw_offset + 1;
265 
266 		/* XXX Make sure it falls within the disk's data area. */
267 
268 		if (ent->ent_name[0] == 0x0000)
269 			strcpy(dkw.dkw_wname, ent_guid_str);
270 		else {
271 			for (j = 0; ent->ent_name[j] != 0x0000; j++) {
272 				/* XXX UTF-16 -> UTF-8 */
273 				dkw.dkw_wname[j] =
274 				    le16toh(ent->ent_name[j]) & 0xff;
275 			}
276 			dkw.dkw_wname[j] = '\0';
277 		}
278 
279 		/*
280 		 * Try with the partition name first.  If that fails,
281 		 * use the GUID string.  If that fails, punt.
282 		 */
283 		if ((error = dkwedge_add(&dkw)) == EEXIST) {
284 			aprint_error("%s: wedge named '%s' already exists, "
285 			    "trying '%s'\n", pdk->dk_name,
286 			    dkw.dkw_wname, /* XXX Unicode */
287 			    ent_guid_str);
288 			strcpy(dkw.dkw_wname, ent_guid_str);
289 			error = dkwedge_add(&dkw);
290 		}
291 		if (error == EEXIST)
292 			aprint_error("%s: wedge named '%s' already exists, "
293 			    "manual intervention required\n", pdk->dk_name,
294 			    dkw.dkw_wname);
295 		else if (error)
296 			aprint_error("%s: error %d adding entry %u (%s), "
297 			    "type %s\n", pdk->dk_name, error, i, ent_guid_str,
298 			    ptype_guid_str);
299 	}
300 	error = 0;
301 
302  out:
303 	free(buf, M_DEVBUF);
304 	return (error);
305 }
306 
307 DKWEDGE_DISCOVERY_METHOD_DECL(GPT, 0, dkwedge_discover_gpt);
308