xref: /openbsd-src/sys/arch/sparc64/stand/ofwboot/softraid_sparc64.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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