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