xref: /dragonfly/sys/kern/subr_diskgpt.c (revision bf20632cc11d2c142354ef0f8c9ca6a3f1680a63)
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 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/endian.h>
39 #include <sys/diskslice.h>
40 #include <sys/diskmbr.h>
41 #include <sys/disk.h>
42 #include <sys/buf.h>
43 #include <sys/malloc.h>
44 #include <sys/syslog.h>
45 #include <sys/bus.h>
46 #include <sys/device.h>
47 #include <sys/gpt.h>
48 
49 static void gpt_setslice(const char *sname, struct disk_info *info,
50                                struct diskslice *sp, struct gpt_ent *sent);
51 
52 /*
53  * Handle GPT on raw disk.  Note that GPTs are not recursive.  The MBR is
54  * ignored once a GPT has been detected.
55  *
56  * GPTs always start at block #1, regardless of how the MBR has been set up.
57  * In fact, the MBR's starting block might be pointing to the boot partition
58  * in the GPT rather then to the start of the GPT.
59  *
60  * This routine is called from mbrinit() when a GPT has been detected.
61  */
62 int
gptinit(cdev_t dev,struct disk_info * info,struct diskslices ** sspp)63 gptinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp)
64 {
65           struct buf *bp1 = NULL;
66           struct buf *bp2 = NULL;
67           struct gpt_hdr *gpt;
68           struct gpt_ent *ent;
69           struct diskslice *sp;
70           struct diskslices *ssp;
71           cdev_t wdev;
72           int error;
73           uint32_t len;
74           uint32_t entries;
75           uint32_t entsz;
76           uint32_t crc;
77           uint32_t table_lba;
78           uint32_t table_blocks;
79           int i = 0, j;
80           const char *dname;
81 
82           /*
83            * The GPT starts in sector 1.
84            */
85           wdev = dev;
86           dname = dev_dname(wdev);
87           bp1 = getpbuf_mem(NULL);
88           KKASSERT(info->d_media_blksize <= bp1->b_bufsize);
89           bp1->b_bio1.bio_offset = info->d_media_blksize;
90           bp1->b_bio1.bio_done = biodone_sync;
91           bp1->b_bio1.bio_flags |= BIO_SYNC;
92           bp1->b_bcount = info->d_media_blksize;
93           bp1->b_cmd = BUF_CMD_READ;
94           bp1->b_flags |= B_FAILONDIS;
95           dev_dstrategy(wdev, &bp1->b_bio1);
96           if (biowait(&bp1->b_bio1, "gptrd") != 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           /*
140            * XXX subject to device dma size limitations
141            */
142           bp2 = getpbuf_mem(NULL);
143           KKASSERT((int)(table_blocks * info->d_media_blksize) <= bp2->b_bufsize);
144           bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize;
145           bp2->b_bio1.bio_done = biodone_sync;
146           bp2->b_bio1.bio_flags |= BIO_SYNC;
147           bp2->b_bcount = table_blocks * info->d_media_blksize;
148           bp2->b_cmd = BUF_CMD_READ;
149           bp2->b_flags |= B_FAILONDIS;
150           dev_dstrategy(wdev, &bp2->b_bio1);
151           if (biowait(&bp2->b_bio1, "gptrd") != 0) {
152                     kprintf("%s: reading GPT partition table @ %lld: error %d\n",
153                               dname,
154                               (long long)bp2->b_bio1.bio_offset,
155                               bp2->b_error);
156                     error = EIO;
157                     goto done;
158           }
159 
160           /*
161            * We are passed a pointer to a minimal slices struct.  Replace
162            * it with a maximal one (128 slices + special slices).  Well,
163            * really there is only one special slice (the WHOLE_DISK_SLICE)
164            * since we use the compatibility slice for s0, but don't quibble.
165            *
166            */
167           kfree(*sspp, M_DEVBUF);
168           ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info);
169 
170           /*
171            * Create a slice for each partition.
172            */
173           for (i = 0; i < (int)entries && i < 128; ++i) {
174                     struct gpt_ent sent;
175                     char partname[2];
176                     char *sname;
177 
178                     ent = (void *)((char *)bp2->b_data + i * entsz);
179                     le_uuid_dec(&ent->ent_type, &sent.ent_type);
180                     le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid);
181                     sent.ent_lba_start = le64toh(ent->ent_lba_start);
182                     sent.ent_lba_end = le64toh(ent->ent_lba_end);
183                     sent.ent_attr = le64toh(ent->ent_attr);
184 
185                     for (j = 0; j < NELEM(ent->ent_name); ++j)
186                               sent.ent_name[j] = le16toh(ent->ent_name[j]);
187 
188                     /*
189                      * The COMPATIBILITY_SLICE is actually slice 0 (s0).  This
190                      * is a bit weird becaue the whole-disk slice is #1, so
191                      * slice 1 (s1) starts at BASE_SLICE.
192                      */
193                     if (i == 0)
194                               sp = &ssp->dss_slices[COMPATIBILITY_SLICE];
195                     else
196                               sp = &ssp->dss_slices[BASE_SLICE+i-1];
197                     sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE,
198                                      WHOLE_SLICE_PART, partname);
199 
200                     if (kuuid_is_nil(&sent.ent_type))
201                               continue;
202 
203                     if (sent.ent_lba_start < table_lba + table_blocks ||
204                         sent.ent_lba_end >= info->d_media_blocks ||
205                         sent.ent_lba_start >= sent.ent_lba_end) {
206                               kprintf("%s part %d: unavailable, bad start or "
207                                         "ending lba\n",
208                                         sname, i);
209                     } else {
210                               gpt_setslice(sname, info, sp, &sent);
211                     }
212           }
213           ssp->dss_nslices = BASE_SLICE + i;
214 
215           error = 0;
216 done:
217           if (bp1) {
218                     bp1->b_flags |= B_INVAL | B_AGE;
219                     relpbuf(bp1, NULL);
220           }
221           if (bp2) {
222                     bp2->b_flags |= B_INVAL | B_AGE;
223                     relpbuf(bp2, NULL);
224           }
225           if (error == EINVAL)
226                     error = 0;
227           return (error);
228 }
229 
230 static
231 void
gpt_setslice(const char * sname,struct disk_info * info,struct diskslice * sp,struct gpt_ent * sent)232 gpt_setslice(const char *sname, struct disk_info *info, struct diskslice *sp,
233                struct gpt_ent *sent)
234 {
235           sp->ds_offset = sent->ent_lba_start;
236           sp->ds_size   = sent->ent_lba_end + 1 - sent->ent_lba_start;
237           sp->ds_type   = 1;  /* XXX */
238           sp->ds_type_uuid = sent->ent_type;
239           sp->ds_stor_uuid = sent->ent_uuid;
240           sp->ds_reserved = 0;          /* no reserved sectors */
241 }
242 
243