1 /*	$OpenBSD: softraid_riscv64.c,v 1.2 2021/06/02 22:44:27 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/aes_xts.h>
28 #include <lib/libsa/softraid.h>
29 #include <lib/libz/zlib.h>
30 
31 #include <efi.h>
32 
33 #include "libsa.h"
34 #include "disk.h"
35 #include "efidev.h"
36 #include "softraid_riscv64.h"
37 
38 static int gpt_chk_mbr(struct dos_partition *, u_int64_t);
39 static uint64_t findopenbsd_gpt(struct sr_boot_volume *, const char **);
40 
41 void
42 srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som)
43 {
44 	struct sr_meta_opt_hdr	*omh;
45 	struct sr_meta_opt_item *omi;
46 #if 0
47 	u_int8_t checksum[MD5_DIGEST_LENGTH];
48 #endif
49 	int			i;
50 
51 	/* Process optional metadata. */
52 	omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) +
53 	    sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no);
54 	for (i = 0; i < sm->ssdi.ssd_opt_no; i++) {
55 
56 #ifdef BIOS_DEBUG
57 		printf("Found optional metadata of type %u, length %u\n",
58 		    omh->som_type, omh->som_length);
59 #endif
60 
61 		/* Unsupported old fixed length optional metadata. */
62 		if (omh->som_length == 0) {
63 			omh = (struct sr_meta_opt_hdr *)((void *)omh +
64 			    SR_OLD_META_OPT_SIZE);
65 			continue;
66 		}
67 
68 		/* Load variable length optional metadata. */
69 		omi = alloc(sizeof(struct sr_meta_opt_item));
70 		bzero(omi, sizeof(struct sr_meta_opt_item));
71 		SLIST_INSERT_HEAD(som, omi, omi_link);
72 		omi->omi_som = alloc(omh->som_length);
73 		bzero(omi->omi_som, omh->som_length);
74 		bcopy(omh, omi->omi_som, omh->som_length);
75 
76 #if 0
77 		/* XXX - Validate checksum. */
78 		bcopy(&omi->omi_som->som_checksum, &checksum,
79 		    MD5_DIGEST_LENGTH);
80 		bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH);
81 		sr_checksum(sc, omi->omi_som,
82 		    &omi->omi_som->som_checksum, omh->som_length);
83 		if (bcmp(&checksum, &omi->omi_som->som_checksum,
84 		    sizeof(checksum)))
85 			panic("%s: invalid optional metadata checksum",
86 			    DEVNAME(sc));
87 #endif
88 
89 		omh = (struct sr_meta_opt_hdr *)((void *)omh +
90 		    omh->som_length);
91 	}
92 }
93 
94 void
95 srprobe_keydisk_load(struct sr_metadata *sm)
96 {
97 	struct sr_meta_opt_hdr	*omh;
98 	struct sr_meta_keydisk	*skm;
99 	struct sr_boot_keydisk	*kd;
100 	int i;
101 
102 	/* Process optional metadata. */
103 	omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) +
104 	    sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no);
105 	for (i = 0; i < sm->ssdi.ssd_opt_no; i++) {
106 
107 		/* Unsupported old fixed length optional metadata. */
108 		if (omh->som_length == 0) {
109 			omh = (struct sr_meta_opt_hdr *)((void *)omh +
110 			    SR_OLD_META_OPT_SIZE);
111 			continue;
112 		}
113 
114 		if (omh->som_type != SR_OPT_KEYDISK) {
115 			omh = (struct sr_meta_opt_hdr *)((void *)omh +
116 			    omh->som_length);
117 			continue;
118 		}
119 
120 		kd = alloc(sizeof(struct sr_boot_keydisk));
121 		bcopy(&sm->ssdi.ssd_uuid, &kd->kd_uuid, sizeof(kd->kd_uuid));
122 		skm = (struct sr_meta_keydisk*)omh;
123 		bcopy(&skm->skm_maskkey, &kd->kd_key, sizeof(kd->kd_key));
124 		SLIST_INSERT_HEAD(&sr_keydisks, kd, kd_link);
125 	}
126 }
127 
128 void
129 srprobe(void)
130 {
131 	struct sr_boot_volume *bv, *bv1, *bv2;
132 	struct sr_boot_chunk *bc, *bc1, *bc2;
133 	struct sr_meta_chunk *mc;
134 	struct sr_metadata *md;
135 	struct diskinfo *dip;
136 	struct partition *pp;
137 	int i, error, volno;
138 	daddr_t off;
139 
140 	/* Probe for softraid volumes. */
141 	SLIST_INIT(&sr_volumes);
142 	SLIST_INIT(&sr_keydisks);
143 
144 	md = alloc(SR_META_SIZE * DEV_BSIZE);
145 
146 	TAILQ_FOREACH(dip, &disklist, list) {
147 
148 		/* Make sure disklabel has been read. */
149 		if ((dip->flags & DISKINFO_FLAG_GOODLABEL) == 0)
150 			continue;
151 
152 		for (i = 0; i < MAXPARTITIONS; i++) {
153 
154 			pp = &dip->disklabel.d_partitions[i];
155 			if (pp->p_fstype != FS_RAID || pp->p_size == 0)
156 				continue;
157 
158 			/* Read softraid metadata. */
159 			bzero(md, SR_META_SIZE * DEV_BSIZE);
160 			off = DL_SECTOBLK(&dip->disklabel, DL_GETPOFFSET(pp));
161 			off += SR_META_OFFSET;
162 			error = dip->diskio(F_READ, dip, off, SR_META_SIZE, md);
163 			if (error)
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 = 0;
185 			bc->sbc_part = 'a' + i;
186 
187 			bc->sbc_mm = 0;
188 
189 			bc->sbc_chunk_id = md->ssdi.ssd_chunk_id;
190 			bc->sbc_ondisk = md->ssd_ondisk;
191 			bc->sbc_state = mc->scm_status;
192 
193 			SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
194 				if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
195 				    sizeof(md->ssdi.ssd_uuid)) == 0)
196 					break;
197 			}
198 
199 			if (bv == NULL) {
200 				bv = alloc(sizeof(struct sr_boot_volume));
201 				bzero(bv, sizeof(struct sr_boot_volume));
202 				bv->sbv_level = md->ssdi.ssd_level;
203 				bv->sbv_volid = md->ssdi.ssd_volid;
204 				bv->sbv_chunk_no = md->ssdi.ssd_chunk_no;
205 				bv->sbv_flags = md->ssdi.ssd_vol_flags;
206 				bv->sbv_size = md->ssdi.ssd_size;
207 				bv->sbv_secsize = md->ssdi.ssd_secsize;
208 				bv->sbv_data_blkno = md->ssd_data_blkno;
209 				bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
210 				    sizeof(md->ssdi.ssd_uuid));
211 				SLIST_INIT(&bv->sbv_chunks);
212 				SLIST_INIT(&bv->sbv_meta_opt);
213 
214 				/* Load optional metadata for this volume. */
215 				srprobe_meta_opt_load(md, &bv->sbv_meta_opt);
216 
217 				/* Maintain volume order. */
218 				bv2 = NULL;
219 				SLIST_FOREACH(bv1, &sr_volumes, sbv_link) {
220 					if (bv1->sbv_volid > bv->sbv_volid)
221 						break;
222 					bv2 = bv1;
223 				}
224 				if (bv2 == NULL)
225 					SLIST_INSERT_HEAD(&sr_volumes, bv,
226 					    sbv_link);
227 				else
228 					SLIST_INSERT_AFTER(bv2, bv, sbv_link);
229 			}
230 
231 			/* Maintain chunk order. */
232 			bc2 = NULL;
233 			SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) {
234 				if (bc1->sbc_chunk_id > bc->sbc_chunk_id)
235 					break;
236 				bc2 = bc1;
237 			}
238 			if (bc2 == NULL)
239 				SLIST_INSERT_HEAD(&bv->sbv_chunks,
240 				    bc, sbc_link);
241 			else
242 				SLIST_INSERT_AFTER(bc2, bc, sbc_link);
243 
244 			bv->sbv_chunks_found++;
245 		}
246 	}
247 
248 	/*
249 	 * Assemble RAID volumes.
250 	 */
251 	volno = 0;
252 	SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
253 
254 		/* Skip if this is a hotspare "volume". */
255 		if (bv->sbv_level == SR_HOTSPARE_LEVEL &&
256 		    bv->sbv_chunk_no == 1)
257 			continue;
258 
259 		/* Determine current ondisk version. */
260 		bv->sbv_ondisk = 0;
261 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
262 			if (bc->sbc_ondisk > bv->sbv_ondisk)
263 				bv->sbv_ondisk = bc->sbc_ondisk;
264 		}
265 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
266 			if (bc->sbc_ondisk != bv->sbv_ondisk)
267 				bc->sbc_state = BIOC_SDOFFLINE;
268 		}
269 
270 		/* XXX - Check for duplicate chunks. */
271 
272 		/*
273 		 * Validate that volume has sufficient chunks for
274 		 * read-only access.
275 		 *
276 		 * XXX - check chunk states.
277 		 */
278 		bv->sbv_state = BIOC_SVOFFLINE;
279 		switch (bv->sbv_level) {
280 		case 0:
281 		case 'C':
282 		case 'c':
283 			if (bv->sbv_chunk_no == bv->sbv_chunks_found)
284 				bv->sbv_state = BIOC_SVONLINE;
285 			break;
286 
287 		case 1:
288 			if (bv->sbv_chunk_no == bv->sbv_chunks_found)
289 				bv->sbv_state = BIOC_SVONLINE;
290 			else if (bv->sbv_chunks_found > 0)
291 				bv->sbv_state = BIOC_SVDEGRADED;
292 			break;
293 		}
294 
295 		bv->sbv_unit = volno++;
296 		if (bv->sbv_state != BIOC_SVOFFLINE)
297 			printf(" sr%d%s", bv->sbv_unit,
298 			    bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : "");
299 	}
300 
301 	explicit_bzero(md, SR_META_SIZE * DEV_BSIZE);
302 	free(md, SR_META_SIZE * DEV_BSIZE);
303 }
304 
305 int
306 sr_strategy(struct sr_boot_volume *bv, int rw, daddr_t blk, size_t size,
307     void *buf, size_t *rsize)
308 {
309 	struct diskinfo *sr_dip, *dip;
310 	struct sr_boot_chunk *bc;
311 	struct aes_xts_ctx ctx;
312 	size_t i, j, nsect;
313 	daddr_t blkno;
314 	u_char iv[8];
315 	u_char *bp;
316 	int err;
317 
318 	/* We only support read-only softraid. */
319 	if (rw != F_READ)
320 		return ENOTSUP;
321 
322 	/* Partition offset within softraid volume. */
323 	sr_dip = (struct diskinfo *)bv->sbv_diskinfo;
324 	blk += DL_SECTOBLK(&sr_dip->disklabel,
325 	    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 		blk += bv->sbv_data_blkno;
340 
341 		/* XXX - If I/O failed we should try another chunk... */
342 		return dip->strategy(dip, rw, blk, size, buf, rsize);
343 
344 	} else if (bv->sbv_level == 'C') {
345 
346 		/* Select first online chunk. */
347 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
348 			if (bc->sbc_state == BIOC_SDONLINE)
349 				break;
350 		if (bc == NULL)
351 			return EIO;
352 
353 		dip = (struct diskinfo *)bc->sbc_diskinfo;
354 
355 		/* XXX - select correct key. */
356 		aes_xts_setkey(&ctx, (u_char *)bv->sbv_keys, 64);
357 
358 		nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
359 		for (i = 0; i < nsect; i++) {
360 			blkno = blk + i;
361 			bp = ((u_char *)buf) + i * DEV_BSIZE;
362 			err = dip->strategy(dip, rw, bv->sbv_data_blkno + blkno,
363 			    DEV_BSIZE, bp, NULL);
364 			if (err != 0)
365 				return err;
366 
367 			bcopy(&blkno, iv, sizeof(blkno));
368 			aes_xts_reinit(&ctx, iv);
369 			for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE)
370 				aes_xts_decrypt(&ctx, bp + j);
371 		}
372 		if (rsize != NULL)
373 			*rsize = nsect * DEV_BSIZE;
374 
375 		return err;
376 
377 	} else
378 		return ENOTSUP;
379 }
380 
381 /*
382  * Returns 0 if the MBR with the provided partition array is a GPT protective
383  * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
384  * one MBR partition, an EFI partition that either covers the whole disk or as
385  * much of it as is possible with a 32bit size field.
386  *
387  * Taken from kern/subr_disk.c.
388  *
389  * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
390  */
391 static int
392 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
393 {
394 	struct dos_partition *dp2;
395 	int efi, found, i;
396 	u_int32_t psize;
397 
398 	found = efi = 0;
399 	for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
400 		if (dp2->dp_typ == DOSPTYP_UNUSED)
401 			continue;
402 		found++;
403 		if (dp2->dp_typ != DOSPTYP_EFI)
404 			continue;
405 		if (letoh32(dp2->dp_start) != GPTSECTOR)
406 			continue;
407 		psize = letoh32(dp2->dp_size);
408 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX)
409 			efi++;
410 	}
411 	if (found == 1 && efi == 1)
412 		return (0);
413 
414 	return (1);
415 }
416 
417 static uint64_t
418 findopenbsd_gpt(struct sr_boot_volume *bv, const char **err)
419 {
420 	struct			 gpt_header gh;
421 	int			 i, part, found;
422 	uint64_t		 lba;
423 	uint32_t		 orig_csum, new_csum;
424 	uint32_t		 ghsize, ghpartsize, ghpartnum, ghpartspersec;
425 	uint32_t		 gpsectors;
426 	const char		 openbsd_uuid_code[] = GPT_UUID_OPENBSD;
427 	struct gpt_partition	 gp;
428 	static struct uuid	*openbsd_uuid = NULL, openbsd_uuid_space;
429 	u_char		 	*buf;
430 
431 	/* Prepare OpenBSD UUID */
432 	if (openbsd_uuid == NULL) {
433 		/* XXX: should be replaced by uuid_dec_be() */
434 		memcpy(&openbsd_uuid_space, openbsd_uuid_code,
435 		    sizeof(openbsd_uuid_space));
436 		openbsd_uuid_space.time_low =
437 		    betoh32(openbsd_uuid_space.time_low);
438 		openbsd_uuid_space.time_mid =
439 		    betoh16(openbsd_uuid_space.time_mid);
440 		openbsd_uuid_space.time_hi_and_version =
441 		    betoh16(openbsd_uuid_space.time_hi_and_version);
442 
443 		openbsd_uuid = &openbsd_uuid_space;
444 	}
445 
446 	if (bv->sbv_secsize > 4096) {
447 		*err = "disk sector > 4096 bytes\n";
448 		return (-1);
449 	}
450 	buf = alloc(bv->sbv_secsize);
451 	if (buf == NULL) {
452 		*err = "out of memory\n";
453 		return (-1);
454 	}
455 	bzero(buf, bv->sbv_secsize);
456 
457 	/* GPT Header */
458 	lba = GPTSECTOR;
459 	sr_strategy(bv, F_READ, lba * (bv->sbv_secsize / DEV_BSIZE), DEV_BSIZE,
460 	    buf, NULL);
461 	memcpy(&gh, buf, sizeof(gh));
462 
463 	/* Check signature */
464 	if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
465 		*err = "bad GPT signature\n";
466 		free(buf, bv->sbv_secsize);
467 		return (-1);
468 	}
469 
470 	if (letoh32(gh.gh_rev) != GPTREVISION) {
471 		*err = "bad GPT revision\n";
472 		free(buf, bv->sbv_secsize);
473 		return (-1);
474 	}
475 
476 	ghsize = letoh32(gh.gh_size);
477 	if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) {
478 		*err = "bad GPT header size\n";
479 		free(buf, bv->sbv_secsize);
480 		return (-1);
481 	}
482 
483 	/* Check checksum */
484 	orig_csum = gh.gh_csum;
485 	gh.gh_csum = 0;
486 	new_csum = crc32(0, (unsigned char *)&gh, ghsize);
487 	gh.gh_csum = orig_csum;
488 	if (letoh32(orig_csum) != new_csum) {
489 		*err = "bad GPT header checksum\n";
490 		free(buf, bv->sbv_secsize);
491 		return (-1);
492 	}
493 
494 	lba = letoh64(gh.gh_part_lba);
495 	ghpartsize = letoh32(gh.gh_part_size);
496 	ghpartspersec = bv->sbv_secsize / ghpartsize;
497 	ghpartnum = letoh32(gh.gh_part_num);
498 	gpsectors = (ghpartnum + ghpartspersec - 1) / ghpartspersec;
499 	new_csum = crc32(0L, Z_NULL, 0);
500 	found = 0;
501 	for (i = 0; i < gpsectors; i++, lba++) {
502 		sr_strategy(bv, F_READ, lba * (bv->sbv_secsize / DEV_BSIZE),
503 		    bv->sbv_secsize, buf, NULL);
504 		for (part = 0; part < ghpartspersec; part++) {
505 			if (ghpartnum == 0)
506 				break;
507 			new_csum = crc32(new_csum, buf + part * sizeof(gp),
508 			    sizeof(gp));
509 			ghpartnum--;
510 			if (found)
511 				continue;
512 			memcpy(&gp, buf + part * sizeof(gp), sizeof(gp));
513 			if (memcmp(&gp.gp_type, openbsd_uuid,
514 			    sizeof(struct uuid)) == 0)
515 				found = 1;
516 		}
517 	}
518 
519 	free(buf, bv->sbv_secsize);
520 
521 	if (new_csum != letoh32(gh.gh_part_csum)) {
522 		*err = "bad GPT entries checksum\n";
523 		return (-1);
524 	}
525 	if (found)
526 		return (letoh64(gp.gp_lba_start));
527 
528 	return (-1);
529 }
530 
531 const char *
532 sr_getdisklabel(struct sr_boot_volume *bv, struct disklabel *label)
533 {
534 	struct dos_partition *dp;
535 	struct dos_mbr mbr;
536 	const char *err = NULL;
537 	u_int start = 0;
538 	char buf[DEV_BSIZE];
539 	int i;
540 
541 	/* Check for MBR to determine partition offset. */
542 	bzero(&mbr, sizeof(mbr));
543 	sr_strategy(bv, F_READ, DOSBBSECTOR, sizeof(mbr), &mbr, NULL);
544 	if (gpt_chk_mbr(mbr.dmbr_parts, bv->sbv_size /
545 		    (bv->sbv_secsize / DEV_BSIZE)) == 0) {
546 		start = findopenbsd_gpt(bv, &err);
547 		if (start == (u_int)-1) {
548 			if (err != NULL)
549 				return (err);
550 			return "no OpenBSD partition\n";
551 		}
552 	} else if (mbr.dmbr_sign == DOSMBR_SIGNATURE) {
553 
554 		/* Search for OpenBSD partition */
555 		for (i = 0; i < NDOSPART; i++) {
556 			dp = &mbr.dmbr_parts[i];
557 			if (!dp->dp_size)
558 				continue;
559 			if (dp->dp_typ == DOSPTYP_OPENBSD) {
560 				start = dp->dp_start;
561 				break;
562 			}
563 		}
564 	}
565 
566 	/* Read the disklabel. */
567 	sr_strategy(bv, F_READ,
568 	    start * (bv->sbv_secsize / DEV_BSIZE) + DOS_LABELSECTOR,
569 	    sizeof(struct disklabel), buf, NULL);
570 
571 #ifdef BIOS_DEBUG
572 	printf("sr_getdisklabel: magic %lx\n",
573 	    ((struct disklabel *)buf)->d_magic);
574 	for (i = 0; i < MAXPARTITIONS; i++)
575 		printf("part %c: type = %d, size = %d, offset = %d\n", 'a' + i,
576 		    (int)((struct disklabel *)buf)->d_partitions[i].p_fstype,
577 		    (int)((struct disklabel *)buf)->d_partitions[i].p_size,
578 		    (int)((struct disklabel *)buf)->d_partitions[i].p_offset);
579 #endif
580 
581 	/* Fill in disklabel */
582 	return (getdisklabel(buf, label));
583 }
584 
585 int
586 sropen(struct open_file *f, ...)
587 {
588 	struct diskinfo *dip = NULL;
589 	struct sr_boot_volume *bv;
590 	va_list ap;
591 	u_int unit, part;
592 
593 	va_start(ap, f);
594 	unit = va_arg(ap, u_int);
595 	part = va_arg(ap, u_int);
596 	va_end(ap);
597 
598 	/* Create a fake diskinfo for this softraid volume. */
599 	SLIST_FOREACH(bv, &sr_volumes, sbv_link)
600 		if (bv->sbv_unit == unit)
601 			break;
602 	if (bv == NULL) {
603 		printf("Unknown device: sr%d\n", unit);
604 		return EADAPT;
605 	}
606 
607 	if (bv->sbv_level == 'C' && bv->sbv_keys == NULL)
608 		if (sr_crypto_unlock_volume(bv) != 0)
609 			return EPERM;
610 
611 	if (bv->sbv_diskinfo == NULL) {
612 		dip = alloc(sizeof(struct diskinfo));
613 		bzero(dip, sizeof(*dip));
614 		dip->diskio = srdiskio;
615 		dip->strategy = srstrategy;
616 		bv->sbv_diskinfo = dip;
617 		dip->sr_vol = bv;
618 	}
619 
620 	dip = bv->sbv_diskinfo;
621 
622 	if ((dip->flags & DISKINFO_FLAG_GOODLABEL) == 0) {
623 		/* Attempt to read disklabel. */
624 		bv->sbv_part = 'c';
625 		if (sr_getdisklabel(bv, &dip->disklabel))
626 			return ERDLAB;
627 		dip->flags |= DISKINFO_FLAG_GOODLABEL;
628 	}
629 
630 	bv->sbv_part = part + 'a';
631 
632 	bootdev_dip = dip;
633 	f->f_devdata = dip;
634 
635 	return 0;
636 }
637 
638 int
639 srstrategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
640     size_t *rsize)
641 {
642 	struct diskinfo *dip = (struct diskinfo *)devdata;
643 	return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
644 }
645 
646 int
647 srdiskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf)
648 {
649 	return dip->diskio(rw, dip, off, nsect, buf);
650 }
651 
652 int
653 srclose(struct open_file *f)
654 {
655 	f->f_devdata = NULL;
656 
657 	return 0;
658 }
659 
660 int
661 srioctl(struct open_file *f, u_long cmd, void *data)
662 {
663 	return 0;
664 }
665