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
srprobe_meta_opt_load(struct sr_metadata * sm,struct sr_meta_opt_head * som)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
srprobe_keydisk_load(struct sr_metadata * sm)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
srprobe(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 *
sr_vol_boot_chunk(struct sr_boot_volume * bv)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
sr_strategy(struct sr_boot_volume * bv,int sr_handle,int rw,daddr_t blk,size_t size,void * buf,size_t * rsize)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 *
sr_getdisklabel(struct sr_boot_volume * bv,struct disklabel * label)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