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
34 * $DragonFly: src/sys/kern/subr_diskgpt.c,v 1.4 2007/07/20 17:21:51 dillon Exp $
37 #include <sys/param.h>
38 #include <sys/systm.h>
40 #include <sys/endian.h>
41 #include <sys/diskslice.h>
42 #include <sys/diskmbr.h>
45 #include <sys/malloc.h>
46 #include <sys/syslog.h>
48 #include <sys/device.h>
51 #define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
53 static void gpt_setslice(const char *sname
, struct disk_info
*info
,
54 struct diskslice
*sp
, struct gpt_ent
*sent
);
57 * Handle GPT on raw disk. Note that GPTs are not recursive. The MBR is
58 * ignored once a GPT has been detected.
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.
64 * This routine is called from mbrinit() when a GPT has been detected.
67 gptinit(cdev_t dev
, struct disk_info
*info
, struct diskslices
**sspp
)
69 struct buf
*bp1
= NULL
;
70 struct buf
*bp2
= NULL
;
74 struct diskslices
*ssp
;
82 uint32_t table_blocks
;
87 * The GPT starts in sector 1.
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",
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
= geteblk((int)(table_blocks
* info
->d_media_blksize
));
143 bp2
->b_bio1
.bio_offset
= (off_t
)table_lba
* info
->d_media_blksize
;
144 bp2
->b_bcount
= table_blocks
* info
->d_media_blksize
;
145 bp2
->b_cmd
= BUF_CMD_READ
;
146 dev_dstrategy(wdev
, &bp2
->b_bio1
);
147 if (biowait(bp2
) != 0) {
148 kprintf("%s: reading GPT partition table @ %lld: error %d\n",
149 dname
, bp2
->b_bio1
.bio_offset
, bp2
->b_error
);
155 * We are passed a pointer to a minimal slices struct. Replace
156 * it with a maximal one (128 slices + special slices). Well,
157 * really there is only one special slice (the WHOLE_DISK_SLICE)
158 * since we use the compatibility slice for s0, but don't quibble.
161 kfree(*sspp
, M_DEVBUF
);
162 ssp
= *sspp
= dsmakeslicestruct(BASE_SLICE
+128, info
);
165 * Create a slice for each partition.
167 for (i
= 0; i
< (int)entries
&& i
< 128; ++i
) {
172 ent
= (void *)((char *)bp2
->b_data
+ i
* entsz
);
173 le_uuid_dec(&ent
->ent_type
, &sent
.ent_type
);
174 le_uuid_dec(&ent
->ent_uuid
, &sent
.ent_uuid
);
175 sent
.ent_lba_start
= le64toh(ent
->ent_lba_start
);
176 sent
.ent_lba_end
= le64toh(ent
->ent_lba_end
);
177 sent
.ent_attr
= le64toh(ent
->ent_attr
);
179 for (j
= 0; j
< arysize(ent
->ent_name
); ++j
)
180 sent
.ent_name
[j
] = le16toh(ent
->ent_name
[j
]);
183 * The COMPATIBILITY_SLICE is actually slice 0 (s0). This
184 * is a bit weird becaue the whole-disk slice is #1, so
185 * slice 1 (s1) starts at BASE_SLICE.
188 sp
= &ssp
->dss_slices
[COMPATIBILITY_SLICE
];
190 sp
= &ssp
->dss_slices
[BASE_SLICE
+i
-1];
191 sname
= dsname(dev
, dkunit(dev
), WHOLE_DISK_SLICE
,
192 WHOLE_SLICE_PART
, partname
);
194 if (kuuid_is_nil(&sent
.ent_type
))
197 if (sent
.ent_lba_start
< table_lba
+ table_blocks
||
198 sent
.ent_lba_end
>= info
->d_media_blocks
||
199 sent
.ent_lba_start
>= sent
.ent_lba_end
) {
200 kprintf("%s part %d: unavailable, bad start or "
204 gpt_setslice(sname
, info
, sp
, &sent
);
207 ssp
->dss_nslices
= BASE_SLICE
+ i
;
212 bp1
->b_flags
|= B_INVAL
| B_AGE
;
216 bp2
->b_flags
|= B_INVAL
| B_AGE
;
226 gpt_setslice(const char *sname
, struct disk_info
*info
, struct diskslice
*sp
,
227 struct gpt_ent
*sent
)
229 sp
->ds_offset
= sent
->ent_lba_start
;
230 sp
->ds_size
= sent
->ent_lba_end
+ 1 - sent
->ent_lba_start
;
231 sp
->ds_type
= 1; /* XXX */
232 sp
->ds_type_uuid
= sent
->ent_type
;
233 sp
->ds_stor_uuid
= sent
->ent_uuid
;
234 sp
->ds_reserved
= 0; /* no reserved sectors */