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 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/conf.h> 38 #include <sys/endian.h> 39 #include <sys/diskslice.h> 40 #include <sys/diskmbr.h> 41 #include <sys/disk.h> 42 #include <sys/buf.h> 43 #include <sys/malloc.h> 44 #include <sys/syslog.h> 45 #include <sys/bus.h> 46 #include <sys/device.h> 47 #include <sys/gpt.h> 48 49 static void gpt_setslice(const char *sname, struct disk_info *info, 50 struct diskslice *sp, struct gpt_ent *sent); 51 52 /* 53 * Handle GPT on raw disk. Note that GPTs are not recursive. The MBR is 54 * ignored once a GPT has been detected. 55 * 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. 59 * 60 * This routine is called from mbrinit() when a GPT has been detected. 61 */ 62 int 63 gptinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp) 64 { 65 struct buf *bp1 = NULL; 66 struct buf *bp2 = NULL; 67 struct gpt_hdr *gpt; 68 struct gpt_ent *ent; 69 struct diskslice *sp; 70 struct diskslices *ssp; 71 cdev_t wdev; 72 int error; 73 uint32_t len; 74 uint32_t entries; 75 uint32_t entsz; 76 uint32_t crc; 77 uint32_t table_lba; 78 uint32_t table_blocks; 79 int i = 0, j; 80 const char *dname; 81 82 /* 83 * The GPT starts in sector 1. 84 */ 85 wdev = dev; 86 dname = dev_dname(wdev); 87 bp1 = geteblk((int)info->d_media_blksize); 88 bp1->b_bio1.bio_offset = info->d_media_blksize; 89 bp1->b_bio1.bio_done = biodone_sync; 90 bp1->b_bio1.bio_flags |= BIO_SYNC; 91 bp1->b_bcount = info->d_media_blksize; 92 bp1->b_cmd = BUF_CMD_READ; 93 bp1->b_flags |= B_FAILONDIS; 94 dev_dstrategy(wdev, &bp1->b_bio1); 95 if (biowait(&bp1->b_bio1, "gptrd") != 0) { 96 kprintf("%s: reading GPT @ block 1: error %d\n", 97 dname, bp1->b_error); 98 error = EIO; 99 goto done; 100 } 101 102 /* 103 * Header sanity check 104 */ 105 gpt = (void *)bp1->b_data; 106 len = le32toh(gpt->hdr_size); 107 if (len < GPT_MIN_HDR_SIZE || len > info->d_media_blksize) { 108 kprintf("%s: Illegal GPT header size %d\n", dname, len); 109 error = EINVAL; 110 goto done; 111 } 112 113 crc = le32toh(gpt->hdr_crc_self); 114 gpt->hdr_crc_self = 0; 115 if (crc32(gpt, len) != crc) { 116 kprintf("%s: GPT CRC32 did not match\n", dname); 117 error = EINVAL; 118 goto done; 119 } 120 121 /* 122 * Validate the partition table and its location, then read it 123 * into a buffer. 124 */ 125 entries = le32toh(gpt->hdr_entries); 126 entsz = le32toh(gpt->hdr_entsz); 127 table_lba = le32toh(gpt->hdr_lba_table); 128 table_blocks = (entries * entsz + info->d_media_blksize - 1) / 129 info->d_media_blksize; 130 if (entries < 1 || entries > 128 || 131 entsz < 128 || (entsz & 7) || entsz > MAXBSIZE / entries || 132 table_lba < 2 || table_lba + table_blocks > info->d_media_blocks) { 133 kprintf("%s: GPT partition table is out of bounds\n", dname); 134 error = EINVAL; 135 goto done; 136 } 137 138 /* 139 * XXX subject to device dma size limitations 140 */ 141 bp2 = geteblk((int)(table_blocks * info->d_media_blksize)); 142 bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize; 143 bp2->b_bio1.bio_done = biodone_sync; 144 bp2->b_bio1.bio_flags |= BIO_SYNC; 145 bp2->b_bcount = table_blocks * info->d_media_blksize; 146 bp2->b_cmd = BUF_CMD_READ; 147 bp2->b_flags |= B_FAILONDIS; 148 dev_dstrategy(wdev, &bp2->b_bio1); 149 if (biowait(&bp2->b_bio1, "gptrd") != 0) { 150 kprintf("%s: reading GPT partition table @ %lld: error %d\n", 151 dname, 152 (long long)bp2->b_bio1.bio_offset, 153 bp2->b_error); 154 error = EIO; 155 goto done; 156 } 157 158 /* 159 * We are passed a pointer to a minimal slices struct. Replace 160 * it with a maximal one (128 slices + special slices). Well, 161 * really there is only one special slice (the WHOLE_DISK_SLICE) 162 * since we use the compatibility slice for s0, but don't quibble. 163 * 164 */ 165 kfree(*sspp, M_DEVBUF); 166 ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info); 167 168 /* 169 * Create a slice for each partition. 170 */ 171 for (i = 0; i < (int)entries && i < 128; ++i) { 172 struct gpt_ent sent; 173 char partname[2]; 174 char *sname; 175 176 ent = (void *)((char *)bp2->b_data + i * entsz); 177 le_uuid_dec(&ent->ent_type, &sent.ent_type); 178 le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid); 179 sent.ent_lba_start = le64toh(ent->ent_lba_start); 180 sent.ent_lba_end = le64toh(ent->ent_lba_end); 181 sent.ent_attr = le64toh(ent->ent_attr); 182 183 for (j = 0; j < NELEM(ent->ent_name); ++j) 184 sent.ent_name[j] = le16toh(ent->ent_name[j]); 185 186 /* 187 * The COMPATIBILITY_SLICE is actually slice 0 (s0). This 188 * is a bit weird becaue the whole-disk slice is #1, so 189 * slice 1 (s1) starts at BASE_SLICE. 190 */ 191 if (i == 0) 192 sp = &ssp->dss_slices[COMPATIBILITY_SLICE]; 193 else 194 sp = &ssp->dss_slices[BASE_SLICE+i-1]; 195 sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE, 196 WHOLE_SLICE_PART, partname); 197 198 if (kuuid_is_nil(&sent.ent_type)) 199 continue; 200 201 if (sent.ent_lba_start < table_lba + table_blocks || 202 sent.ent_lba_end >= info->d_media_blocks || 203 sent.ent_lba_start >= sent.ent_lba_end) { 204 kprintf("%s part %d: unavailable, bad start or " 205 "ending lba\n", 206 sname, i); 207 } else { 208 gpt_setslice(sname, info, sp, &sent); 209 } 210 } 211 ssp->dss_nslices = BASE_SLICE + i; 212 213 error = 0; 214 done: 215 if (bp1) { 216 bp1->b_flags |= B_INVAL | B_AGE; 217 brelse(bp1); 218 } 219 if (bp2) { 220 bp2->b_flags |= B_INVAL | B_AGE; 221 brelse(bp2); 222 } 223 if (error == EINVAL) 224 error = 0; 225 return (error); 226 } 227 228 static 229 void 230 gpt_setslice(const char *sname, struct disk_info *info, struct diskslice *sp, 231 struct gpt_ent *sent) 232 { 233 sp->ds_offset = sent->ent_lba_start; 234 sp->ds_size = sent->ent_lba_end + 1 - sent->ent_lba_start; 235 sp->ds_type = 1; /* XXX */ 236 sp->ds_type_uuid = sent->ent_type; 237 sp->ds_stor_uuid = sent->ent_uuid; 238 sp->ds_reserved = 0; /* no reserved sectors */ 239 } 240 241