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.3 2007/06/19 06:07:57 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 bp2 = geteblk((int)(table_blocks * info->d_media_blksize)); 140 bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize; 141 bp2->b_bcount = table_blocks * info->d_media_blksize; 142 bp2->b_cmd = BUF_CMD_READ; 143 dev_dstrategy(wdev, &bp2->b_bio1); 144 if (biowait(bp2) != 0) { 145 kprintf("%s: reading GPT partition table @ %lld: error %d\n", 146 dname, bp2->b_bio1.bio_offset, bp2->b_error); 147 error = EIO; 148 goto done; 149 } 150 151 /* 152 * We are passed a pointer to a minimal slices struct. Replace 153 * it with a maximal one (128 slices + special slices). Well, 154 * really there is only one special slice (the WHOLE_DISK_SLICE) 155 * since we use the compatibility slice for s0, but don't quibble. 156 * 157 */ 158 kfree(*sspp, M_DEVBUF); 159 ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info); 160 161 /* 162 * Create a slice for each partition. 163 */ 164 for (i = 0; i < (int)entries && i < 128; ++i) { 165 struct gpt_ent sent; 166 char partname[2]; 167 char *sname; 168 169 ent = (void *)((char *)bp2->b_data + i * entsz); 170 le_uuid_dec(&ent->ent_type, &sent.ent_type); 171 le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid); 172 sent.ent_lba_start = le64toh(ent->ent_lba_start); 173 sent.ent_lba_end = le64toh(ent->ent_lba_end); 174 sent.ent_attr = le64toh(ent->ent_attr); 175 176 for (j = 0; j < arysize(ent->ent_name); ++j) 177 sent.ent_name[j] = le16toh(ent->ent_name[j]); 178 179 /* 180 * The COMPATIBILITY_SLICE is actually slice 0 (s0). This 181 * is a bit weird becaue the whole-disk slice is #1, so 182 * slice 1 (s1) starts at BASE_SLICE. 183 */ 184 if (i == 0) 185 sp = &ssp->dss_slices[COMPATIBILITY_SLICE]; 186 else 187 sp = &ssp->dss_slices[BASE_SLICE+i-1]; 188 sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE, 189 WHOLE_SLICE_PART, partname); 190 191 if (kuuid_is_nil(&sent.ent_type)) 192 continue; 193 194 if (sent.ent_lba_start < table_lba + table_blocks || 195 sent.ent_lba_end >= info->d_media_blocks || 196 sent.ent_lba_start >= sent.ent_lba_end) { 197 kprintf("%s part %d: unavailable, bad start or " 198 "ending lba\n", 199 sname, i); 200 } else { 201 gpt_setslice(sname, info, sp, &sent); 202 } 203 } 204 ssp->dss_nslices = BASE_SLICE + i; 205 206 error = 0; 207 done: 208 if (bp1) { 209 bp1->b_flags |= B_INVAL | B_AGE; 210 brelse(bp1); 211 } 212 if (bp2) { 213 bp2->b_flags |= B_INVAL | B_AGE; 214 brelse(bp2); 215 } 216 if (error == EINVAL) 217 error = 0; 218 return (error); 219 } 220 221 static 222 void 223 gpt_setslice(const char *sname, struct disk_info *info, struct diskslice *sp, 224 struct gpt_ent *sent) 225 { 226 sp->ds_offset = sent->ent_lba_start; 227 sp->ds_size = sent->ent_lba_end + 1 - sent->ent_lba_start; 228 sp->ds_type = 1; /* XXX */ 229 sp->ds_type_uuid = sent->ent_type; 230 sp->ds_stor_uuid = sent->ent_uuid; 231 sp->ds_reserved = 0; /* no reserved sectors */ 232 } 233 234