> Date: Wed, 30 Jan 2019 22:31:31 +0100
> From: Patrick Wildt <[email protected]>
>
> Hi,
>
> to boot a pinebook with softraid crypto there are three parts that need
> to be implemented, which this diff hopefully does well enough.
>
> First, our EFI bootloader so far only supports one disk, so we need to
> extend this so we can see all connected block devices. The second part
> is adding the softraid code, which probes for softraid partitions on the
> block devices and "spawns" its own block device. At last, we need to
> push the softraid uuid and key up to the kernel.
>
> Pushing the informaton to the kernel is the easiest part. We can add
> another openbsd-specific property in the device tree which we read out
> early on, so that we can zero the space in the device tree so it can not
> be read by anyone else.
>
> For the other part there's a bit more to do. Very early on efiboot
> looks for the boot device. While doing that, we create a list of block
> devices. While there, already try to read the disklabel because after
> this the softraid probing needs to know the partition table.
>
> We then call srprobe() which will go over the disklist to look for a
> disk that contains RAID volumes, but calling the disk's diskio and
> strategy function to read from the device. This creates the sr_volumes
> list of softraid volumes. The MI boot code will then look for the boot
> device name by calling devboot(). If we don't have the device path for
> the boot device, it's a PXE boot. Otherwise we will find it in either
> the disklist or sr_volumes list.
>
> The next part is the MI code opening the device, looking for a file-
> system. The boot device name now is e.g. sr0a, which will then be
> looked up in the devsw[] array. From there it's straight forward.
> Softraid maps the unit number to a volume, the volume already has a
> pointer to the diskinfo structure from the actual device and then
> calls the IO functions of that device.
>
> softraid_arm64.[ch] is copied from amd64 and adjusted a little bit
> for arm64, including adding the small devsw[] abstraction.
>
> With this I can successfully boot from softraid crypto on bluhm@'s
> Pinebook.
>
> Feedback?
Apart from the property names, this looks good to me. So feel free to
go ahead once you changed those properties.
> diff --git sys/arch/arm64/arm64/machdep.c sys/arch/arm64/arm64/machdep.c
> index 45f6451066a..03bc8464f3a 100644
> --- sys/arch/arm64/arm64/machdep.c
> +++ sys/arch/arm64/arm64/machdep.c
> @@ -30,6 +30,7 @@
> #include <sys/msgbuf.h>
> #include <sys/buf.h>
> #include <sys/termios.h>
> +#include <sys/sensors.h>
>
> #include <net/if.h>
> #include <uvm/uvm.h>
> @@ -49,6 +50,11 @@
>
> #include <dev/acpi/efi.h>
>
> +#include "softraid.h"
> +#if NSOFTRAID > 0
> +#include <dev/softraidvar.h>
> +#endif
> +
> char *boot_args = NULL;
> char *boot_file = "";
>
> @@ -826,6 +832,22 @@ initarm(struct arm64_bootparams *abp)
> bootmac = lladdr;
> }
>
> + len = fdt_node_property(node, "openbsd,bootsr-uuid", &prop);
> +#if NSOFTRAID > 0
> + if (len == sizeof(sr_bootuuid))
> + memcpy(&sr_bootuuid, prop, sizeof(sr_bootuuid));
> +#endif
> + if (len > 0)
> + explicit_bzero(prop, len);
> +
> + len = fdt_node_property(node, "openbsd,bootsr-maskkey", &prop);
> +#if NSOFTRAID > 0
> + if (len == sizeof(sr_bootkey))
> + memcpy(&sr_bootkey, prop, sizeof(sr_bootkey));
> +#endif
> + if (len > 0)
> + explicit_bzero(prop, len);
> +
> len = fdt_node_property(node, "openbsd,uefi-mmap-start", &prop);
> if (len == sizeof(mmap_start))
> mmap_start = bemtoh64((uint64_t *)prop);
> diff --git sys/arch/arm64/stand/efiboot/Makefile
> sys/arch/arm64/stand/efiboot/Makefile
> index 36eecd6ca9c..d0ec46662ee 100644
> --- sys/arch/arm64/stand/efiboot/Makefile
> +++ sys/arch/arm64/stand/efiboot/Makefile
> @@ -10,6 +10,7 @@ INSTALL_STRIP=
> BINDIR= /usr/mdec
> SRCS= start.S self_reloc.c efiboot.c conf.c exec.c efiacpi.c
> SRCS+= efidev.c efipxe.c efirng.c fdt.c dt_blob.S
> +SRCS+= softraid_arm64.c
>
> S= ${.CURDIR}/../../../..
> EFIDIR= ${S}/stand/efi
> @@ -31,6 +32,8 @@ SRCS+= close.c closeall.c cons.c cread.c dev.c
> disklabel.c dkcksum.c fstat.c \
> SRCS+= loadfile.c
> SRCS+= ufs.c
> SRCS+= arp.c ether.c globals.c in_cksum.c net.c netif.c netudp.c tftp.c
> +SRCS+= aes_xts.c bcrypt_pbkdf.c blowfish.c explicit_bzero.c
> hmac_sha1.c \
> + pkcs5_pbkdf2.c rijndael.c sha1.c sha2.c softraid.c
>
> .PATH: ${S}/lib/libkern/arch/arm64 ${S}/lib/libkern
> SRCS+= divdi3.c moddi3.c qdivrem.c strlcpy.c strlen.c
> diff --git sys/arch/arm64/stand/efiboot/conf.c
> sys/arch/arm64/stand/efiboot/conf.c
> index 5e8fd5fa0e7..be301b4a9d4 100644
> --- sys/arch/arm64/stand/efiboot/conf.c
> +++ sys/arch/arm64/stand/efiboot/conf.c
> @@ -27,14 +27,23 @@
> */
>
> #include <sys/param.h>
> +#include <sys/queue.h>
> +#include <sys/disklabel.h>
> #include <lib/libsa/stand.h>
> #include <lib/libsa/tftp.h>
> #include <lib/libsa/ufs.h>
> #include <dev/cons.h>
>
> +#include <dev/biovar.h>
> +#include <dev/softraidvar.h>
> +
> +#include <efi.h>
> +
> +#include "disk.h"
> #include "efiboot.h"
> #include "efidev.h"
> #include "efipxe.h"
> +#include "softraid_arm64.h"
>
> const char version[] = "0.13";
> int debug = 0;
> @@ -52,6 +61,7 @@ int nfsys = nitems(file_system);
> struct devsw devsw[] = {
> { "tftp", tftpstrategy, tftpopen, tftpclose, tftpioctl },
> { "sd", efistrategy, efiopen, eficlose, efiioctl },
> + { "sr", srstrategy, sropen, srclose, srioctl },
> };
> int ndevs = nitems(devsw);
>
> diff --git sys/arch/arm64/stand/efiboot/disk.h
> sys/arch/arm64/stand/efiboot/disk.h
> index 4007259da32..2a6bf3db93f 100644
> --- sys/arch/arm64/stand/efiboot/disk.h
> +++ sys/arch/arm64/stand/efiboot/disk.h
> @@ -3,6 +3,8 @@
> #ifndef _DISK_H
> #define _DISK_H
>
> +#include <sys/queue.h>
> +
> typedef struct efi_diskinfo {
> EFI_BLOCK_IO *blkio;
> UINT32 mediaid;
> @@ -11,10 +13,21 @@ typedef struct efi_diskinfo {
> struct diskinfo {
> struct efi_diskinfo ed;
> struct disklabel disklabel;
> + struct sr_boot_volume *sr_vol;
> +
> + u_int part;
> + u_int flags;
> +#define DISKINFO_FLAG_GOODLABEL (1 << 0)
>
> - u_int sc_part;
> + int (*diskio)(int, struct diskinfo *, u_int, int, void *);
> + int (*strategy)(void *, int, daddr32_t, size_t, void *, size_t *);
> +
> + TAILQ_ENTRY(diskinfo) list;
> };
> +TAILQ_HEAD(disklist_lh, diskinfo);
> +
> +extern struct diskinfo *bootdev_dip;
>
> -extern struct diskinfo diskinfo;
> +extern struct disklist_lh disklist;
>
> #endif /* _DISK_H */
> diff --git sys/arch/arm64/stand/efiboot/efiboot.c
> sys/arch/arm64/stand/efiboot/efiboot.c
> index 4dc9d7e32fa..f3045639d91 100644
> --- sys/arch/arm64/stand/efiboot/efiboot.c
> +++ sys/arch/arm64/stand/efiboot/efiboot.c
> @@ -28,14 +28,21 @@
> #include <efiprot.h>
> #include <eficonsctl.h>
>
> +#include <dev/biovar.h>
> +#include <dev/softraidvar.h>
> +
> #include <lib/libkern/libkern.h>
> +#include <lib/libsa/softraid.h>
> #include <stand/boot/cmd.h>
>
> +#include "libsa.h"
> #include "disk.h"
> +#include "softraid_arm64.h"
> +
> +#include "efidev.h"
> #include "efiboot.h"
> #include "eficall.h"
> #include "fdt.h"
> -#include "libsa.h"
>
> EFI_SYSTEM_TABLE *ST;
> EFI_BOOT_SERVICES *BS;
> @@ -173,19 +180,23 @@ efi_heap_init(void)
> panic("BS->AllocatePages()");
> }
>
> -EFI_BLOCK_IO *disk;
> +struct disklist_lh disklist;
> +struct diskinfo *bootdev_dip;
>
> void
> efi_diskprobe(void)
> {
> - int i, depth = -1;
> + int i, bootdev = 0, depth = -1;
> UINTN sz;
> EFI_STATUS status;
> EFI_HANDLE *handles = NULL;
> EFI_BLOCK_IO *blkio;
> EFI_BLOCK_IO_MEDIA *media;
> + struct diskinfo *di;
> EFI_DEVICE_PATH *dp;
>
> + TAILQ_INIT(&disklist);
> +
> sz = 0;
> status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 0, &sz, 0);
> if (status == EFI_BUFFER_TOO_SMALL) {
> @@ -217,20 +228,36 @@ efi_diskprobe(void)
> media = blkio->Media;
> if (media->LogicalPartition || !media->MediaPresent)
> continue;
> + di = alloc(sizeof(struct diskinfo));
> + efid_init(di, blkio);
>
> - if (efi_bootdp == NULL || depth == -1)
> - continue;
> + if (efi_bootdp == NULL || depth == -1 || bootdev != 0)
> + goto next;
> status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid,
> (void **)&dp);
> if (EFI_ERROR(status))
> - continue;
> + goto next;
> if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
> - disk = blkio;
> - break;
> + TAILQ_INSERT_HEAD(&disklist, di, list);
> + bootdev_dip = di;
> + bootdev = 1;
> + continue;
> }
> +next:
> + TAILQ_INSERT_TAIL(&disklist, di, list);
> }
>
> free(handles, sz);
> +
> + /* Print available disks and probe for softraid. */
> + i = 0;
> + printf("disks:");
> + TAILQ_FOREACH(di, &disklist, list) {
> + printf(" sd%d%s", i, di == bootdev_dip ? "*" : "");
> + i++;
> + }
> + srprobe();
> + printf("\n");
> }
>
> /*
> @@ -370,6 +397,7 @@ static EFI_GUID fdt_guid = FDT_TABLE_GUID;
> void *
> efi_makebootargs(char *bootargs)
> {
> + struct sr_boot_volume *bv;
> u_char bootduid[8];
> u_char zero[8] = { 0 };
> uint64_t uefi_system_table = htobe64((uintptr_t)ST);
> @@ -399,12 +427,27 @@ efi_makebootargs(char *bootargs)
> fdt_node_add_property(node, "bootargs", bootargs, len);
>
> /* Pass DUID of the boot disk. */
> - memcpy(&bootduid, diskinfo.disklabel.d_uid, sizeof(bootduid));
> - if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) {
> - fdt_node_add_property(node, "openbsd,bootduid", bootduid,
> + if (bootdev_dip) {
> + memcpy(&bootduid, bootdev_dip->disklabel.d_uid,
> sizeof(bootduid));
> + if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) {
> + fdt_node_add_property(node, "openbsd,bootduid",
> + bootduid, sizeof(bootduid));
> + }
> +
> + if (bootdev_dip->sr_vol != NULL) {
> + bv = bootdev_dip->sr_vol;
> + fdt_node_add_property(node, "openbsd,bootsr-uuid",
> + &bv->sbv_uuid, sizeof(bv->sbv_uuid));
> + if (bv->sbv_maskkey != NULL)
> + fdt_node_add_property(node,
> + "openbsd,bootsr-maskkey", bv->sbv_maskkey,
> + SR_CRYPTO_MAXKEYBYTES);
> + }
> }
>
> + sr_clear_keys();
> +
> /* Pass netboot interface address. */
> if (bootmac)
> fdt_node_add_property(node, "openbsd,bootmac", bootmac, 6);
> @@ -558,10 +601,51 @@ getsecs(void)
> void
> devboot(dev_t dev, char *p)
> {
> - if (disk)
> - strlcpy(p, "sd0a", 5);
> - else
> + struct sr_boot_volume *bv;
> + struct sr_boot_chunk *bc;
> + struct diskinfo *dip;
> + int sd_boot_vol = 0;
> + int sr_boot_vol = -1;
> + int part_type = FS_UNUSED;
> +
> + if (bootdev_dip == NULL) {
> strlcpy(p, "tftp0a", 7);
> + return;
> + }
> +
> + TAILQ_FOREACH(dip, &disklist, list) {
> + if (bootdev_dip == dip)
> + break;
> + sd_boot_vol++;
> + }
> +
> + /*
> + * Determine the partition type for the 'a' partition of the
> + * boot device.
> + */
> + if ((bootdev_dip->flags & DISKINFO_FLAG_GOODLABEL) != 0)
> + part_type = bootdev_dip->disklabel.d_partitions[0].p_fstype;
> +
> + /*
> + * See if we booted from a disk that is a member of a bootable
> + * softraid volume.
> + */
> + SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
> + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
> + if (bc->sbc_diskinfo == bootdev_dip)
> + sr_boot_vol = bv->sbv_unit;
> + if (sr_boot_vol != -1)
> + break;
> + }
> +
> + if (sr_boot_vol != -1 && part_type != FS_BSDFFS) {
> + strlcpy(p, "sr0a", 5);
> + p[2] = '0' + sr_boot_vol;
> + return;
> + }
> +
> + strlcpy(p, "sd0a", 5);
> + p[2] = '0' + sd_boot_vol;
> }
>
> int
> diff --git sys/arch/arm64/stand/efiboot/efidev.c
> sys/arch/arm64/stand/efiboot/efidev.c
> index 91c1ce5f90d..3cd4fac874b 100644
> --- sys/arch/arm64/stand/efiboot/efidev.c
> +++ sys/arch/arm64/stand/efiboot/efidev.c
> @@ -48,17 +48,30 @@ extern int debug;
> #define EFI_BLKSPERSEC(_ed) ((_ed)->blkio->Media->BlockSize / DEV_BSIZE)
> #define EFI_SECTOBLK(_ed, _n) ((_n) * EFI_BLKSPERSEC(_ed))
>
> -extern EFI_BLOCK_IO *disk;
> -struct diskinfo diskinfo;
> -
> static EFI_STATUS
> efid_io(int, efi_diskinfo_t, u_int, int, void *);
> static int efid_diskio(int, struct diskinfo *, u_int, int, void *);
> +const char * efi_getdisklabel(efi_diskinfo_t, struct disklabel *);
> static int efi_getdisklabel_cd9660(efi_diskinfo_t, struct disklabel *);
> static u_int findopenbsd(efi_diskinfo_t, const char **);
> static u_int findopenbsd_gpt(efi_diskinfo_t, const char **);
> static int gpt_chk_mbr(struct dos_partition *, u_int64_t);
>
> +void
> +efid_init(struct diskinfo *dip, void *handle)
> +{
> + EFI_BLOCK_IO *blkio = handle;
> +
> + memset(dip, 0, sizeof(struct diskinfo));
> + dip->ed.blkio = blkio;
> + dip->ed.mediaid = blkio->Media->MediaId;
> + dip->diskio = efid_diskio;
> + dip->strategy = efistrategy;
> +
> + if (efi_getdisklabel(&dip->ed, &dip->disklabel) == NULL)
> + dip->flags |= DISKINFO_FLAG_GOODLABEL;
> +}
> +
> static EFI_STATUS
> efid_io(int rw, efi_diskinfo_t ed, u_int off, int nsect, void *buf)
> {
> @@ -471,32 +484,33 @@ efi_getdisklabel_cd9660(efi_diskinfo_t ed, struct
> disklabel *label)
> int
> efiopen(struct open_file *f, ...)
> {
> - struct diskinfo *dip = &diskinfo;
> + struct diskinfo *dip = NULL;
> va_list ap;
> u_int unit, part;
> - const char *err;
> -
> - if (disk == NULL)
> - return (ENXIO);
> + int i = 0;
>
> va_start(ap, f);
> unit = va_arg(ap, u_int);
> part = va_arg(ap, u_int);
> va_end(ap);
>
> - if (unit != 0 || part >= MAXPARTITIONS)
> + if (part >= MAXPARTITIONS)
> return (ENXIO);
>
> - diskinfo.ed.blkio = disk;
> - diskinfo.ed.mediaid = disk->Media->MediaId;
> - diskinfo.sc_part = part;
> -
> - err = efi_getdisklabel(&dip->ed, &dip->disklabel);
> - if (err) {
> - printf("%s\n", err);
> - return EINVAL;
> + TAILQ_FOREACH(dip, &disklist, list) {
> + if (i == unit)
> + break;
> + i++;
> }
>
> + if (dip == NULL)
> + return (ENXIO);
> +
> + if ((dip->flags & DISKINFO_FLAG_GOODLABEL) == 0)
> + return (ENXIO);
> +
> + dip->part = part;
> + bootdev_dip = dip;
> f->f_devdata = dip;
>
> return 0;
> @@ -512,7 +526,7 @@ efistrategy(void *devdata, int rw, daddr32_t blk, size_t
> size, void *buf,
>
> nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
> blk += DL_SECTOBLK(&dip->disklabel,
> - dip->disklabel.d_partitions[dip->sc_part].p_offset);
> + dip->disklabel.d_partitions[dip->part].p_offset);
>
> if (blk < 0)
> error = EINVAL;
> diff --git sys/arch/arm64/stand/efiboot/efidev.h
> sys/arch/arm64/stand/efiboot/efidev.h
> index a771bfcc1f7..95a09463ab5 100644
> --- sys/arch/arm64/stand/efiboot/efidev.h
> +++ sys/arch/arm64/stand/efiboot/efidev.h
> @@ -29,6 +29,7 @@
> */
>
> /* efidev.c */
> +void efid_init(struct diskinfo *, void *handle);
> int efiopen(struct open_file *, ...);
> int efistrategy(void *, int, daddr32_t, size_t, void *, size_t *);
> int eficlose(struct open_file *);
> diff --git sys/arch/arm64/stand/efiboot/softraid_arm64.c
> sys/arch/arm64/stand/efiboot/softraid_arm64.c
> new file mode 100644
> index 00000000000..693198d2357
> --- /dev/null
> +++ sys/arch/arm64/stand/efiboot/softraid_arm64.c
> @@ -0,0 +1,665 @@
> +/* $OpenBSD: softraid_amd64.c,v 1.5 2017/12/18 12:53:33 fcambus Exp $
> */
> +
> +/*
> + * Copyright (c) 2012 Joel Sing <[email protected]>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/param.h>
> +#include <sys/queue.h>
> +#include <sys/disklabel.h>
> +#include <sys/reboot.h>
> +
> +#include <dev/biovar.h>
> +#include <dev/softraidvar.h>
> +
> +#include <lib/libsa/aes_xts.h>
> +#include <lib/libsa/softraid.h>
> +#include <lib/libz/zlib.h>
> +
> +#include <efi.h>
> +
> +#include "libsa.h"
> +#include "disk.h"
> +#include "efidev.h"
> +#include "softraid_arm64.h"
> +
> +static int gpt_chk_mbr(struct dos_partition *, u_int64_t);
> +static uint64_t findopenbsd_gpt(struct sr_boot_volume *, const char **);
> +
> +void
> +srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som)
> +{
> + struct sr_meta_opt_hdr *omh;
> + struct sr_meta_opt_item *omi;
> +#if 0
> + u_int8_t checksum[MD5_DIGEST_LENGTH];
> +#endif
> + int i;
> +
> + /* Process optional metadata. */
> + omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) +
> + sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no);
> + for (i = 0; i < sm->ssdi.ssd_opt_no; i++) {
> +
> +#ifdef BIOS_DEBUG
> + printf("Found optional metadata of type %u, length %u\n",
> + omh->som_type, omh->som_length);
> +#endif
> +
> + /* Unsupported old fixed length optional metadata. */
> + if (omh->som_length == 0) {
> + omh = (struct sr_meta_opt_hdr *)((void *)omh +
> + SR_OLD_META_OPT_SIZE);
> + continue;
> + }
> +
> + /* Load variable length optional metadata. */
> + omi = alloc(sizeof(struct sr_meta_opt_item));
> + bzero(omi, sizeof(struct sr_meta_opt_item));
> + SLIST_INSERT_HEAD(som, omi, omi_link);
> + omi->omi_som = alloc(omh->som_length);
> + bzero(omi->omi_som, omh->som_length);
> + bcopy(omh, omi->omi_som, omh->som_length);
> +
> +#if 0
> + /* XXX - Validate checksum. */
> + bcopy(&omi->omi_som->som_checksum, &checksum,
> + MD5_DIGEST_LENGTH);
> + bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH);
> + sr_checksum(sc, omi->omi_som,
> + &omi->omi_som->som_checksum, omh->som_length);
> + if (bcmp(&checksum, &omi->omi_som->som_checksum,
> + sizeof(checksum)))
> + panic("%s: invalid optional metadata checksum",
> + DEVNAME(sc));
> +#endif
> +
> + omh = (struct sr_meta_opt_hdr *)((void *)omh +
> + omh->som_length);
> + }
> +}
> +
> +void
> +srprobe_keydisk_load(struct sr_metadata *sm)
> +{
> + struct sr_meta_opt_hdr *omh;
> + struct sr_meta_keydisk *skm;
> + struct sr_boot_keydisk *kd;
> + int i;
> +
> + /* Process optional metadata. */
> + omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) +
> + sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no);
> + for (i = 0; i < sm->ssdi.ssd_opt_no; i++) {
> +
> + /* Unsupported old fixed length optional metadata. */
> + if (omh->som_length == 0) {
> + omh = (struct sr_meta_opt_hdr *)((void *)omh +
> + SR_OLD_META_OPT_SIZE);
> + continue;
> + }
> +
> + if (omh->som_type != SR_OPT_KEYDISK) {
> + omh = (struct sr_meta_opt_hdr *)((void *)omh +
> + omh->som_length);
> + continue;
> + }
> +
> + kd = alloc(sizeof(struct sr_boot_keydisk));
> + bcopy(&sm->ssdi.ssd_uuid, &kd->kd_uuid, sizeof(kd->kd_uuid));
> + skm = (struct sr_meta_keydisk*)omh;
> + bcopy(&skm->skm_maskkey, &kd->kd_key, sizeof(kd->kd_key));
> + SLIST_INSERT_HEAD(&sr_keydisks, kd, kd_link);
> + }
> +}
> +
> +void
> +srprobe(void)
> +{
> + struct sr_boot_volume *bv, *bv1, *bv2;
> + struct sr_boot_chunk *bc, *bc1, *bc2;
> + struct sr_meta_chunk *mc;
> + struct sr_metadata *md;
> + struct diskinfo *dip;
> + struct partition *pp;
> + int i, error, volno;
> + daddr_t off;
> +
> + /* Probe for softraid volumes. */
> + SLIST_INIT(&sr_volumes);
> + SLIST_INIT(&sr_keydisks);
> +
> + md = alloc(SR_META_SIZE * DEV_BSIZE);
> +
> + TAILQ_FOREACH(dip, &disklist, list) {
> +
> + /* Make sure disklabel has been read. */
> + if ((dip->flags & DISKINFO_FLAG_GOODLABEL) == 0)
> + continue;
> +
> + for (i = 0; i < MAXPARTITIONS; i++) {
> +
> + pp = &dip->disklabel.d_partitions[i];
> + if (pp->p_fstype != FS_RAID || pp->p_size == 0)
> + continue;
> +
> + /* Read softraid metadata. */
> + bzero(md, SR_META_SIZE * DEV_BSIZE);
> + off = DL_SECTOBLK(&dip->disklabel, DL_GETPOFFSET(pp));
> + off += SR_META_OFFSET;
> + error = dip->diskio(F_READ, dip, off, SR_META_SIZE, md);
> + if (error)
> + continue;
> +
> + /* Is this valid softraid metadata? */
> + if (md->ssdi.ssd_magic != SR_MAGIC)
> + continue;
> +
> + /* XXX - validate checksum. */
> +
> + /* Handle key disks separately... */
> + if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) {
> + srprobe_keydisk_load(md);
> + continue;
> + }
> +
> + /* Locate chunk-specific metadata for this chunk. */
> + mc = (struct sr_meta_chunk *)(md + 1);
> + mc += md->ssdi.ssd_chunk_id;
> +
> + bc = alloc(sizeof(struct sr_boot_chunk));
> + bc->sbc_diskinfo = dip;
> + bc->sbc_disk = 0;
> + bc->sbc_part = 'a' + i;
> +
> + bc->sbc_mm = 0;
> +
> + bc->sbc_chunk_id = md->ssdi.ssd_chunk_id;
> + bc->sbc_ondisk = md->ssd_ondisk;
> + bc->sbc_state = mc->scm_status;
> +
> + SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
> + if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
> + sizeof(md->ssdi.ssd_uuid)) == 0)
> + break;
> + }
> +
> + if (bv == NULL) {
> + bv = alloc(sizeof(struct sr_boot_volume));
> + bzero(bv, sizeof(struct sr_boot_volume));
> + bv->sbv_level = md->ssdi.ssd_level;
> + bv->sbv_volid = md->ssdi.ssd_volid;
> + bv->sbv_chunk_no = md->ssdi.ssd_chunk_no;
> + bv->sbv_flags = md->ssdi.ssd_vol_flags;
> + bv->sbv_size = md->ssdi.ssd_size;
> + bv->sbv_secsize = md->ssdi.ssd_secsize;
> + bv->sbv_data_blkno = md->ssd_data_blkno;
> + bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
> + sizeof(md->ssdi.ssd_uuid));
> + SLIST_INIT(&bv->sbv_chunks);
> + SLIST_INIT(&bv->sbv_meta_opt);
> +
> + /* Load optional metadata for this volume. */
> + srprobe_meta_opt_load(md, &bv->sbv_meta_opt);
> +
> + /* Maintain volume order. */
> + bv2 = NULL;
> + SLIST_FOREACH(bv1, &sr_volumes, sbv_link) {
> + if (bv1->sbv_volid > bv->sbv_volid)
> + break;
> + bv2 = bv1;
> + }
> + if (bv2 == NULL)
> + SLIST_INSERT_HEAD(&sr_volumes, bv,
> + sbv_link);
> + else
> + SLIST_INSERT_AFTER(bv2, bv, sbv_link);
> + }
> +
> + /* Maintain chunk order. */
> + bc2 = NULL;
> + SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) {
> + if (bc1->sbc_chunk_id > bc->sbc_chunk_id)
> + break;
> + bc2 = bc1;
> + }
> + if (bc2 == NULL)
> + SLIST_INSERT_HEAD(&bv->sbv_chunks,
> + bc, sbc_link);
> + else
> + SLIST_INSERT_AFTER(bc2, bc, sbc_link);
> +
> + bv->sbv_chunks_found++;
> + }
> + }
> +
> + /*
> + * Assemble RAID volumes.
> + */
> + volno = 0;
> + SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
> +
> + /* Skip if this is a hotspare "volume". */
> + if (bv->sbv_level == SR_HOTSPARE_LEVEL &&
> + bv->sbv_chunk_no == 1)
> + continue;
> +
> + /* Determine current ondisk version. */
> + bv->sbv_ondisk = 0;
> + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
> + if (bc->sbc_ondisk > bv->sbv_ondisk)
> + bv->sbv_ondisk = bc->sbc_ondisk;
> + }
> + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
> + if (bc->sbc_ondisk != bv->sbv_ondisk)
> + bc->sbc_state = BIOC_SDOFFLINE;
> + }
> +
> + /* XXX - Check for duplicate chunks. */
> +
> + /*
> + * Validate that volume has sufficient chunks for
> + * read-only access.
> + *
> + * XXX - check chunk states.
> + */
> + bv->sbv_state = BIOC_SVOFFLINE;
> + switch (bv->sbv_level) {
> + case 0:
> + case 'C':
> + case 'c':
> + if (bv->sbv_chunk_no == bv->sbv_chunks_found)
> + bv->sbv_state = BIOC_SVONLINE;
> + break;
> +
> + case 1:
> + if (bv->sbv_chunk_no == bv->sbv_chunks_found)
> + bv->sbv_state = BIOC_SVONLINE;
> + else if (bv->sbv_chunks_found > 0)
> + bv->sbv_state = BIOC_SVDEGRADED;
> + break;
> + }
> +
> + bv->sbv_unit = volno++;
> + if (bv->sbv_state != BIOC_SVOFFLINE)
> + printf(" sr%d%s", bv->sbv_unit,
> + bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : "");
> + }
> +
> + explicit_bzero(md, SR_META_SIZE * DEV_BSIZE);
> + free(md, SR_META_SIZE * DEV_BSIZE);
> +}
> +
> +int
> +sr_strategy(struct sr_boot_volume *bv, int rw, daddr32_t blk, size_t size,
> + void *buf, size_t *rsize)
> +{
> + struct diskinfo *sr_dip, *dip;
> + struct sr_boot_chunk *bc;
> + struct aes_xts_ctx ctx;
> + size_t i, j, nsect;
> + daddr_t blkno;
> + u_char iv[8];
> + u_char *bp;
> + int err;
> +
> + /* We only support read-only softraid. */
> + if (rw != F_READ)
> + return ENOTSUP;
> +
> + /* Partition offset within softraid volume. */
> + sr_dip = (struct diskinfo *)bv->sbv_diskinfo;
> + blk += DL_SECTOBLK(&sr_dip->disklabel,
> + sr_dip->disklabel.d_partitions[bv->sbv_part - 'a'].p_offset);
> +
> + if (bv->sbv_level == 0) {
> + return ENOTSUP;
> + } else if (bv->sbv_level == 1) {
> +
> + /* Select first online chunk. */
> + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
> + if (bc->sbc_state == BIOC_SDONLINE)
> + break;
> + if (bc == NULL)
> + return EIO;
> +
> + dip = (struct diskinfo *)bc->sbc_diskinfo;
> + blk += bv->sbv_data_blkno;
> +
> + /* XXX - If I/O failed we should try another chunk... */
> + return dip->strategy(dip, rw, blk, size, buf, rsize);
> +
> + } else if (bv->sbv_level == 'C') {
> +
> + /* Select first online chunk. */
> + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
> + if (bc->sbc_state == BIOC_SDONLINE)
> + break;
> + if (bc == NULL)
> + return EIO;
> +
> + dip = (struct diskinfo *)bc->sbc_diskinfo;
> +
> + /* XXX - select correct key. */
> + aes_xts_setkey(&ctx, (u_char *)bv->sbv_keys, 64);
> +
> + nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
> + for (i = 0; i < nsect; i++) {
> + blkno = blk + i;
> + bp = ((u_char *)buf) + i * DEV_BSIZE;
> + err = dip->strategy(dip, rw, bv->sbv_data_blkno + blkno,
> + DEV_BSIZE, bp, NULL);
> + if (err != 0)
> + return err;
> +
> + bcopy(&blkno, iv, sizeof(blkno));
> + aes_xts_reinit(&ctx, iv);
> + for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE)
> + aes_xts_decrypt(&ctx, bp + j);
> + }
> + if (rsize != NULL)
> + *rsize = nsect * DEV_BSIZE;
> +
> + return err;
> +
> + } else
> + return ENOTSUP;
> +}
> +
> +/*
> + * Returns 0 if the MBR with the provided partition array is a GPT protective
> + * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
> + * one MBR partition, an EFI partition that either covers the whole disk or
> as
> + * much of it as is possible with a 32bit size field.
> + *
> + * Taken from kern/subr_disk.c.
> + *
> + * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
> + */
> +static int
> +gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
> +{
> + struct dos_partition *dp2;
> + int efi, found, i;
> + u_int32_t psize;
> +
> + found = efi = 0;
> + for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
> + if (dp2->dp_typ == DOSPTYP_UNUSED)
> + continue;
> + found++;
> + if (dp2->dp_typ != DOSPTYP_EFI)
> + continue;
> + psize = letoh32(dp2->dp_size);
> + if (psize == (dsize - 1) ||
> + psize == UINT32_MAX) {
> + if (letoh32(dp2->dp_start) == 1)
> + efi++;
> + }
> + }
> + if (found == 1 && efi == 1)
> + return (0);
> +
> + return (1);
> +}
> +
> +static uint64_t
> +findopenbsd_gpt(struct sr_boot_volume *bv, const char **err)
> +{
> + struct gpt_header gh;
> + int i, part, found;
> + uint64_t lba;
> + uint32_t orig_csum, new_csum;
> + uint32_t ghsize, ghpartsize, ghpartnum, ghpartspersec;
> + uint32_t gpsectors;
> + const char openbsd_uuid_code[] = GPT_UUID_OPENBSD;
> + struct gpt_partition gp;
> + static struct uuid *openbsd_uuid = NULL, openbsd_uuid_space;
> + u_char *buf;
> +
> + /* Prepare OpenBSD UUID */
> + if (openbsd_uuid == NULL) {
> + /* XXX: should be replaced by uuid_dec_be() */
> + memcpy(&openbsd_uuid_space, openbsd_uuid_code,
> + sizeof(openbsd_uuid_space));
> + openbsd_uuid_space.time_low =
> + betoh32(openbsd_uuid_space.time_low);
> + openbsd_uuid_space.time_mid =
> + betoh16(openbsd_uuid_space.time_mid);
> + openbsd_uuid_space.time_hi_and_version =
> + betoh16(openbsd_uuid_space.time_hi_and_version);
> +
> + openbsd_uuid = &openbsd_uuid_space;
> + }
> +
> + if (bv->sbv_secsize > 4096) {
> + *err = "disk sector > 4096 bytes\n";
> + return (-1);
> + }
> + buf = alloc(bv->sbv_secsize);
> + if (buf == NULL) {
> + *err = "out of memory\n";
> + return (-1);
> + }
> + bzero(buf, bv->sbv_secsize);
> +
> + /* LBA1: GPT Header */
> + lba = 1;
> + sr_strategy(bv, F_READ, lba * (bv->sbv_secsize / DEV_BSIZE), DEV_BSIZE,
> + buf, NULL);
> + memcpy(&gh, buf, sizeof(gh));
> +
> + /* Check signature */
> + if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
> + *err = "bad GPT signature\n";
> + free(buf, bv->sbv_secsize);
> + return (-1);
> + }
> +
> + if (letoh32(gh.gh_rev) != GPTREVISION) {
> + *err = "bad GPT revision\n";
> + free(buf, bv->sbv_secsize);
> + return (-1);
> + }
> +
> + ghsize = letoh32(gh.gh_size);
> + if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) {
> + *err = "bad GPT header size\n";
> + free(buf, bv->sbv_secsize);
> + return (-1);
> + }
> +
> + /* Check checksum */
> + orig_csum = gh.gh_csum;
> + gh.gh_csum = 0;
> + new_csum = crc32(0, (unsigned char *)&gh, ghsize);
> + gh.gh_csum = orig_csum;
> + if (letoh32(orig_csum) != new_csum) {
> + *err = "bad GPT header checksum\n";
> + free(buf, bv->sbv_secsize);
> + return (-1);
> + }
> +
> + lba = letoh64(gh.gh_part_lba);
> + ghpartsize = letoh32(gh.gh_part_size);
> + ghpartspersec = bv->sbv_secsize / ghpartsize;
> + ghpartnum = letoh32(gh.gh_part_num);
> + gpsectors = (ghpartnum + ghpartspersec - 1) / ghpartspersec;
> + new_csum = crc32(0L, Z_NULL, 0);
> + found = 0;
> + for (i = 0; i < gpsectors; i++, lba++) {
> + sr_strategy(bv, F_READ, lba * (bv->sbv_secsize / DEV_BSIZE),
> + bv->sbv_secsize, buf, NULL);
> + for (part = 0; part < ghpartspersec; part++) {
> + if (ghpartnum == 0)
> + break;
> + new_csum = crc32(new_csum, buf + part * sizeof(gp),
> + sizeof(gp));
> + ghpartnum--;
> + if (found)
> + continue;
> + memcpy(&gp, buf + part * sizeof(gp), sizeof(gp));
> + if (memcmp(&gp.gp_type, openbsd_uuid,
> + sizeof(struct uuid)) == 0)
> + found = 1;
> + }
> + }
> +
> + free(buf, bv->sbv_secsize);
> +
> + if (new_csum != letoh32(gh.gh_part_csum)) {
> + *err = "bad GPT entries checksum\n";
> + return (-1);
> + }
> + if (found)
> + return (letoh64(gp.gp_lba_start));
> +
> + return (-1);
> +}
> +
> +const char *
> +sr_getdisklabel(struct sr_boot_volume *bv, struct disklabel *label)
> +{
> + struct dos_partition *dp;
> + struct dos_mbr mbr;
> + const char *err = NULL;
> + u_int start = 0;
> + char buf[DEV_BSIZE];
> + int i;
> +
> + /* Check for MBR to determine partition offset. */
> + bzero(&mbr, sizeof(mbr));
> + sr_strategy(bv, F_READ, DOSBBSECTOR, sizeof(mbr), &mbr, NULL);
> + if (gpt_chk_mbr(mbr.dmbr_parts, bv->sbv_size /
> + (bv->sbv_secsize / DEV_BSIZE)) == 0) {
> + start = findopenbsd_gpt(bv, &err);
> + if (start == (u_int)-1) {
> + if (err != NULL)
> + return (err);
> + return "no OpenBSD partition\n";
> + }
> + } else if (mbr.dmbr_sign == DOSMBR_SIGNATURE) {
> +
> + /* Search for OpenBSD partition */
> + for (i = 0; i < NDOSPART; i++) {
> + dp = &mbr.dmbr_parts[i];
> + if (!dp->dp_size)
> + continue;
> + if (dp->dp_typ == DOSPTYP_OPENBSD) {
> + start = dp->dp_start;
> + break;
> + }
> + }
> + }
> +
> + /* Read the disklabel. */
> + sr_strategy(bv, F_READ,
> + start * (bv->sbv_secsize / DEV_BSIZE) + DOS_LABELSECTOR,
> + sizeof(struct disklabel), buf, NULL);
> +
> +#ifdef BIOS_DEBUG
> + printf("sr_getdisklabel: magic %lx\n",
> + ((struct disklabel *)buf)->d_magic);
> + for (i = 0; i < MAXPARTITIONS; i++)
> + printf("part %c: type = %d, size = %d, offset = %d\n", 'a' + i,
> + (int)((struct disklabel *)buf)->d_partitions[i].p_fstype,
> + (int)((struct disklabel *)buf)->d_partitions[i].p_size,
> + (int)((struct disklabel *)buf)->d_partitions[i].p_offset);
> +#endif
> +
> + /* Fill in disklabel */
> + return (getdisklabel(buf, label));
> +}
> +
> +int
> +sropen(struct open_file *f, ...)
> +{
> + struct diskinfo *dip = NULL;
> + struct sr_boot_volume *bv;
> + va_list ap;
> + u_int unit, part;
> +
> + va_start(ap, f);
> + unit = va_arg(ap, u_int);
> + part = va_arg(ap, u_int);
> + va_end(ap);
> +
> + /* Create a fake diskinfo for this softraid volume. */
> + SLIST_FOREACH(bv, &sr_volumes, sbv_link)
> + if (bv->sbv_unit == unit)
> + break;
> + if (bv == NULL) {
> + printf("Unknown device: sr%d\n", unit);
> + return EADAPT;
> + }
> +
> + if (bv->sbv_level == 'C' && bv->sbv_keys == NULL)
> + if (sr_crypto_unlock_volume(bv) != 0)
> + return EPERM;
> +
> + if (bv->sbv_diskinfo == NULL) {
> + dip = alloc(sizeof(struct diskinfo));
> + bzero(dip, sizeof(*dip));
> + dip->diskio = srdiskio;
> + dip->strategy = srstrategy;
> + bv->sbv_diskinfo = dip;
> + dip->sr_vol = bv;
> + }
> +
> + dip = bv->sbv_diskinfo;
> +
> + if ((dip->flags & DISKINFO_FLAG_GOODLABEL) == 0) {
> + /* Attempt to read disklabel. */
> + bv->sbv_part = 'c';
> + if (sr_getdisklabel(bv, &dip->disklabel))
> + return ERDLAB;
> + dip->flags |= DISKINFO_FLAG_GOODLABEL;
> + }
> +
> + bv->sbv_part = part + 'a';
> +
> + bootdev_dip = dip;
> + f->f_devdata = dip;
> +
> + return 0;
> +}
> +
> +int
> +srstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
> + size_t *rsize)
> +{
> + struct diskinfo *dip = (struct diskinfo *)devdata;
> + return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
> +}
> +
> +int
> +srdiskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf)
> +{
> + return dip->diskio(rw, dip, off, nsect, buf);
> +}
> +
> +int
> +srclose(struct open_file *f)
> +{
> + f->f_devdata = NULL;
> +
> + return 0;
> +}
> +
> +int
> +srioctl(struct open_file *f, u_long cmd, void *data)
> +{
> + return 0;
> +}
> diff --git sys/arch/arm64/stand/efiboot/softraid_arm64.h
> sys/arch/arm64/stand/efiboot/softraid_arm64.h
> new file mode 100644
> index 00000000000..bd6b0f6e4c3
> --- /dev/null
> +++ sys/arch/arm64/stand/efiboot/softraid_arm64.h
> @@ -0,0 +1,33 @@
> +/* $OpenBSD: softraid_amd64.h,v 1.3 2016/09/11 17:51:21 jsing Exp $
> */
> +
> +/*
> + * Copyright (c) 2012 Joel Sing <[email protected]>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#ifndef _SOFTRAID_ARM64_H_
> +#define _SOFTRAID_ARM64_H_
> +
> +void srprobe(void);
> +
> +const char *sr_getdisklabel(struct sr_boot_volume *, struct disklabel *);
> +int sr_strategy(struct sr_boot_volume *, int, daddr32_t, size_t,
> + void *, size_t *);
> +int sropen(struct open_file *, ...);
> +int srstrategy(void *, int, daddr32_t, size_t, void *, size_t *);
> +int srdiskio(int, struct diskinfo *, u_int, int, void *);
> +int srclose(struct open_file *);
> +int srioctl(struct open_file *, u_long, void *);
> +
> +#endif /* _SOFTRAID_ARM64_H */
>
>