1 /* $OpenBSD: softraid_i386.c,v 1.2 2016/09/11 17:52:47 jsing Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Joel Sing <jsing@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/queue.h> 21 #include <sys/disklabel.h> 22 #include <sys/reboot.h> 23 24 #include <dev/biovar.h> 25 #include <dev/softraidvar.h> 26 27 #include <lib/libsa/aes_xts.h> 28 #include <lib/libsa/softraid.h> 29 30 #include "libsa.h" 31 #include "disk.h" 32 #include "softraid_i386.h" 33 34 void 35 srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som) 36 { 37 struct sr_meta_opt_hdr *omh; 38 struct sr_meta_opt_item *omi; 39 #if 0 40 u_int8_t checksum[MD5_DIGEST_LENGTH]; 41 #endif 42 int i; 43 44 /* Process optional metadata. */ 45 omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) + 46 sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no); 47 for (i = 0; i < sm->ssdi.ssd_opt_no; i++) { 48 49 #ifdef BIOS_DEBUG 50 printf("Found optional metadata of type %u, length %u\n", 51 omh->som_type, omh->som_length); 52 #endif 53 54 /* Unsupported old fixed length optional metadata. */ 55 if (omh->som_length == 0) { 56 omh = (struct sr_meta_opt_hdr *)((void *)omh + 57 SR_OLD_META_OPT_SIZE); 58 continue; 59 } 60 61 /* Load variable length optional metadata. */ 62 omi = alloc(sizeof(struct sr_meta_opt_item)); 63 bzero(omi, sizeof(struct sr_meta_opt_item)); 64 SLIST_INSERT_HEAD(som, omi, omi_link); 65 omi->omi_som = alloc(omh->som_length); 66 bzero(omi->omi_som, omh->som_length); 67 bcopy(omh, omi->omi_som, omh->som_length); 68 69 #if 0 70 /* XXX - Validate checksum. */ 71 bcopy(&omi->omi_som->som_checksum, &checksum, 72 MD5_DIGEST_LENGTH); 73 bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH); 74 sr_checksum(sc, omi->omi_som, 75 &omi->omi_som->som_checksum, omh->som_length); 76 if (bcmp(&checksum, &omi->omi_som->som_checksum, 77 sizeof(checksum))) 78 panic("%s: invalid optional metadata checksum", 79 DEVNAME(sc)); 80 #endif 81 82 omh = (struct sr_meta_opt_hdr *)((void *)omh + 83 omh->som_length); 84 } 85 } 86 87 void 88 srprobe_keydisk_load(struct sr_metadata *sm) 89 { 90 struct sr_meta_opt_hdr *omh; 91 struct sr_meta_keydisk *skm; 92 struct sr_boot_keydisk *kd; 93 int i; 94 95 /* Process optional metadata. */ 96 omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) + 97 sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no); 98 for (i = 0; i < sm->ssdi.ssd_opt_no; i++) { 99 100 /* Unsupported old fixed length optional metadata. */ 101 if (omh->som_length == 0) { 102 omh = (struct sr_meta_opt_hdr *)((void *)omh + 103 SR_OLD_META_OPT_SIZE); 104 continue; 105 } 106 107 if (omh->som_type != SR_OPT_KEYDISK) { 108 omh = (struct sr_meta_opt_hdr *)((void *)omh + 109 omh->som_length); 110 continue; 111 } 112 113 kd = alloc(sizeof(struct sr_boot_keydisk)); 114 bcopy(&sm->ssdi.ssd_uuid, &kd->kd_uuid, sizeof(kd->kd_uuid)); 115 skm = (struct sr_meta_keydisk*)omh; 116 bcopy(&skm->skm_maskkey, &kd->kd_key, sizeof(kd->kd_key)); 117 SLIST_INSERT_HEAD(&sr_keydisks, kd, kd_link); 118 } 119 } 120 121 void 122 srprobe(void) 123 { 124 struct sr_boot_volume *bv, *bv1, *bv2; 125 struct sr_boot_chunk *bc, *bc1, *bc2; 126 struct sr_meta_chunk *mc; 127 struct sr_metadata *md; 128 struct diskinfo *dip; 129 struct partition *pp; 130 int i, error, volno; 131 dev_t bsd_dev; 132 daddr_t off; 133 134 /* Probe for softraid volumes. */ 135 SLIST_INIT(&sr_volumes); 136 SLIST_INIT(&sr_keydisks); 137 138 md = alloc(SR_META_SIZE * DEV_BSIZE); 139 140 TAILQ_FOREACH(dip, &disklist, list) { 141 142 /* Only check hard disks, skip those with I/O errors. */ 143 if ((dip->bios_info.bios_number & 0x80) == 0 || 144 (dip->bios_info.flags & BDI_INVALID)) 145 continue; 146 147 /* Make sure disklabel has been read. */ 148 if ((dip->bios_info.flags & (BDI_BADLABEL|BDI_GOODLABEL)) == 0) 149 continue; 150 151 for (i = 0; i < MAXPARTITIONS; i++) { 152 153 pp = &dip->disklabel.d_partitions[i]; 154 if (pp->p_fstype != FS_RAID || pp->p_size == 0) 155 continue; 156 157 /* Read softraid metadata. */ 158 bzero(md, SR_META_SIZE * DEV_BSIZE); 159 off = DL_SECTOBLK(&dip->disklabel, DL_GETPOFFSET(pp)); 160 off += SR_META_OFFSET; 161 error = dip->diskio(F_READ, dip, off, SR_META_SIZE, md); 162 if (error) 163 continue; 164 165 /* Is this valid softraid metadata? */ 166 if (md->ssdi.ssd_magic != SR_MAGIC) 167 continue; 168 169 /* XXX - validate checksum. */ 170 171 /* Handle key disks separately... */ 172 if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) { 173 srprobe_keydisk_load(md); 174 continue; 175 } 176 177 /* Locate chunk-specific metadata for this chunk. */ 178 mc = (struct sr_meta_chunk *)(md + 1); 179 mc += md->ssdi.ssd_chunk_id; 180 181 bc = alloc(sizeof(struct sr_boot_chunk)); 182 bc->sbc_diskinfo = dip; 183 bc->sbc_disk = dip->bios_info.bios_number; 184 bc->sbc_part = 'a' + i; 185 186 bsd_dev = dip->bios_info.bsd_dev; 187 bc->sbc_mm = MAKEBOOTDEV(B_TYPE(bsd_dev), 188 B_ADAPTOR(bsd_dev), B_CONTROLLER(bsd_dev), 189 B_UNIT(bsd_dev), bc->sbc_part - 'a'); 190 191 bc->sbc_chunk_id = md->ssdi.ssd_chunk_id; 192 bc->sbc_ondisk = md->ssd_ondisk; 193 bc->sbc_state = mc->scm_status; 194 195 SLIST_FOREACH(bv, &sr_volumes, sbv_link) { 196 if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid, 197 sizeof(md->ssdi.ssd_uuid)) == 0) 198 break; 199 } 200 201 if (bv == NULL) { 202 bv = alloc(sizeof(struct sr_boot_volume)); 203 bzero(bv, sizeof(struct sr_boot_volume)); 204 bv->sbv_level = md->ssdi.ssd_level; 205 bv->sbv_volid = md->ssdi.ssd_volid; 206 bv->sbv_chunk_no = md->ssdi.ssd_chunk_no; 207 bv->sbv_flags = md->ssdi.ssd_vol_flags; 208 bv->sbv_size = md->ssdi.ssd_size; 209 bv->sbv_data_blkno = md->ssd_data_blkno; 210 bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid, 211 sizeof(md->ssdi.ssd_uuid)); 212 SLIST_INIT(&bv->sbv_chunks); 213 SLIST_INIT(&bv->sbv_meta_opt); 214 215 /* Load optional metadata for this volume. */ 216 srprobe_meta_opt_load(md, &bv->sbv_meta_opt); 217 218 /* Maintain volume order. */ 219 bv2 = NULL; 220 SLIST_FOREACH(bv1, &sr_volumes, sbv_link) { 221 if (bv1->sbv_volid > bv->sbv_volid) 222 break; 223 bv2 = bv1; 224 } 225 if (bv2 == NULL) 226 SLIST_INSERT_HEAD(&sr_volumes, bv, 227 sbv_link); 228 else 229 SLIST_INSERT_AFTER(bv2, bv, sbv_link); 230 } 231 232 /* Maintain chunk order. */ 233 bc2 = NULL; 234 SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) { 235 if (bc1->sbc_chunk_id > bc->sbc_chunk_id) 236 break; 237 bc2 = bc1; 238 } 239 if (bc2 == NULL) 240 SLIST_INSERT_HEAD(&bv->sbv_chunks, 241 bc, sbc_link); 242 else 243 SLIST_INSERT_AFTER(bc2, bc, sbc_link); 244 245 bv->sbv_chunks_found++; 246 } 247 } 248 249 /* 250 * Assemble RAID volumes. 251 */ 252 volno = 0; 253 SLIST_FOREACH(bv, &sr_volumes, sbv_link) { 254 255 /* Skip if this is a hotspare "volume". */ 256 if (bv->sbv_level == SR_HOTSPARE_LEVEL && 257 bv->sbv_chunk_no == 1) 258 continue; 259 260 /* Determine current ondisk version. */ 261 bv->sbv_ondisk = 0; 262 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { 263 if (bc->sbc_ondisk > bv->sbv_ondisk) 264 bv->sbv_ondisk = bc->sbc_ondisk; 265 } 266 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { 267 if (bc->sbc_ondisk != bv->sbv_ondisk) 268 bc->sbc_state = BIOC_SDOFFLINE; 269 } 270 271 /* XXX - Check for duplicate chunks. */ 272 273 /* 274 * Validate that volume has sufficient chunks for 275 * read-only access. 276 * 277 * XXX - check chunk states. 278 */ 279 bv->sbv_state = BIOC_SVOFFLINE; 280 switch (bv->sbv_level) { 281 case 0: 282 case 'C': 283 case 'c': 284 if (bv->sbv_chunk_no == bv->sbv_chunks_found) 285 bv->sbv_state = BIOC_SVONLINE; 286 break; 287 288 case 1: 289 if (bv->sbv_chunk_no == bv->sbv_chunks_found) 290 bv->sbv_state = BIOC_SVONLINE; 291 else if (bv->sbv_chunks_found > 0) 292 bv->sbv_state = BIOC_SVDEGRADED; 293 break; 294 } 295 296 bv->sbv_unit = volno++; 297 if (bv->sbv_state != BIOC_SVOFFLINE) 298 printf(" sr%d%s", bv->sbv_unit, 299 bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : ""); 300 } 301 302 explicit_bzero(md, SR_META_SIZE * DEV_BSIZE); 303 free(md, 0); 304 } 305 306 int 307 sr_strategy(struct sr_boot_volume *bv, int rw, daddr32_t blk, size_t size, 308 void *buf, size_t *rsize) 309 { 310 struct diskinfo *sr_dip, *dip; 311 struct sr_boot_chunk *bc; 312 struct aes_xts_ctx ctx; 313 size_t i, j, nsect; 314 daddr_t blkno; 315 u_char iv[8]; 316 u_char *bp; 317 int err; 318 319 /* We only support read-only softraid. */ 320 if (rw != F_READ) 321 return ENOTSUP; 322 323 /* Partition offset within softraid volume. */ 324 sr_dip = (struct diskinfo *)bv->sbv_diskinfo; 325 blk += sr_dip->disklabel.d_partitions[bv->sbv_part - 'a'].p_offset; 326 327 if (bv->sbv_level == 0) { 328 return ENOTSUP; 329 } else if (bv->sbv_level == 1) { 330 331 /* Select first online chunk. */ 332 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) 333 if (bc->sbc_state == BIOC_SDONLINE) 334 break; 335 if (bc == NULL) 336 return EIO; 337 338 dip = (struct diskinfo *)bc->sbc_diskinfo; 339 dip->bsddev = bc->sbc_mm; 340 blk += bv->sbv_data_blkno; 341 342 /* XXX - If I/O failed we should try another chunk... */ 343 return dip->strategy(dip, rw, blk, size, buf, rsize); 344 345 } else if (bv->sbv_level == 'C') { 346 347 /* Select first online chunk. */ 348 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) 349 if (bc->sbc_state == BIOC_SDONLINE) 350 break; 351 if (bc == NULL) 352 return EIO; 353 354 dip = (struct diskinfo *)bc->sbc_diskinfo; 355 dip->bsddev = bc->sbc_mm; 356 357 /* XXX - select correct key. */ 358 aes_xts_setkey(&ctx, (u_char *)bv->sbv_keys, 64); 359 360 nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE; 361 for (i = 0; i < nsect; i++) { 362 blkno = blk + i; 363 bp = ((u_char *)buf) + i * DEV_BSIZE; 364 err = dip->strategy(dip, rw, bv->sbv_data_blkno + blkno, 365 DEV_BSIZE, bp, NULL); 366 if (err != 0) 367 return err; 368 369 bcopy(&blkno, iv, sizeof(blkno)); 370 aes_xts_reinit(&ctx, iv); 371 for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE) 372 aes_xts_decrypt(&ctx, bp + j); 373 } 374 if (rsize != NULL) 375 *rsize = nsect * DEV_BSIZE; 376 377 return err; 378 379 } else 380 return ENOTSUP; 381 } 382 383 const char * 384 sr_getdisklabel(struct sr_boot_volume *bv, struct disklabel *label) 385 { 386 struct dos_partition *dp; 387 struct dos_mbr mbr; 388 u_int start = 0; 389 char buf[DEV_BSIZE]; 390 int i; 391 392 /* Check for MBR to determine partition offset. */ 393 bzero(&mbr, sizeof(mbr)); 394 sr_strategy(bv, F_READ, DOSBBSECTOR, sizeof(mbr), &mbr, NULL); 395 if (mbr.dmbr_sign == DOSMBR_SIGNATURE) { 396 397 /* Search for OpenBSD partition */ 398 for (i = 0; i < NDOSPART; i++) { 399 dp = &mbr.dmbr_parts[i]; 400 if (!dp->dp_size) 401 continue; 402 if (dp->dp_typ == DOSPTYP_OPENBSD) { 403 start = dp->dp_start; 404 break; 405 } 406 } 407 } 408 409 /* Read the disklabel. */ 410 sr_strategy(bv, F_READ, start + DOS_LABELSECTOR, 411 sizeof(struct disklabel), buf, NULL); 412 413 #ifdef BIOS_DEBUG 414 printf("sr_getdisklabel: magic %lx\n", 415 ((struct disklabel *)buf)->d_magic); 416 for (i = 0; i < MAXPARTITIONS; i++) 417 printf("part %c: type = %d, size = %d, offset = %d\n", 'a' + i, 418 (int)((struct disklabel *)buf)->d_partitions[i].p_fstype, 419 (int)((struct disklabel *)buf)->d_partitions[i].p_size, 420 (int)((struct disklabel *)buf)->d_partitions[i].p_offset); 421 #endif 422 423 /* Fill in disklabel */ 424 return (getdisklabel(buf, label)); 425 } 426