xref: /dflybsd-src/sys/kern/subr_diskgpt.c (revision fb8e1c0c0541c89d2a73956362ee4eb015a16101)
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sys/kern/subr_diskgpt.c,v 1.3 2007/06/19 06:07:57 dillon Exp $
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/conf.h>
40 #include <sys/endian.h>
41 #include <sys/diskslice.h>
42 #include <sys/diskmbr.h>
43 #include <sys/disk.h>
44 #include <sys/buf.h>
45 #include <sys/malloc.h>
46 #include <sys/syslog.h>
47 #include <sys/bus.h>
48 #include <sys/device.h>
49 #include <sys/gpt.h>
50 
51 #define arysize(ary)	(sizeof(ary)/sizeof((ary)[0]))
52 
53 static void gpt_setslice(const char *sname, struct disk_info *info,
54 			 struct diskslice *sp, struct gpt_ent *sent);
55 
56 /*
57  * Handle GPT on raw disk.  Note that GPTs are not recursive.  The MBR is
58  * ignored once a GPT has been detected.
59  *
60  * GPTs always start at block #1, regardless of how the MBR has been set up.
61  * In fact, the MBR's starting block might be pointing to the boot partition
62  * in the GPT rather then to the start of the GPT.
63  *
64  * This routine is called from mbrinit() when a GPT has been detected.
65  */
66 int
67 gptinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp)
68 {
69 	struct buf *bp1 = NULL;
70 	struct buf *bp2 = NULL;
71 	struct gpt_hdr *gpt;
72 	struct gpt_ent *ent;
73 	struct diskslice *sp;
74 	struct diskslices *ssp;
75 	cdev_t wdev;
76 	int error;
77 	uint32_t len;
78 	uint32_t entries;
79 	uint32_t entsz;
80 	uint32_t crc;
81 	uint32_t table_lba;
82 	uint32_t table_blocks;
83 	int i = 0, j;
84 	const char *dname;
85 
86 	/*
87 	 * The GPT starts in sector 1.
88 	 */
89 	wdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), WHOLE_SLICE_PART);
90 	dname = dev_dname(wdev);
91 	bp1 = geteblk((int)info->d_media_blksize);
92 	bp1->b_bio1.bio_offset = info->d_media_blksize;
93 	bp1->b_bcount = info->d_media_blksize;
94 	bp1->b_cmd = BUF_CMD_READ;
95 	dev_dstrategy(wdev, &bp1->b_bio1);
96 	if (biowait(bp1) != 0) {
97 		kprintf("%s: reading GPT @ block 1: error %d\n",
98 			dname, bp1->b_error);
99 		error = EIO;
100 		goto done;
101 	}
102 
103 	/*
104 	 * Header sanity check
105 	 */
106 	gpt = (void *)bp1->b_data;
107 	len = le32toh(gpt->hdr_size);
108 	if (len < GPT_MIN_HDR_SIZE || len > info->d_media_blksize) {
109 		kprintf("%s: Illegal GPT header size %d\n", dname, len);
110 		error = EINVAL;
111 		goto done;
112 	}
113 
114 	crc = le32toh(gpt->hdr_crc_self);
115 	gpt->hdr_crc_self = 0;
116 	if (crc32(gpt, len) != crc) {
117 		kprintf("%s: GPT CRC32 did not match\n", dname);
118 		error = EINVAL;
119 		goto done;
120 	}
121 
122 	/*
123 	 * Validate the partition table and its location, then read it
124 	 * into a buffer.
125 	 */
126 	entries = le32toh(gpt->hdr_entries);
127 	entsz = le32toh(gpt->hdr_entsz);
128 	table_lba = le32toh(gpt->hdr_lba_table);
129 	table_blocks = (entries * entsz + info->d_media_blksize - 1) /
130 		       info->d_media_blksize;
131 	if (entries < 1 || entries > 128 ||
132 	    entsz < 128 || (entsz & 7) || entsz > MAXBSIZE / entries ||
133 	    table_lba < 2 || table_lba + table_blocks > info->d_media_blocks) {
134 		kprintf("%s: GPT partition table is out of bounds\n", dname);
135 		error = EINVAL;
136 		goto done;
137 	}
138 
139 	bp2 = geteblk((int)(table_blocks * info->d_media_blksize));
140 	bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize;
141 	bp2->b_bcount = table_blocks * info->d_media_blksize;
142 	bp2->b_cmd = BUF_CMD_READ;
143 	dev_dstrategy(wdev, &bp2->b_bio1);
144 	if (biowait(bp2) != 0) {
145 		kprintf("%s: reading GPT partition table @ %lld: error %d\n",
146 			dname, bp2->b_bio1.bio_offset, bp2->b_error);
147 		error = EIO;
148 		goto done;
149 	}
150 
151 	/*
152 	 * We are passed a pointer to a minimal slices struct.  Replace
153 	 * it with a maximal one (128 slices + special slices).  Well,
154 	 * really there is only one special slice (the WHOLE_DISK_SLICE)
155 	 * since we use the compatibility slice for s0, but don't quibble.
156 	 *
157 	 */
158 	kfree(*sspp, M_DEVBUF);
159 	ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info);
160 
161 	/*
162 	 * Create a slice for each partition.
163 	 */
164 	for (i = 0; i < (int)entries && i < 128; ++i) {
165 		struct gpt_ent sent;
166 		char partname[2];
167 		char *sname;
168 
169 		ent = (void *)((char *)bp2->b_data + i * entsz);
170 		le_uuid_dec(&ent->ent_type, &sent.ent_type);
171 		le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid);
172 		sent.ent_lba_start = le64toh(ent->ent_lba_start);
173 		sent.ent_lba_end = le64toh(ent->ent_lba_end);
174 		sent.ent_attr = le64toh(ent->ent_attr);
175 
176 		for (j = 0; j < arysize(ent->ent_name); ++j)
177 			sent.ent_name[j] = le16toh(ent->ent_name[j]);
178 
179 		/*
180 		 * The COMPATIBILITY_SLICE is actually slice 0 (s0).  This
181 		 * is a bit weird becaue the whole-disk slice is #1, so
182 		 * slice 1 (s1) starts at BASE_SLICE.
183 		 */
184 		if (i == 0)
185 			sp = &ssp->dss_slices[COMPATIBILITY_SLICE];
186 		else
187 			sp = &ssp->dss_slices[BASE_SLICE+i-1];
188 		sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE,
189 			       WHOLE_SLICE_PART, partname);
190 
191 		if (kuuid_is_nil(&sent.ent_type))
192 			continue;
193 
194 		if (sent.ent_lba_start < table_lba + table_blocks ||
195 		    sent.ent_lba_end >= info->d_media_blocks ||
196 		    sent.ent_lba_start >= sent.ent_lba_end) {
197 			kprintf("%s part %d: unavailable, bad start or "
198 				"ending lba\n",
199 				sname, i);
200 		} else {
201 			gpt_setslice(sname, info, sp, &sent);
202 		}
203 	}
204 	ssp->dss_nslices = BASE_SLICE + i;
205 
206 	error = 0;
207 done:
208 	if (bp1) {
209 		bp1->b_flags |= B_INVAL | B_AGE;
210 		brelse(bp1);
211 	}
212 	if (bp2) {
213 		bp2->b_flags |= B_INVAL | B_AGE;
214 		brelse(bp2);
215 	}
216 	if (error == EINVAL)
217 		error = 0;
218 	return (error);
219 }
220 
221 static
222 void
223 gpt_setslice(const char *sname, struct disk_info *info, struct diskslice *sp,
224 	     struct gpt_ent *sent)
225 {
226 	sp->ds_offset = sent->ent_lba_start;
227 	sp->ds_size   = sent->ent_lba_end + 1 - sent->ent_lba_start;
228 	sp->ds_type   = 1;	/* XXX */
229 	sp->ds_type_uuid = sent->ent_type;
230 	sp->ds_stor_uuid = sent->ent_uuid;
231 	sp->ds_reserved = 0;	/* no reserved sectors */
232 }
233 
234