139a86b92SMatthew Dillon /*
239a86b92SMatthew Dillon * Copyright (c) 2007 The DragonFly Project. All rights reserved.
339a86b92SMatthew Dillon *
439a86b92SMatthew Dillon * This code is derived from software contributed to The DragonFly Project
539a86b92SMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
639a86b92SMatthew Dillon *
739a86b92SMatthew Dillon * Redistribution and use in source and binary forms, with or without
839a86b92SMatthew Dillon * modification, are permitted provided that the following conditions
939a86b92SMatthew Dillon * are met:
1039a86b92SMatthew Dillon *
1139a86b92SMatthew Dillon * 1. Redistributions of source code must retain the above copyright
1239a86b92SMatthew Dillon * notice, this list of conditions and the following disclaimer.
1339a86b92SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
1439a86b92SMatthew Dillon * notice, this list of conditions and the following disclaimer in
1539a86b92SMatthew Dillon * the documentation and/or other materials provided with the
1639a86b92SMatthew Dillon * distribution.
1739a86b92SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
1839a86b92SMatthew Dillon * contributors may be used to endorse or promote products derived
1939a86b92SMatthew Dillon * from this software without specific, prior written permission.
2039a86b92SMatthew Dillon *
2139a86b92SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2239a86b92SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2339a86b92SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2439a86b92SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2539a86b92SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2639a86b92SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2739a86b92SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2839a86b92SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2939a86b92SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3039a86b92SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3139a86b92SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3239a86b92SMatthew Dillon * SUCH DAMAGE.
3339a86b92SMatthew Dillon */
3439a86b92SMatthew Dillon
3539a86b92SMatthew Dillon #include <sys/param.h>
3639a86b92SMatthew Dillon #include <sys/systm.h>
3739a86b92SMatthew Dillon #include <sys/conf.h>
3839a86b92SMatthew Dillon #include <sys/endian.h>
3939a86b92SMatthew Dillon #include <sys/diskslice.h>
4039a86b92SMatthew Dillon #include <sys/diskmbr.h>
4139a86b92SMatthew Dillon #include <sys/disk.h>
4239a86b92SMatthew Dillon #include <sys/buf.h>
4339a86b92SMatthew Dillon #include <sys/malloc.h>
4439a86b92SMatthew Dillon #include <sys/syslog.h>
4539a86b92SMatthew Dillon #include <sys/bus.h>
4639a86b92SMatthew Dillon #include <sys/device.h>
4739a86b92SMatthew Dillon #include <sys/gpt.h>
4839a86b92SMatthew Dillon
4939a86b92SMatthew Dillon static void gpt_setslice(const char *sname, struct disk_info *info,
5039a86b92SMatthew Dillon struct diskslice *sp, struct gpt_ent *sent);
5139a86b92SMatthew Dillon
5239a86b92SMatthew Dillon /*
5339a86b92SMatthew Dillon * Handle GPT on raw disk. Note that GPTs are not recursive. The MBR is
5439a86b92SMatthew Dillon * ignored once a GPT has been detected.
5539a86b92SMatthew Dillon *
5639a86b92SMatthew Dillon * GPTs always start at block #1, regardless of how the MBR has been set up.
5739a86b92SMatthew Dillon * In fact, the MBR's starting block might be pointing to the boot partition
5839a86b92SMatthew Dillon * in the GPT rather then to the start of the GPT.
5939a86b92SMatthew Dillon *
6039a86b92SMatthew Dillon * This routine is called from mbrinit() when a GPT has been detected.
6139a86b92SMatthew Dillon */
6239a86b92SMatthew Dillon int
gptinit(cdev_t dev,struct disk_info * info,struct diskslices ** sspp)6339a86b92SMatthew Dillon gptinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp)
6439a86b92SMatthew Dillon {
6539a86b92SMatthew Dillon struct buf *bp1 = NULL;
6639a86b92SMatthew Dillon struct buf *bp2 = NULL;
6739a86b92SMatthew Dillon struct gpt_hdr *gpt;
6839a86b92SMatthew Dillon struct gpt_ent *ent;
6939a86b92SMatthew Dillon struct diskslice *sp;
7039a86b92SMatthew Dillon struct diskslices *ssp;
7139a86b92SMatthew Dillon cdev_t wdev;
7239a86b92SMatthew Dillon int error;
7339a86b92SMatthew Dillon uint32_t len;
7439a86b92SMatthew Dillon uint32_t entries;
7539a86b92SMatthew Dillon uint32_t entsz;
7639a86b92SMatthew Dillon uint32_t crc;
7739a86b92SMatthew Dillon uint32_t table_lba;
7839a86b92SMatthew Dillon uint32_t table_blocks;
7939a86b92SMatthew Dillon int i = 0, j;
8039a86b92SMatthew Dillon const char *dname;
8139a86b92SMatthew Dillon
8239a86b92SMatthew Dillon /*
8339a86b92SMatthew Dillon * The GPT starts in sector 1.
8439a86b92SMatthew Dillon */
85cd29885aSMatthew Dillon wdev = dev;
8639a86b92SMatthew Dillon dname = dev_dname(wdev);
87*bf20632cSMatthew Dillon bp1 = getpbuf_mem(NULL);
88*bf20632cSMatthew Dillon KKASSERT(info->d_media_blksize <= bp1->b_bufsize);
8939a86b92SMatthew Dillon bp1->b_bio1.bio_offset = info->d_media_blksize;
90ae8e83e6SMatthew Dillon bp1->b_bio1.bio_done = biodone_sync;
91ae8e83e6SMatthew Dillon bp1->b_bio1.bio_flags |= BIO_SYNC;
9239a86b92SMatthew Dillon bp1->b_bcount = info->d_media_blksize;
9339a86b92SMatthew Dillon bp1->b_cmd = BUF_CMD_READ;
94a06d536bSMatthew Dillon bp1->b_flags |= B_FAILONDIS;
9539a86b92SMatthew Dillon dev_dstrategy(wdev, &bp1->b_bio1);
96ae8e83e6SMatthew Dillon if (biowait(&bp1->b_bio1, "gptrd") != 0) {
9739a86b92SMatthew Dillon kprintf("%s: reading GPT @ block 1: error %d\n",
9839a86b92SMatthew Dillon dname, bp1->b_error);
9939a86b92SMatthew Dillon error = EIO;
10039a86b92SMatthew Dillon goto done;
10139a86b92SMatthew Dillon }
10239a86b92SMatthew Dillon
10339a86b92SMatthew Dillon /*
10439a86b92SMatthew Dillon * Header sanity check
10539a86b92SMatthew Dillon */
10639a86b92SMatthew Dillon gpt = (void *)bp1->b_data;
10739a86b92SMatthew Dillon len = le32toh(gpt->hdr_size);
10839a86b92SMatthew Dillon if (len < GPT_MIN_HDR_SIZE || len > info->d_media_blksize) {
10939a86b92SMatthew Dillon kprintf("%s: Illegal GPT header size %d\n", dname, len);
11039a86b92SMatthew Dillon error = EINVAL;
11139a86b92SMatthew Dillon goto done;
11239a86b92SMatthew Dillon }
11339a86b92SMatthew Dillon
11439a86b92SMatthew Dillon crc = le32toh(gpt->hdr_crc_self);
11539a86b92SMatthew Dillon gpt->hdr_crc_self = 0;
11639a86b92SMatthew Dillon if (crc32(gpt, len) != crc) {
11739a86b92SMatthew Dillon kprintf("%s: GPT CRC32 did not match\n", dname);
11839a86b92SMatthew Dillon error = EINVAL;
11939a86b92SMatthew Dillon goto done;
12039a86b92SMatthew Dillon }
12139a86b92SMatthew Dillon
12239a86b92SMatthew Dillon /*
12339a86b92SMatthew Dillon * Validate the partition table and its location, then read it
12439a86b92SMatthew Dillon * into a buffer.
12539a86b92SMatthew Dillon */
12639a86b92SMatthew Dillon entries = le32toh(gpt->hdr_entries);
12739a86b92SMatthew Dillon entsz = le32toh(gpt->hdr_entsz);
12839a86b92SMatthew Dillon table_lba = le32toh(gpt->hdr_lba_table);
12939a86b92SMatthew Dillon table_blocks = (entries * entsz + info->d_media_blksize - 1) /
13039a86b92SMatthew Dillon info->d_media_blksize;
13139a86b92SMatthew Dillon if (entries < 1 || entries > 128 ||
13239a86b92SMatthew Dillon entsz < 128 || (entsz & 7) || entsz > MAXBSIZE / entries ||
13339a86b92SMatthew Dillon table_lba < 2 || table_lba + table_blocks > info->d_media_blocks) {
13439a86b92SMatthew Dillon kprintf("%s: GPT partition table is out of bounds\n", dname);
13539a86b92SMatthew Dillon error = EINVAL;
13639a86b92SMatthew Dillon goto done;
13739a86b92SMatthew Dillon }
13839a86b92SMatthew Dillon
139c34665ceSMatthew Dillon /*
140c34665ceSMatthew Dillon * XXX subject to device dma size limitations
141c34665ceSMatthew Dillon */
142*bf20632cSMatthew Dillon bp2 = getpbuf_mem(NULL);
143*bf20632cSMatthew Dillon KKASSERT((int)(table_blocks * info->d_media_blksize) <= bp2->b_bufsize);
14439a86b92SMatthew Dillon bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize;
145ae8e83e6SMatthew Dillon bp2->b_bio1.bio_done = biodone_sync;
146ae8e83e6SMatthew Dillon bp2->b_bio1.bio_flags |= BIO_SYNC;
14739a86b92SMatthew Dillon bp2->b_bcount = table_blocks * info->d_media_blksize;
14839a86b92SMatthew Dillon bp2->b_cmd = BUF_CMD_READ;
149a06d536bSMatthew Dillon bp2->b_flags |= B_FAILONDIS;
15039a86b92SMatthew Dillon dev_dstrategy(wdev, &bp2->b_bio1);
151ae8e83e6SMatthew Dillon if (biowait(&bp2->b_bio1, "gptrd") != 0) {
15239a86b92SMatthew Dillon kprintf("%s: reading GPT partition table @ %lld: error %d\n",
153973c11b9SMatthew Dillon dname,
154973c11b9SMatthew Dillon (long long)bp2->b_bio1.bio_offset,
155973c11b9SMatthew Dillon bp2->b_error);
15639a86b92SMatthew Dillon error = EIO;
15739a86b92SMatthew Dillon goto done;
15839a86b92SMatthew Dillon }
15939a86b92SMatthew Dillon
16039a86b92SMatthew Dillon /*
16139a86b92SMatthew Dillon * We are passed a pointer to a minimal slices struct. Replace
162ae81fc47SMatthew Dillon * it with a maximal one (128 slices + special slices). Well,
163ae81fc47SMatthew Dillon * really there is only one special slice (the WHOLE_DISK_SLICE)
164ae81fc47SMatthew Dillon * since we use the compatibility slice for s0, but don't quibble.
165ae81fc47SMatthew Dillon *
16639a86b92SMatthew Dillon */
16739a86b92SMatthew Dillon kfree(*sspp, M_DEVBUF);
168ae81fc47SMatthew Dillon ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info);
16939a86b92SMatthew Dillon
17039a86b92SMatthew Dillon /*
17139a86b92SMatthew Dillon * Create a slice for each partition.
17239a86b92SMatthew Dillon */
173ae81fc47SMatthew Dillon for (i = 0; i < (int)entries && i < 128; ++i) {
17439a86b92SMatthew Dillon struct gpt_ent sent;
17539a86b92SMatthew Dillon char partname[2];
17639a86b92SMatthew Dillon char *sname;
17739a86b92SMatthew Dillon
17839a86b92SMatthew Dillon ent = (void *)((char *)bp2->b_data + i * entsz);
17939a86b92SMatthew Dillon le_uuid_dec(&ent->ent_type, &sent.ent_type);
18039a86b92SMatthew Dillon le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid);
18139a86b92SMatthew Dillon sent.ent_lba_start = le64toh(ent->ent_lba_start);
18239a86b92SMatthew Dillon sent.ent_lba_end = le64toh(ent->ent_lba_end);
18339a86b92SMatthew Dillon sent.ent_attr = le64toh(ent->ent_attr);
18439a86b92SMatthew Dillon
185ae7ec37eSSascha Wildner for (j = 0; j < NELEM(ent->ent_name); ++j)
18639a86b92SMatthew Dillon sent.ent_name[j] = le16toh(ent->ent_name[j]);
18739a86b92SMatthew Dillon
18839a86b92SMatthew Dillon /*
18939a86b92SMatthew Dillon * The COMPATIBILITY_SLICE is actually slice 0 (s0). This
19039a86b92SMatthew Dillon * is a bit weird becaue the whole-disk slice is #1, so
19139a86b92SMatthew Dillon * slice 1 (s1) starts at BASE_SLICE.
19239a86b92SMatthew Dillon */
19339a86b92SMatthew Dillon if (i == 0)
19439a86b92SMatthew Dillon sp = &ssp->dss_slices[COMPATIBILITY_SLICE];
19539a86b92SMatthew Dillon else
196ae81fc47SMatthew Dillon sp = &ssp->dss_slices[BASE_SLICE+i-1];
19739a86b92SMatthew Dillon sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE,
19839a86b92SMatthew Dillon WHOLE_SLICE_PART, partname);
19939a86b92SMatthew Dillon
20039a86b92SMatthew Dillon if (kuuid_is_nil(&sent.ent_type))
20139a86b92SMatthew Dillon continue;
20239a86b92SMatthew Dillon
20339a86b92SMatthew Dillon if (sent.ent_lba_start < table_lba + table_blocks ||
204ae81fc47SMatthew Dillon sent.ent_lba_end >= info->d_media_blocks ||
205ae81fc47SMatthew Dillon sent.ent_lba_start >= sent.ent_lba_end) {
20639a86b92SMatthew Dillon kprintf("%s part %d: unavailable, bad start or "
20739a86b92SMatthew Dillon "ending lba\n",
20839a86b92SMatthew Dillon sname, i);
20939a86b92SMatthew Dillon } else {
21039a86b92SMatthew Dillon gpt_setslice(sname, info, sp, &sent);
21139a86b92SMatthew Dillon }
21239a86b92SMatthew Dillon }
21339a86b92SMatthew Dillon ssp->dss_nslices = BASE_SLICE + i;
21439a86b92SMatthew Dillon
21539a86b92SMatthew Dillon error = 0;
21639a86b92SMatthew Dillon done:
21739a86b92SMatthew Dillon if (bp1) {
21839a86b92SMatthew Dillon bp1->b_flags |= B_INVAL | B_AGE;
219*bf20632cSMatthew Dillon relpbuf(bp1, NULL);
22039a86b92SMatthew Dillon }
22139a86b92SMatthew Dillon if (bp2) {
22239a86b92SMatthew Dillon bp2->b_flags |= B_INVAL | B_AGE;
223*bf20632cSMatthew Dillon relpbuf(bp2, NULL);
22439a86b92SMatthew Dillon }
22539a86b92SMatthew Dillon if (error == EINVAL)
22639a86b92SMatthew Dillon error = 0;
22739a86b92SMatthew Dillon return (error);
22839a86b92SMatthew Dillon }
22939a86b92SMatthew Dillon
23039a86b92SMatthew Dillon static
23139a86b92SMatthew Dillon void
gpt_setslice(const char * sname,struct disk_info * info,struct diskslice * sp,struct gpt_ent * sent)23239a86b92SMatthew Dillon gpt_setslice(const char *sname, struct disk_info *info, struct diskslice *sp,
23339a86b92SMatthew Dillon struct gpt_ent *sent)
23439a86b92SMatthew Dillon {
23539a86b92SMatthew Dillon sp->ds_offset = sent->ent_lba_start;
23639a86b92SMatthew Dillon sp->ds_size = sent->ent_lba_end + 1 - sent->ent_lba_start;
23739a86b92SMatthew Dillon sp->ds_type = 1; /* XXX */
23818cb7addSMatthew Dillon sp->ds_type_uuid = sent->ent_type;
23918cb7addSMatthew Dillon sp->ds_stor_uuid = sent->ent_uuid;
24039a86b92SMatthew Dillon sp->ds_reserved = 0; /* no reserved sectors */
24139a86b92SMatthew Dillon }
24239a86b92SMatthew Dillon
243