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 dev_dstrategy(wdev, &bp1->b_bio1); 94 if (biowait(&bp1->b_bio1, "gptrd") != 0) { 95 kprintf("%s: reading GPT @ block 1: error %d\n", 96 dname, bp1->b_error); 97 error = EIO; 98 goto done; 99 } 100 101 /* 102 * Header sanity check 103 */ 104 gpt = (void *)bp1->b_data; 105 len = le32toh(gpt->hdr_size); 106 if (len < GPT_MIN_HDR_SIZE || len > info->d_media_blksize) { 107 kprintf("%s: Illegal GPT header size %d\n", dname, len); 108 error = EINVAL; 109 goto done; 110 } 111 112 crc = le32toh(gpt->hdr_crc_self); 113 gpt->hdr_crc_self = 0; 114 if (crc32(gpt, len) != crc) { 115 kprintf("%s: GPT CRC32 did not match\n", dname); 116 error = EINVAL; 117 goto done; 118 } 119 120 /* 121 * Validate the partition table and its location, then read it 122 * into a buffer. 123 */ 124 entries = le32toh(gpt->hdr_entries); 125 entsz = le32toh(gpt->hdr_entsz); 126 table_lba = le32toh(gpt->hdr_lba_table); 127 table_blocks = (entries * entsz + info->d_media_blksize - 1) / 128 info->d_media_blksize; 129 if (entries < 1 || entries > 128 || 130 entsz < 128 || (entsz & 7) || entsz > MAXBSIZE / entries || 131 table_lba < 2 || table_lba + table_blocks > info->d_media_blocks) { 132 kprintf("%s: GPT partition table is out of bounds\n", dname); 133 error = EINVAL; 134 goto done; 135 } 136 137 /* 138 * XXX subject to device dma size limitations 139 */ 140 bp2 = geteblk((int)(table_blocks * info->d_media_blksize)); 141 bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize; 142 bp2->b_bio1.bio_done = biodone_sync; 143 bp2->b_bio1.bio_flags |= BIO_SYNC; 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->b_bio1, "gptrd") != 0) { 148 kprintf("%s: reading GPT partition table @ %lld: error %d\n", 149 dname, 150 (long long)bp2->b_bio1.bio_offset, 151 bp2->b_error); 152 error = EIO; 153 goto done; 154 } 155 156 /* 157 * We are passed a pointer to a minimal slices struct. Replace 158 * it with a maximal one (128 slices + special slices). Well, 159 * really there is only one special slice (the WHOLE_DISK_SLICE) 160 * since we use the compatibility slice for s0, but don't quibble. 161 * 162 */ 163 kfree(*sspp, M_DEVBUF); 164 ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info); 165 166 /* 167 * Create a slice for each partition. 168 */ 169 for (i = 0; i < (int)entries && i < 128; ++i) { 170 struct gpt_ent sent; 171 char partname[2]; 172 char *sname; 173 174 ent = (void *)((char *)bp2->b_data + i * entsz); 175 le_uuid_dec(&ent->ent_type, &sent.ent_type); 176 le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid); 177 sent.ent_lba_start = le64toh(ent->ent_lba_start); 178 sent.ent_lba_end = le64toh(ent->ent_lba_end); 179 sent.ent_attr = le64toh(ent->ent_attr); 180 181 for (j = 0; j < NELEM(ent->ent_name); ++j) 182 sent.ent_name[j] = le16toh(ent->ent_name[j]); 183 184 /* 185 * The COMPATIBILITY_SLICE is actually slice 0 (s0). This 186 * is a bit weird becaue the whole-disk slice is #1, so 187 * slice 1 (s1) starts at BASE_SLICE. 188 */ 189 if (i == 0) 190 sp = &ssp->dss_slices[COMPATIBILITY_SLICE]; 191 else 192 sp = &ssp->dss_slices[BASE_SLICE+i-1]; 193 sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE, 194 WHOLE_SLICE_PART, partname); 195 196 if (kuuid_is_nil(&sent.ent_type)) 197 continue; 198 199 if (sent.ent_lba_start < table_lba + table_blocks || 200 sent.ent_lba_end >= info->d_media_blocks || 201 sent.ent_lba_start >= sent.ent_lba_end) { 202 kprintf("%s part %d: unavailable, bad start or " 203 "ending lba\n", 204 sname, i); 205 } else { 206 gpt_setslice(sname, info, sp, &sent); 207 } 208 } 209 ssp->dss_nslices = BASE_SLICE + i; 210 211 error = 0; 212 done: 213 if (bp1) { 214 bp1->b_flags |= B_INVAL | B_AGE; 215 brelse(bp1); 216 } 217 if (bp2) { 218 bp2->b_flags |= B_INVAL | B_AGE; 219 brelse(bp2); 220 } 221 if (error == EINVAL) 222 error = 0; 223 return (error); 224 } 225 226 static 227 void 228 gpt_setslice(const char *sname, struct disk_info *info, struct diskslice *sp, 229 struct gpt_ent *sent) 230 { 231 sp->ds_offset = sent->ent_lba_start; 232 sp->ds_size = sent->ent_lba_end + 1 - sent->ent_lba_start; 233 sp->ds_type = 1; /* XXX */ 234 sp->ds_type_uuid = sent->ent_type; 235 sp->ds_stor_uuid = sent->ent_uuid; 236 sp->ds_reserved = 0; /* no reserved sectors */ 237 } 238 239