1 /* $OpenBSD: softraid_sparc64.c,v 1.2 2016/09/11 17:53:26 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/stand.h> 28 #include <lib/libsa/aes_xts.h> 29 #include <lib/libsa/softraid.h> 30 31 #include "disk.h" 32 #include "ofdev.h" 33 #include "softraid_sparc64.h" 34 35 void 36 srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som) 37 { 38 struct sr_meta_opt_hdr *omh; 39 struct sr_meta_opt_item *omi; 40 #if 0 41 u_int8_t checksum[MD5_DIGEST_LENGTH]; 42 #endif 43 int i; 44 45 /* Process optional metadata. */ 46 omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) + 47 sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no); 48 for (i = 0; i < sm->ssdi.ssd_opt_no; i++) { 49 50 #ifdef DEBUG 51 printf("Found optional metadata of type %u, length %u\n", 52 omh->som_type, omh->som_length); 53 #endif 54 55 /* Unsupported old fixed length optional metadata. */ 56 if (omh->som_length == 0) { 57 omh = (struct sr_meta_opt_hdr *)((void *)omh + 58 SR_OLD_META_OPT_SIZE); 59 continue; 60 } 61 62 /* Load variable length optional metadata. */ 63 omi = alloc(sizeof(struct sr_meta_opt_item)); 64 bzero(omi, sizeof(struct sr_meta_opt_item)); 65 SLIST_INSERT_HEAD(som, omi, omi_link); 66 omi->omi_som = alloc(omh->som_length); 67 bzero(omi->omi_som, omh->som_length); 68 bcopy(omh, omi->omi_som, omh->som_length); 69 70 #if 0 71 /* XXX - Validate checksum. */ 72 bcopy(&omi->omi_som->som_checksum, &checksum, 73 MD5_DIGEST_LENGTH); 74 bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH); 75 sr_checksum(sc, omi->omi_som, 76 &omi->omi_som->som_checksum, omh->som_length); 77 if (bcmp(&checksum, &omi->omi_som->som_checksum, 78 sizeof(checksum))) 79 panic("%s: invalid optional metadata checksum", 80 DEVNAME(sc)); 81 #endif 82 83 omh = (struct sr_meta_opt_hdr *)((void *)omh + 84 omh->som_length); 85 } 86 } 87 88 void 89 srprobe_keydisk_load(struct sr_metadata *sm) 90 { 91 struct sr_meta_opt_hdr *omh; 92 struct sr_meta_keydisk *skm; 93 struct sr_boot_keydisk *kd; 94 int i; 95 96 /* Process optional metadata. */ 97 omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) + 98 sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no); 99 for (i = 0; i < sm->ssdi.ssd_opt_no; i++) { 100 101 /* Unsupported old fixed length optional metadata. */ 102 if (omh->som_length == 0) { 103 omh = (struct sr_meta_opt_hdr *)((void *)omh + 104 SR_OLD_META_OPT_SIZE); 105 continue; 106 } 107 108 if (omh->som_type != SR_OPT_KEYDISK) { 109 omh = (struct sr_meta_opt_hdr *)((void *)omh + 110 omh->som_length); 111 continue; 112 } 113 114 kd = alloc(sizeof(struct sr_boot_keydisk)); 115 bcopy(&sm->ssdi.ssd_uuid, &kd->kd_uuid, sizeof(kd->kd_uuid)); 116 skm = (struct sr_meta_keydisk*)omh; 117 bcopy(&skm->skm_maskkey, &kd->kd_key, sizeof(kd->kd_key)); 118 SLIST_INSERT_HEAD(&sr_keydisks, kd, kd_link); 119 } 120 } 121 122 void 123 srprobe(void) 124 { 125 struct sr_boot_volume *bv, *bv1, *bv2; 126 struct sr_boot_chunk *bc, *bc1, *bc2; 127 struct sr_meta_chunk *mc; 128 struct sr_metadata *md; 129 struct diskinfo *dip; 130 struct partition *pp; 131 struct of_dev ofdev; 132 size_t read; 133 int i, error, diskno, volno, ihandle; 134 dev_t bsd_dev; 135 136 /* Probe for softraid volumes. */ 137 SLIST_INIT(&sr_volumes); 138 SLIST_INIT(&sr_keydisks); 139 140 md = alloc(SR_META_SIZE * DEV_BSIZE); 141 diskno = 0; 142 ihandle = -1; 143 TAILQ_FOREACH(dip, &disklist, list) { 144 ihandle = OF_open(dip->path); 145 if (ihandle == -1) 146 continue; 147 bzero(&ofdev, sizeof(ofdev)); 148 ofdev.handle = ihandle; 149 ofdev.type = OFDEV_DISK; 150 ofdev.bsize = DEV_BSIZE; 151 for (i = 0; i < MAXPARTITIONS; i++) { 152 pp = &dip->disklabel.d_partitions[i]; 153 if (pp->p_fstype != FS_RAID || pp->p_size == 0) 154 continue; 155 156 /* Read softraid metadata. */ 157 bzero(md, SR_META_SIZE * DEV_BSIZE); 158 ofdev.partoff = DL_SECTOBLK(&dip->disklabel, 159 DL_GETPOFFSET(pp)); 160 error = strategy(&ofdev, F_READ, SR_META_OFFSET, 161 SR_META_SIZE * DEV_BSIZE, md, &read); 162 if (error || read != SR_META_SIZE * DEV_BSIZE) 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 = diskno++; 184 bc->sbc_part = 'a' + i; 185 186 bsd_dev = MAKEBOOTDEV( 187 dip->disklabel.d_type == DTYPE_SCSI ? 4 : 0, 188 0, 0, diskno, RAW_PART); 189 bc->sbc_mm = MAKEBOOTDEV(B_TYPE(bsd_dev), 190 B_ADAPTOR(bsd_dev), B_CONTROLLER(bsd_dev), 191 B_UNIT(bsd_dev), bc->sbc_part - 'a'); 192 193 bc->sbc_chunk_id = md->ssdi.ssd_chunk_id; 194 bc->sbc_ondisk = md->ssd_ondisk; 195 bc->sbc_state = mc->scm_status; 196 197 SLIST_FOREACH(bv, &sr_volumes, sbv_link) { 198 if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid, 199 sizeof(md->ssdi.ssd_uuid)) == 0) 200 break; 201 } 202 203 if (bv == NULL) { 204 bv = alloc(sizeof(struct sr_boot_volume)); 205 bzero(bv, sizeof(struct sr_boot_volume)); 206 bv->sbv_level = md->ssdi.ssd_level; 207 bv->sbv_volid = md->ssdi.ssd_volid; 208 bv->sbv_chunk_no = md->ssdi.ssd_chunk_no; 209 bv->sbv_flags = md->ssdi.ssd_vol_flags; 210 bv->sbv_size = md->ssdi.ssd_size; 211 bv->sbv_data_blkno = md->ssd_data_blkno; 212 bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid, 213 sizeof(md->ssdi.ssd_uuid)); 214 SLIST_INIT(&bv->sbv_chunks); 215 SLIST_INIT(&bv->sbv_meta_opt); 216 217 /* Load optional metadata for this volume. */ 218 srprobe_meta_opt_load(md, &bv->sbv_meta_opt); 219 220 /* Maintain volume order. */ 221 bv2 = NULL; 222 SLIST_FOREACH(bv1, &sr_volumes, sbv_link) { 223 if (bv1->sbv_volid > bv->sbv_volid) 224 break; 225 bv2 = bv1; 226 } 227 if (bv2 == NULL) 228 SLIST_INSERT_HEAD(&sr_volumes, bv, 229 sbv_link); 230 else 231 SLIST_INSERT_AFTER(bv2, bv, sbv_link); 232 } 233 234 /* Maintain chunk order. */ 235 bc2 = NULL; 236 SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) { 237 if (bc1->sbc_chunk_id > bc->sbc_chunk_id) 238 break; 239 bc2 = bc1; 240 } 241 if (bc2 == NULL) 242 SLIST_INSERT_HEAD(&bv->sbv_chunks, 243 bc, sbc_link); 244 else 245 SLIST_INSERT_AFTER(bc2, bc, sbc_link); 246 247 bv->sbv_chunks_found++; 248 } 249 OF_close(ihandle); 250 } 251 252 /* 253 * Assemble RAID volumes. 254 */ 255 volno = 0; 256 SLIST_FOREACH(bv, &sr_volumes, sbv_link) { 257 258 /* Skip if this is a hotspare "volume". */ 259 if (bv->sbv_level == SR_HOTSPARE_LEVEL && 260 bv->sbv_chunk_no == 1) 261 continue; 262 263 /* Determine current ondisk version. */ 264 bv->sbv_ondisk = 0; 265 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { 266 if (bc->sbc_ondisk > bv->sbv_ondisk) 267 bv->sbv_ondisk = bc->sbc_ondisk; 268 } 269 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { 270 if (bc->sbc_ondisk != bv->sbv_ondisk) 271 bc->sbc_state = BIOC_SDOFFLINE; 272 } 273 274 /* XXX - Check for duplicate chunks. */ 275 276 /* 277 * Validate that volume has sufficient chunks for 278 * read-only access. 279 * 280 * XXX - check chunk states. 281 */ 282 bv->sbv_state = BIOC_SVOFFLINE; 283 switch (bv->sbv_level) { 284 case 0: 285 case 'C': 286 case 'c': 287 if (bv->sbv_chunk_no == bv->sbv_chunks_found) 288 bv->sbv_state = BIOC_SVONLINE; 289 break; 290 291 case 1: 292 if (bv->sbv_chunk_no == bv->sbv_chunks_found) 293 bv->sbv_state = BIOC_SVONLINE; 294 else if (bv->sbv_chunks_found > 0) 295 bv->sbv_state = BIOC_SVDEGRADED; 296 break; 297 } 298 299 bv->sbv_unit = volno++; 300 if (bv->sbv_state != BIOC_SVOFFLINE) 301 printf("sr%d%s\n", bv->sbv_unit, 302 bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : ""); 303 } 304 305 explicit_bzero(md, SR_META_SIZE * DEV_BSIZE); 306 free(md, SR_META_SIZE * DEV_BSIZE); 307 } 308 309 int 310 sr_strategy(struct sr_boot_volume *bv, int rw, daddr32_t blk, size_t size, 311 void *buf, size_t *rsize) 312 { 313 struct diskinfo *sr_dip, *dip; 314 struct partition *pp; 315 struct sr_boot_chunk *bc; 316 struct aes_xts_ctx ctx; 317 struct of_dev ofdev; 318 size_t i, j, nsect; 319 daddr_t blkno; 320 u_char iv[8]; 321 u_char *bp; 322 int err; 323 int ihandle; 324 325 /* We only support read-only softraid. */ 326 if (rw != F_READ) 327 return ENOTSUP; 328 329 /* Partition offset within softraid volume. */ 330 sr_dip = (struct diskinfo *)bv->sbv_diskinfo; 331 blk += 332 DL_GETPOFFSET(&sr_dip->disklabel.d_partitions[bv->sbv_part - 'a']); 333 334 if (bv->sbv_level == 0) { 335 return ENOTSUP; 336 } else if (bv->sbv_level == 1) { 337 338 /* Select first online chunk. */ 339 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) 340 if (bc->sbc_state == BIOC_SDONLINE) 341 break; 342 if (bc == NULL) 343 return EIO; 344 345 dip = (struct diskinfo *)bc->sbc_diskinfo; 346 pp = &dip->disklabel.d_partitions[bc->sbc_part - 'a']; 347 blk += bv->sbv_data_blkno; 348 349 /* XXX - If I/O failed we should try another chunk... */ 350 ihandle = OF_open(dip->path); 351 if (ihandle == -1) 352 return EIO; 353 bzero(&ofdev, sizeof(ofdev)); 354 ofdev.handle = ihandle; 355 ofdev.type = OFDEV_DISK; 356 ofdev.bsize = DEV_BSIZE; 357 ofdev.partoff = DL_GETPOFFSET(pp); 358 err = strategy(&ofdev, rw, blk, size, buf, rsize); 359 OF_close(ihandle); 360 return err; 361 362 } else if (bv->sbv_level == 'C') { 363 364 /* Select first online chunk. */ 365 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) 366 if (bc->sbc_state == BIOC_SDONLINE) 367 break; 368 if (bc == NULL) 369 return EIO; 370 371 dip = (struct diskinfo *)bc->sbc_diskinfo; 372 pp = &dip->disklabel.d_partitions[bc->sbc_part - 'a']; 373 374 ihandle = OF_open(dip->path); 375 if (ihandle == -1) 376 return EIO; 377 bzero(&ofdev, sizeof(ofdev)); 378 ofdev.handle = ihandle; 379 ofdev.type = OFDEV_DISK; 380 ofdev.bsize = DEV_BSIZE; 381 ofdev.partoff = DL_GETPOFFSET(pp); 382 383 /* XXX - select correct key. */ 384 aes_xts_setkey(&ctx, (u_char *)bv->sbv_keys, 64); 385 386 nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE; 387 for (i = 0; i < nsect; i++) { 388 blkno = blk + i; 389 bp = ((u_char *)buf) + i * DEV_BSIZE; 390 391 err = strategy(&ofdev, rw, 392 bv->sbv_data_blkno + blkno, 393 DEV_BSIZE, bp, rsize); 394 if (err != 0 || *rsize != DEV_BSIZE) { 395 printf("Read from crypto volume failed " 396 "(read %d bytes): %s\n", *rsize, 397 strerror(err)); 398 OF_close(ihandle); 399 return err; 400 } 401 bcopy(&blkno, iv, sizeof(blkno)); 402 aes_xts_reinit(&ctx, iv); 403 for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE) 404 aes_xts_decrypt(&ctx, bp + j); 405 } 406 *rsize = nsect * DEV_BSIZE; 407 OF_close(ihandle); 408 return err; 409 410 } else 411 return ENOTSUP; 412 } 413 414 const char * 415 sr_getdisklabel(struct sr_boot_volume *bv, struct disklabel *label) 416 { 417 struct of_dev ofdev; 418 int err; 419 #ifdef DEBUG 420 int i; 421 #endif 422 423 bzero(&ofdev, sizeof ofdev); 424 ofdev.type = OFDEV_SOFTRAID; 425 426 if (load_disklabel(&ofdev, label)) 427 return ("Could not read disklabel from softraid"); 428 #ifdef DEBUG 429 printf("sr_getdisklabel: magic %lx\n", label->d_magic); 430 for (i = 0; i < MAXPARTITIONS; i++) 431 printf("part %c: type = %d, size = %d, offset = %d\n", 'a' + i, 432 (int)label->d_partitions[i].p_fstype, 433 (int)label->d_partitions[i].p_size, 434 (int)label->d_partitions[i].p_offset); 435 #endif 436 437 return (NULL); 438 } 439