1 | /* $NetBSD: dkwedge_gpt.c,v 1.17 2016/04/28 00:35:24 christos 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.17 2016/04/28 00:35:24 christos 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 | /* UTF-8 encoding stuff */ |
51 | #include <fs/unicode.h> |
52 | |
53 | /* |
54 | * GUID to dkw_ptype mapping information. |
55 | * |
56 | * GPT_ENT_TYPE_MS_BASIC_DATA is not suited to mapping. Aside from being |
57 | * used for multiple Microsoft file systems, Linux uses it for its own |
58 | * set of native file systems. Treating this GUID as unknown seems best. |
59 | */ |
60 | |
61 | static const struct { |
62 | struct uuid ptype_guid; |
63 | const char *ptype_str; |
64 | } gpt_ptype_guid_to_str_tab[] = { |
65 | { GPT_ENT_TYPE_EFI, DKW_PTYPE_FAT }, |
66 | { GPT_ENT_TYPE_NETBSD_SWAP, DKW_PTYPE_SWAP }, |
67 | { GPT_ENT_TYPE_FREEBSD_SWAP, DKW_PTYPE_SWAP }, |
68 | { GPT_ENT_TYPE_NETBSD_FFS, DKW_PTYPE_FFS }, |
69 | { GPT_ENT_TYPE_FREEBSD_UFS, DKW_PTYPE_FFS }, |
70 | { GPT_ENT_TYPE_APPLE_UFS, DKW_PTYPE_FFS }, |
71 | { GPT_ENT_TYPE_NETBSD_LFS, DKW_PTYPE_LFS }, |
72 | { GPT_ENT_TYPE_NETBSD_RAIDFRAME, DKW_PTYPE_RAIDFRAME }, |
73 | { GPT_ENT_TYPE_NETBSD_CCD, DKW_PTYPE_CCD }, |
74 | { GPT_ENT_TYPE_NETBSD_CGD, DKW_PTYPE_CGD }, |
75 | { GPT_ENT_TYPE_APPLE_HFS, DKW_PTYPE_APPLEHFS }, |
76 | }; |
77 | |
78 | static const char * |
79 | gpt_ptype_guid_to_str(const struct uuid *guid) |
80 | { |
81 | int i; |
82 | |
83 | for (i = 0; i < __arraycount(gpt_ptype_guid_to_str_tab); i++) { |
84 | if (memcmp(&gpt_ptype_guid_to_str_tab[i].ptype_guid, |
85 | guid, sizeof(*guid)) == 0) |
86 | return (gpt_ptype_guid_to_str_tab[i].ptype_str); |
87 | } |
88 | |
89 | return (DKW_PTYPE_UNKNOWN); |
90 | } |
91 | |
92 | static int |
93 | (struct gpt_hdr *hdr) |
94 | { |
95 | uint32_t crc; |
96 | int rv; |
97 | |
98 | crc = hdr->hdr_crc_self; |
99 | hdr->hdr_crc_self = 0; |
100 | rv = le32toh(crc) == crc32(0, (void *)hdr, le32toh(hdr->hdr_size)); |
101 | hdr->hdr_crc_self = crc; |
102 | |
103 | return (rv); |
104 | } |
105 | |
106 | static int |
107 | dkwedge_discover_gpt(struct disk *pdk, struct vnode *vp) |
108 | { |
109 | static const struct uuid ent_type_unused = GPT_ENT_TYPE_UNUSED; |
110 | static const char gpt_hdr_sig[] = GPT_HDR_SIG; |
111 | struct dkwedge_info dkw; |
112 | void *buf; |
113 | uint32_t secsize; |
114 | struct gpt_hdr *hdr; |
115 | struct gpt_ent *ent; |
116 | uint32_t entries, entsz; |
117 | daddr_t lba_start, lba_end, lba_table; |
118 | uint32_t gpe_crc; |
119 | int error; |
120 | u_int i; |
121 | size_t r, n; |
122 | uint8_t *c; |
123 | |
124 | secsize = DEV_BSIZE << pdk->dk_blkshift; |
125 | buf = malloc(secsize, M_DEVBUF, M_WAITOK); |
126 | |
127 | /* |
128 | * Note: We don't bother with a Legacy or Protective MBR |
129 | * here. If a GPT is found, then the search stops, and |
130 | * the GPT is authoritative. |
131 | */ |
132 | |
133 | /* Read in the GPT Header. */ |
134 | error = dkwedge_read(pdk, vp, GPT_HDR_BLKNO << pdk->dk_blkshift, buf, secsize); |
135 | if (error) |
136 | goto out; |
137 | hdr = buf; |
138 | |
139 | /* Validate it. */ |
140 | if (memcmp(gpt_hdr_sig, hdr->hdr_sig, sizeof(hdr->hdr_sig)) != 0) { |
141 | /* XXX Should check at end-of-disk. */ |
142 | error = ESRCH; |
143 | goto out; |
144 | } |
145 | if (hdr->hdr_revision != htole32(GPT_HDR_REVISION)) { |
146 | /* XXX Should check at end-of-disk. */ |
147 | error = ESRCH; |
148 | goto out; |
149 | } |
150 | if (le32toh(hdr->hdr_size) > secsize) { |
151 | /* XXX Should check at end-of-disk. */ |
152 | error = ESRCH; |
153 | goto out; |
154 | } |
155 | if (gpt_verify_header_crc(hdr) == 0) { |
156 | /* XXX Should check at end-of-disk. */ |
157 | error = ESRCH; |
158 | goto out; |
159 | } |
160 | |
161 | /* XXX Now that we found it, should we validate the backup? */ |
162 | |
163 | { |
164 | struct uuid disk_guid; |
165 | char guid_str[UUID_STR_LEN]; |
166 | uuid_dec_le(hdr->hdr_guid, &disk_guid); |
167 | uuid_snprintf(guid_str, sizeof(guid_str), &disk_guid); |
168 | aprint_verbose("%s: GPT GUID: %s\n" , pdk->dk_name, guid_str); |
169 | } |
170 | |
171 | entries = le32toh(hdr->hdr_entries); |
172 | entsz = roundup(le32toh(hdr->hdr_entsz), 8); |
173 | if (entsz > roundup(sizeof(struct gpt_ent), 8)) { |
174 | aprint_error("%s: bogus GPT entry size: %u\n" , |
175 | pdk->dk_name, le32toh(hdr->hdr_entsz)); |
176 | error = EINVAL; |
177 | goto out; |
178 | } |
179 | gpe_crc = le32toh(hdr->hdr_crc_table); |
180 | |
181 | /* XXX Clamp entries at 512 for now. */ |
182 | if (entries > 512) { |
183 | aprint_error("%s: WARNING: clamping number of GPT entries to " |
184 | "512 (was %u)\n" , pdk->dk_name, entries); |
185 | entries = 512; |
186 | } |
187 | |
188 | lba_start = le64toh(hdr->hdr_lba_start); |
189 | lba_end = le64toh(hdr->hdr_lba_end); |
190 | lba_table = le64toh(hdr->hdr_lba_table); |
191 | if (lba_start < 0 || lba_end < 0 || lba_table < 0) { |
192 | aprint_error("%s: GPT block numbers out of range\n" , |
193 | pdk->dk_name); |
194 | error = EINVAL; |
195 | goto out; |
196 | } |
197 | |
198 | free(buf, M_DEVBUF); |
199 | buf = malloc(roundup(entries * entsz, secsize), M_DEVBUF, M_WAITOK); |
200 | error = dkwedge_read(pdk, vp, lba_table << pdk->dk_blkshift, buf, |
201 | roundup(entries * entsz, secsize)); |
202 | if (error) { |
203 | /* XXX Should check alternate location. */ |
204 | aprint_error("%s: unable to read GPT partition array, " |
205 | "error = %d\n" , pdk->dk_name, error); |
206 | goto out; |
207 | } |
208 | |
209 | if (crc32(0, buf, entries * entsz) != gpe_crc) { |
210 | /* XXX Should check alternate location. */ |
211 | aprint_error("%s: bad GPT partition array CRC\n" , |
212 | pdk->dk_name); |
213 | error = EINVAL; |
214 | goto out; |
215 | } |
216 | |
217 | /* |
218 | * Walk the partitions, adding a wedge for each type we know about. |
219 | */ |
220 | for (i = 0; i < entries; i++) { |
221 | struct uuid ptype_guid, ent_guid; |
222 | const char *ptype; |
223 | int j; |
224 | char ptype_guid_str[UUID_STR_LEN], ent_guid_str[UUID_STR_LEN]; |
225 | |
226 | ent = (struct gpt_ent *)((char *)buf + (i * entsz)); |
227 | |
228 | uuid_dec_le(ent->ent_type, &ptype_guid); |
229 | if (memcmp(&ptype_guid, &ent_type_unused, |
230 | sizeof(ptype_guid)) == 0) |
231 | continue; |
232 | |
233 | uuid_dec_le(ent->ent_guid, &ent_guid); |
234 | |
235 | uuid_snprintf(ptype_guid_str, sizeof(ptype_guid_str), |
236 | &ptype_guid); |
237 | uuid_snprintf(ent_guid_str, sizeof(ent_guid_str), |
238 | &ent_guid); |
239 | |
240 | /* figure out the type */ |
241 | ptype = gpt_ptype_guid_to_str(&ptype_guid); |
242 | strcpy(dkw.dkw_ptype, ptype); |
243 | |
244 | strcpy(dkw.dkw_parent, pdk->dk_name); |
245 | dkw.dkw_offset = le64toh(ent->ent_lba_start); |
246 | dkw.dkw_size = le64toh(ent->ent_lba_end) - dkw.dkw_offset + 1; |
247 | |
248 | /* XXX Make sure it falls within the disk's data area. */ |
249 | |
250 | if (ent->ent_name[0] == 0x0000) |
251 | strcpy(dkw.dkw_wname, ent_guid_str); |
252 | else { |
253 | c = dkw.dkw_wname; |
254 | r = sizeof(dkw.dkw_wname) - 1; |
255 | for (j = 0; ent->ent_name[j] != 0x0000; j++) { |
256 | n = wput_utf8(c, r, le16toh(ent->ent_name[j])); |
257 | if (n == 0) |
258 | break; |
259 | c += n; r -= n; |
260 | } |
261 | *c = '\0'; |
262 | } |
263 | |
264 | /* |
265 | * Try with the partition name first. If that fails, |
266 | * use the GUID string. If that fails, punt. |
267 | */ |
268 | if ((error = dkwedge_add(&dkw)) == EEXIST && |
269 | strcmp(dkw.dkw_wname, ent_guid_str) != 0) { |
270 | char orig[sizeof(dkw.dkw_wname)]; |
271 | strcpy(orig, dkw.dkw_wname); |
272 | strcpy(dkw.dkw_wname, ent_guid_str); |
273 | error = dkwedge_add(&dkw); |
274 | if (!error) |
275 | aprint_error("%s: wedge named '%s' already " |
276 | "existed, using '%s'\n" , pdk->dk_name, |
277 | orig, ent_guid_str); |
278 | } |
279 | if (error == EEXIST) |
280 | aprint_error("%s: wedge named '%s' already exists, " |
281 | "manual intervention required\n" , pdk->dk_name, |
282 | dkw.dkw_wname); |
283 | else if (error) |
284 | aprint_error("%s: error %d adding entry %u (%s), " |
285 | "type %s\n" , pdk->dk_name, error, i, ent_guid_str, |
286 | ptype_guid_str); |
287 | } |
288 | error = 0; |
289 | |
290 | out: |
291 | free(buf, M_DEVBUF); |
292 | return (error); |
293 | } |
294 | |
295 | DKWEDGE_DISCOVERY_METHOD_DECL(GPT, 0, dkwedge_discover_gpt); |
296 | |