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