2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/endian.h>
39 #include <sys/diskslice.h>
40 #include <sys/diskmbr.h>
43 #include <sys/malloc.h>
44 #include <sys/syslog.h>
46 #include <sys/device.h>
49 static void gpt_setslice(const char *sname
, struct disk_info
*info
,
50 struct diskslice
*sp
, struct gpt_ent
*sent
);
53 * Handle GPT on raw disk. Note that GPTs are not recursive. The MBR is
54 * ignored once a GPT has been detected.
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.
60 * This routine is called from mbrinit() when a GPT has been detected.
63 gptinit(cdev_t dev
, struct disk_info
*info
, struct diskslices
**sspp
)
65 struct buf
*bp1
= NULL
;
66 struct buf
*bp2
= NULL
;
70 struct diskslices
*ssp
;
78 uint32_t table_blocks
;
83 * The GPT starts in sector 1.
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",
104 * Header sanity check
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
);
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
);
123 * Validate the partition table and its location, then read it
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
);
140 * XXX subject to device dma size limitations
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",
154 (long long)bp2
->b_bio1
.bio_offset
,
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.
167 kfree(*sspp
, M_DEVBUF
);
168 ssp
= *sspp
= dsmakeslicestruct(BASE_SLICE
+128, info
);
171 * Create a slice for each partition.
173 for (i
= 0; i
< (int)entries
&& i
< 128; ++i
) {
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
);
185 for (j
= 0; j
< NELEM(ent
->ent_name
); ++j
)
186 sent
.ent_name
[j
] = le16toh(ent
->ent_name
[j
]);
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.
194 sp
= &ssp
->dss_slices
[COMPATIBILITY_SLICE
];
196 sp
= &ssp
->dss_slices
[BASE_SLICE
+i
-1];
197 sname
= dsname(dev
, dkunit(dev
), WHOLE_DISK_SLICE
,
198 WHOLE_SLICE_PART
, partname
);
200 if (kuuid_is_nil(&sent
.ent_type
))
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 "
210 gpt_setslice(sname
, info
, sp
, &sent
);
213 ssp
->dss_nslices
= BASE_SLICE
+ i
;
218 bp1
->b_flags
|= B_INVAL
| B_AGE
;
222 bp2
->b_flags
|= B_INVAL
| B_AGE
;
232 gpt_setslice(const char *sname
, struct disk_info
*info
, struct diskslice
*sp
,
233 struct gpt_ent
*sent
)
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 */