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 * $DragonFly: src/sys/kern/subr_diskgpt.c,v 1.4 2007/07/20 17:21:51 dillon Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/conf.h> 40 #include <sys/endian.h> 41 #include <sys/diskslice.h> 42 #include <sys/diskmbr.h> 43 #include <sys/disk.h> 44 #include <sys/buf.h> 45 #include <sys/malloc.h> 46 #include <sys/syslog.h> 47 #include <sys/bus.h> 48 #include <sys/device.h> 49 #include <sys/gpt.h> 50 51 #define arysize(ary) (sizeof(ary)/sizeof((ary)[0])) 52 53 static void gpt_setslice(const char *sname, struct disk_info *info, 54 struct diskslice *sp, struct gpt_ent *sent); 55 56 /* 57 * Handle GPT on raw disk. Note that GPTs are not recursive. The MBR is 58 * ignored once a GPT has been detected. 59 * 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. 63 * 64 * This routine is called from mbrinit() when a GPT has been detected. 65 */ 66 int 67 gptinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp) 68 { 69 struct buf *bp1 = NULL; 70 struct buf *bp2 = NULL; 71 struct gpt_hdr *gpt; 72 struct gpt_ent *ent; 73 struct diskslice *sp; 74 struct diskslices *ssp; 75 cdev_t wdev; 76 int error; 77 uint32_t len; 78 uint32_t entries; 79 uint32_t entsz; 80 uint32_t crc; 81 uint32_t table_lba; 82 uint32_t table_blocks; 83 int i = 0, j; 84 const char *dname; 85 86 /* 87 * The GPT starts in sector 1. 88 */ 89 wdev = dev; 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_bio1.bio_done = biodone_sync; 94 bp1->b_bio1.bio_flags |= BIO_SYNC; 95 bp1->b_bcount = info->d_media_blksize; 96 bp1->b_cmd = BUF_CMD_READ; 97 dev_dstrategy(wdev, &bp1->b_bio1); 98 if (biowait(&bp1->b_bio1, "gptrd") != 0) { 99 kprintf("%s: reading GPT @ block 1: error %d\n", 100 dname, bp1->b_error); 101 error = EIO; 102 goto done; 103 } 104 105 /* 106 * Header sanity check 107 */ 108 gpt = (void *)bp1->b_data; 109 len = le32toh(gpt->hdr_size); 110 if (len < GPT_MIN_HDR_SIZE || len > info->d_media_blksize) { 111 kprintf("%s: Illegal GPT header size %d\n", dname, len); 112 error = EINVAL; 113 goto done; 114 } 115 116 crc = le32toh(gpt->hdr_crc_self); 117 gpt->hdr_crc_self = 0; 118 if (crc32(gpt, len) != crc) { 119 kprintf("%s: GPT CRC32 did not match\n", dname); 120 error = EINVAL; 121 goto done; 122 } 123 124 /* 125 * Validate the partition table and its location, then read it 126 * into a buffer. 127 */ 128 entries = le32toh(gpt->hdr_entries); 129 entsz = le32toh(gpt->hdr_entsz); 130 table_lba = le32toh(gpt->hdr_lba_table); 131 table_blocks = (entries * entsz + info->d_media_blksize - 1) / 132 info->d_media_blksize; 133 if (entries < 1 || entries > 128 || 134 entsz < 128 || (entsz & 7) || entsz > MAXBSIZE / entries || 135 table_lba < 2 || table_lba + table_blocks > info->d_media_blocks) { 136 kprintf("%s: GPT partition table is out of bounds\n", dname); 137 error = EINVAL; 138 goto done; 139 } 140 141 /* 142 * XXX subject to device dma size limitations 143 */ 144 bp2 = geteblk((int)(table_blocks * info->d_media_blksize)); 145 bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize; 146 bp2->b_bio1.bio_done = biodone_sync; 147 bp2->b_bio1.bio_flags |= BIO_SYNC; 148 bp2->b_bcount = table_blocks * info->d_media_blksize; 149 bp2->b_cmd = BUF_CMD_READ; 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 < arysize(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 brelse(bp1); 220 } 221 if (bp2) { 222 bp2->b_flags |= B_INVAL | B_AGE; 223 brelse(bp2); 224 } 225 if (error == EINVAL) 226 error = 0; 227 return (error); 228 } 229 230 static 231 void 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