1 /*	$OpenBSD: softraid_sparc64.c,v 1.5 2020/12/09 18:10:19 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 			if (bv->sbv_chunk_no == bv->sbv_chunks_found)
294 				bv->sbv_state = BIOC_SVONLINE;
295 			else if (bv->sbv_chunks_found > 0)
296 				bv->sbv_state = BIOC_SVDEGRADED;
297 			break;
298 		}
299 
300 		bv->sbv_unit = volno++;
301 		if (bv->sbv_state != BIOC_SVOFFLINE)
302 			printf("sr%d%s\n", bv->sbv_unit,
303 			    bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : "");
304 	}
305 
306 	explicit_bzero(md, SR_META_SIZE * DEV_BSIZE);
307 	free(md, SR_META_SIZE * DEV_BSIZE);
308 }
309 
310 struct sr_boot_chunk *
311 sr_vol_boot_chunk(struct sr_boot_volume *bv)
312 {
313 	struct sr_boot_chunk *bc = NULL;
314 
315 	if (bv->sbv_level == 1 || bv->sbv_level == 'C' ) { /* RAID1 or CRYPTO */
316 		/* Select first online chunk. */
317 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
318 			if (bc->sbc_state == BIOC_SDONLINE)
319 				break;
320 	}
321 
322 	return bc;
323 }
324 
325 
326 int
327 sr_strategy(struct sr_boot_volume *bv, int sr_handle, int rw, daddr_t blk,
328     size_t size, void *buf, size_t *rsize)
329 {
330 	struct diskinfo *sr_dip, *dip;
331 	struct partition *pp;
332 	struct sr_boot_chunk *bc;
333 	struct aes_xts_ctx ctx;
334 	struct of_dev ofdev;
335 	size_t i, j, nsect;
336 	daddr_t blkno;
337 	u_char iv[8];
338 	u_char *bp;
339 	int err;
340 
341 	/* We only support read-only softraid. */
342 	if (rw != F_READ)
343 		return ENOTSUP;
344 
345 	/* Partition offset within softraid volume. */
346 	sr_dip = (struct diskinfo *)bv->sbv_diskinfo;
347 	blk +=
348 	    DL_GETPOFFSET(&sr_dip->disklabel.d_partitions[bv->sbv_part - 'a']);
349 
350 	bc = sr_vol_boot_chunk(bv);
351 	if (bc == NULL)
352 		return ENXIO;
353 
354 	dip = (struct diskinfo *)bc->sbc_diskinfo;
355 	pp = &dip->disklabel.d_partitions[bc->sbc_part - 'a'];
356 	bzero(&ofdev, sizeof(ofdev));
357 	ofdev.handle = sr_handle;
358 	ofdev.type = OFDEV_DISK;
359 	ofdev.bsize = DEV_BSIZE;
360 	ofdev.partoff = DL_GETPOFFSET(pp);
361 
362 	if (bv->sbv_level == 0) {
363 		return ENOTSUP;
364 	} else if (bv->sbv_level == 1) {
365 		blk += bv->sbv_data_blkno;
366 
367 		/* XXX - If I/O failed we should try another chunk... */
368 		err = strategy(&ofdev, rw, blk, size, buf, rsize);
369 		return err;
370 
371 	} else if (bv->sbv_level == 'C') {
372 		/* XXX - select correct key. */
373 		aes_xts_setkey(&ctx, (u_char *)bv->sbv_keys, 64);
374 
375 		nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
376 		for (i = 0; i < nsect; i++) {
377 			blkno = blk + i;
378 			bp = ((u_char *)buf) + i * DEV_BSIZE;
379 
380 			err = strategy(&ofdev, rw,
381 			    bv->sbv_data_blkno + blkno,
382 			    DEV_BSIZE, bp, rsize);
383 			if (err != 0 || *rsize != DEV_BSIZE) {
384 				printf("Read from crypto volume failed "
385 				    "(read %d bytes): %s\n", *rsize,
386 				    strerror(err));
387 				return err;
388 			}
389 			bcopy(&blkno, iv, sizeof(blkno));
390 			aes_xts_reinit(&ctx, iv);
391 			for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE)
392 				aes_xts_decrypt(&ctx, bp + j);
393 		}
394 		*rsize = nsect * DEV_BSIZE;
395 		return err;
396 
397 	} else
398 		return ENOTSUP;
399 }
400 
401 const char *
402 sr_getdisklabel(struct sr_boot_volume *bv, struct disklabel *label)
403 {
404 	struct of_dev ofdev;
405 	int err;
406 #ifdef DEBUG
407 	int i;
408 #endif
409 
410 	bzero(&ofdev, sizeof ofdev);
411 	ofdev.type = OFDEV_SOFTRAID;
412 
413 	if (load_disklabel(&ofdev, label))
414 		return ("Could not read disklabel from softraid");
415 #ifdef DEBUG
416 	printf("sr_getdisklabel: magic %lx\n", label->d_magic);
417 	for (i = 0; i < MAXPARTITIONS; i++)
418 		printf("part %c: type = %d, size = %d, offset = %d\n", 'a' + i,
419 		    (int)label->d_partitions[i].p_fstype,
420 		    (int)label->d_partitions[i].p_size,
421 		    (int)label->d_partitions[i].p_offset);
422 #endif
423 
424 	return (NULL);
425 }
426