xref: /dragonfly/sys/kern/subr_diskgpt.c (revision bf20632c)
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