1 /* $NetBSD: dkwedge_gpt.c,v 1.9 2008/06/29 15:13:28 christos Exp $ */
4 * Copyright (c) 2004 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 * EFI GUID Partition Table support for disk wedges
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: dkwedge_gpt.c,v 1.9 2008/06/29 15:13:28 christos Exp $");
39 #include <sys/param.h>
40 #include <sys/systm.h>
42 #include <sys/errno.h>
44 #include <sys/vnode.h>
45 #include <sys/malloc.h>
47 #include <sys/disklabel_gpt.h>
51 * GUID to dkw_ptype mapping information.
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.
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
},
76 gpt_ptype_guid_to_str(const struct uuid
*guid
)
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
);
86 return (DKW_PTYPE_UNKNOWN
);
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
97 gpt_crc32(const void *vbuf
, size_t len
)
99 const uint8_t *buf
= vbuf
;
105 crc
= (crc
>> 4) ^ gpt_crc_tab
[crc
& 0xf];
106 crc
= (crc
>> 4) ^ gpt_crc_tab
[crc
& 0xf];
109 return (crc
^ 0xffffffffU
);
113 gpt_verify_header_crc(struct gpt_hdr
*hdr
)
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
;
127 dkwedge_discover_gpt(struct disk
*pdk
, struct vnode
*vp
)
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
;
135 uint32_t entries
, entsz
;
136 daddr_t lba_start
, lba_end
, lba_table
;
141 buf
= malloc(DEV_BSIZE
, M_DEVBUF
, M_WAITOK
);
144 * Note: We don't bother with a Legacy or Protective MBR
145 * here. If a GPT is found, then the search stops, and
146 * the GPT is authoritative.
149 /* Read in the GPT Header. */
150 error
= dkwedge_read(pdk
, vp
, GPT_HDR_BLKNO
, buf
, DEV_BSIZE
);
156 if (memcmp(gpt_hdr_sig
, hdr
->hdr_sig
, sizeof(hdr
->hdr_sig
)) != 0) {
157 /* XXX Should check at end-of-disk. */
161 if (hdr
->hdr_revision
!= htole32(GPT_HDR_REVISION
)) {
162 /* XXX Should check at end-of-disk. */
166 if (le32toh(hdr
->hdr_size
) > DEV_BSIZE
) {
167 /* XXX Should check at end-of-disk. */
171 if (gpt_verify_header_crc(hdr
) == 0) {
172 /* XXX Should check at end-of-disk. */
177 /* XXX Now that we found it, should we validate the backup? */
180 struct uuid disk_guid
;
181 char guid_str
[UUID_STR_LEN
];
182 uuid_dec_le(hdr
->hdr_guid
, &disk_guid
);
183 uuid_snprintf(guid_str
, sizeof(guid_str
), &disk_guid
);
184 aprint_verbose("%s: GPT GUID: %s\n", pdk
->dk_name
, guid_str
);
187 entries
= le32toh(hdr
->hdr_entries
);
188 entsz
= roundup(le32toh(hdr
->hdr_entsz
), 8);
189 if (entsz
> roundup(sizeof(struct gpt_ent
), 8)) {
190 aprint_error("%s: bogus GPT entry size: %u\n",
191 pdk
->dk_name
, le32toh(hdr
->hdr_entsz
));
195 gpe_crc
= le32toh(hdr
->hdr_crc_table
);
197 /* XXX Clamp entries at 128 for now. */
199 aprint_error("%s: WARNING: clamping number of GPT entries to "
200 "128 (was %u)\n", pdk
->dk_name
, entries
);
204 lba_start
= le64toh(hdr
->hdr_lba_start
);
205 lba_end
= le64toh(hdr
->hdr_lba_end
);
206 lba_table
= le64toh(hdr
->hdr_lba_table
);
207 if (lba_start
< 0 || lba_end
< 0 || lba_table
< 0) {
208 aprint_error("%s: GPT block numbers out of range\n",
215 buf
= malloc(roundup(entries
* entsz
, DEV_BSIZE
), M_DEVBUF
, M_WAITOK
);
216 error
= dkwedge_read(pdk
, vp
, lba_table
, buf
,
217 roundup(entries
* entsz
, DEV_BSIZE
));
219 /* XXX Should check alternate location. */
220 aprint_error("%s: unable to read GPT partition array, "
221 "error = %d\n", pdk
->dk_name
, error
);
225 if (gpt_crc32(buf
, entries
* entsz
) != gpe_crc
) {
226 /* XXX Should check alternate location. */
227 aprint_error("%s: bad GPT partition array CRC\n",
234 * Walk the partitions, adding a wedge for each type we know about.
236 for (i
= 0; i
< entries
; i
++) {
237 struct uuid ptype_guid
, ent_guid
;
240 char ptype_guid_str
[UUID_STR_LEN
], ent_guid_str
[UUID_STR_LEN
];
242 ent
= (struct gpt_ent
*)((char *)buf
+ (i
* entsz
));
244 uuid_dec_le(ent
->ent_type
, &ptype_guid
);
245 if (memcmp(&ptype_guid
, &ent_type_unused
,
246 sizeof(ptype_guid
)) == 0)
249 uuid_dec_le(ent
->ent_guid
, &ent_guid
);
251 uuid_snprintf(ptype_guid_str
, sizeof(ptype_guid_str
),
253 uuid_snprintf(ent_guid_str
, sizeof(ent_guid_str
),
256 /* figure out the type */
257 ptype
= gpt_ptype_guid_to_str(&ptype_guid
);
258 strcpy(dkw
.dkw_ptype
, ptype
);
260 strcpy(dkw
.dkw_parent
, pdk
->dk_name
);
261 dkw
.dkw_offset
= le64toh(ent
->ent_lba_start
);
262 dkw
.dkw_size
= le64toh(ent
->ent_lba_end
) - dkw
.dkw_offset
+ 1;
264 /* XXX Make sure it falls within the disk's data area. */
266 if (ent
->ent_name
[0] == 0x0000)
267 strcpy(dkw
.dkw_wname
, ent_guid_str
);
269 for (j
= 0; ent
->ent_name
[j
] != 0x0000; j
++) {
270 /* XXX UTF-16 -> UTF-8 */
272 le16toh(ent
->ent_name
[j
]) & 0xff;
274 dkw
.dkw_wname
[j
] = '\0';
278 * Try with the partition name first. If that fails,
279 * use the GUID string. If that fails, punt.
281 if ((error
= dkwedge_add(&dkw
)) == EEXIST
) {
282 aprint_error("%s: wedge named '%s' already exists, "
283 "trying '%s'\n", pdk
->dk_name
,
284 dkw
.dkw_wname
, /* XXX Unicode */
286 strcpy(dkw
.dkw_wname
, ent_guid_str
);
287 error
= dkwedge_add(&dkw
);
290 aprint_error("%s: wedge named '%s' already exists, "
291 "manual intervention required\n", pdk
->dk_name
,
294 aprint_error("%s: error %d adding entry %u (%s), "
295 "type %s\n", pdk
->dk_name
, error
, i
, ent_guid_str
,
305 DKWEDGE_DISCOVERY_METHOD_DECL(GPT
, 0, dkwedge_discover_gpt
);