riscv64 efiboot bootloader
authordrahn <drahn@openbsd.org>
Wed, 28 Apr 2021 19:01:00 +0000 (19:01 +0000)
committerdrahn <drahn@openbsd.org>
Wed, 28 Apr 2021 19:01:00 +0000 (19:01 +0000)
Ported from arm64 with changes for riscv64.
Initial effort by Mickael Torres, with additional updates
ACPI api removed per request
ok kettenis@

22 files changed:
sys/arch/riscv64/stand/Makefile [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/Makefile [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/conf.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/disk.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/efiboot.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/efiboot.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/eficall.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/efidev.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/efidev.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/efipxe.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/efipxe.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/efirng.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/exec.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/fdt.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/fdt.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/heap.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/ldscript.riscv64 [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/libsa.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/self_reloc.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/softraid_riscv64.c [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/softraid_riscv64.h [new file with mode: 0644]
sys/arch/riscv64/stand/efiboot/start.S [new file with mode: 0644]

diff --git a/sys/arch/riscv64/stand/Makefile b/sys/arch/riscv64/stand/Makefile
new file mode 100644 (file)
index 0000000..7fe4690
--- /dev/null
@@ -0,0 +1,5 @@
+#      $OpenBSD: Makefile,v 1.1 2021/04/28 19:01:00 drahn Exp $
+
+SUBDIR=        efiboot
+
+.include <bsd.subdir.mk>
diff --git a/sys/arch/riscv64/stand/efiboot/Makefile b/sys/arch/riscv64/stand/efiboot/Makefile
new file mode 100644 (file)
index 0000000..5f5eee0
--- /dev/null
@@ -0,0 +1,85 @@
+#      $OpenBSD: Makefile,v 1.1 2021/04/28 19:01:00 drahn Exp $
+
+NOMAN=         #
+
+.if ${MACHINE} == "riscv64"
+
+PROG=          BOOTRISCV64.EFI
+OBJFMT=                binary
+INSTALL_STRIP=
+BINDIR=                /usr/mdec
+SRCS=          start.S self_reloc.c efiboot.c conf.c exec.c
+SRCS+=         efidev.c efipxe.c efirng.c fdt.c
+SRCS+=         softraid_riscv64.c
+
+S=             ${.CURDIR}/../../../..
+EFIDIR=                ${S}/stand/efi
+
+OBJCOPY?=      objcopy
+OBJDUMP?=      objdump
+
+LDFLAGS+=-nostdlib -T ${.CURDIR}/ldscript.riscv64 -Bsymbolic -shared
+
+.PATH: ${S}/stand/boot
+SRCS+= boot.c cmd.c vars.c
+
+.PATH: ${S}/lib/libsa
+SRCS+= alloc.c ctime.c exit.c getchar.c hexdump.c \
+       memcmp.c memcpy.c memmove.c memset.c \
+       printf.c putchar.c snprintf.c strchr.c strcmp.c strerror.c strncmp.c \
+       strncpy.c strtol.c strtoll.c
+SRCS+= close.c closeall.c cons.c cread.c dev.c disklabel.c dkcksum.c fchmod.c \
+       fstat.c lseek.c open.c read.c readdir.c stat.c
+SRCS+= loadfile.c arc4.c
+SRCS+= ufs.c ufs2.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/riscv64 ${S}/lib/libkern
+SRCS+= divdi3.c moddi3.c qdivrem.c strlcat.c strlcpy.c strlen.c
+
+.PATH: ${S}/lib/libz
+SRCS+= adler32.c crc32.c inflate.c inftrees.c
+
+CPPFLAGS+=     -nostdinc
+CPPFLAGS+=     -I${S} -I. -I${.CURDIR}
+CPPFLAGS+=     -I${EFIDIR}/include -I${EFIDIR}/include/riscv64
+CPPFLAGS+=     -D_STANDALONE
+CPPFLAGS+=     -DSMALL -DSLOW -DNOBYFOUR -D__INTERNAL_LIBSA_CREAD
+CPPFLAGS+=     -DNEEDS_HEAP_H -DFWRANDOM
+CPPFLAGS+=     -march=rv64gc --target=riscv64 -mno-relax
+COPTS+=                -Wno-attributes -Wno-format
+COPTS+=                -ffreestanding -fno-stack-protector
+COPTS+=                -fshort-wchar -fPIC -fno-builtin
+COPTS+=                -Wall -Werror
+
+PROG.elf=      ${PROG:S/.EFI/.elf/}
+CLEANFILES+=   ${PROG.elf} ${PROG.elf}.tmp
+
+${PROG}: ${PROG.elf}
+       ${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \
+               -j .dynamic -j .dynsym -j .dynstr -j .rel -j .rel.dyn \
+               -j .rela -j .rela.dyn -j .reloc \
+               --output-target=${OBJFMT} ${PROG.elf} ${.TARGET}
+
+.include <bsd.prog.mk>
+
+${PROG.elf}: ${OBJS}
+       ${LD} ${LDFLAGS} -o ${.TARGET}.tmp ${OBJS} ${LDADD}
+       @if ${OBJDUMP} -t ${.TARGET}.tmp | grep 'UND'; then     \
+               (echo Undefined symbols; false);                \
+       fi
+       mv ${.TARGET}.tmp ${.TARGET}
+
+.if !make(clean) && !make(cleandir) && !make(includes) && !make(obj)
+.BEGIN:
+       @([ -h machine ] || ln -s ${.CURDIR}/../../../${MACHINE}/include machine)
+.NOPATH: machine
+CLEANFILES+=   machine
+.endif
+
+.else
+NOPROG=yes
+.include <bsd.prog.mk>
+.endif
diff --git a/sys/arch/riscv64/stand/efiboot/conf.c b/sys/arch/riscv64/stand/efiboot/conf.c
new file mode 100644 (file)
index 0000000..4fd7153
--- /dev/null
@@ -0,0 +1,81 @@
+/*     $OpenBSD: conf.c,v 1.1 2021/04/28 19:01:00 drahn Exp $  */
+
+/*
+ * Copyright (c) 1996 Michael Shalayeff
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#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 <lib/libsa/ufs2.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_riscv64.h"
+
+const char version[] = "1.4";
+int    debug = 0;
+
+struct fs_ops file_system[] = {
+       { mtftp_open,  mtftp_close,  mtftp_read,  mtftp_write,  mtftp_seek,
+         mtftp_stat,  mtftp_readdir   },
+       { efitftp_open,tftp_close,   tftp_read,   tftp_write,   tftp_seek,
+         tftp_stat,   tftp_readdir   },
+       { ufs_open,    ufs_close,    ufs_read,    ufs_write,    ufs_seek,
+         ufs_stat,    ufs_readdir,  ufs_fchmod },
+       { ufs2_open,   ufs2_close,   ufs2_read,   ufs2_write,   ufs2_seek,
+         ufs2_stat,   ufs2_readdir, ufs2_fchmod },
+};
+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);
+
+struct consdev constab[] = {
+       { efi_cons_probe, efi_cons_init, efi_cons_getc, efi_cons_putc },
+       { efi_fb_probe, efi_fb_init, efi_cons_getc, efi_cons_putc },
+       { NULL }
+};
+struct consdev *cn_tab;
+
+struct netif_driver *netif_drivers[] = {
+       &efinet_driver,
+};
+int n_netif_drivers = nitems(netif_drivers);
diff --git a/sys/arch/riscv64/stand/efiboot/disk.h b/sys/arch/riscv64/stand/efiboot/disk.h
new file mode 100644 (file)
index 0000000..1492c4f
--- /dev/null
@@ -0,0 +1,33 @@
+/* $OpenBSD: disk.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */
+
+#ifndef _DISK_H
+#define _DISK_H
+
+#include <sys/queue.h>
+
+typedef struct efi_diskinfo {
+       EFI_BLOCK_IO            *blkio;
+       UINT32                   mediaid;
+} *efi_diskinfo_t;
+
+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)
+
+       int (*diskio)(int, struct diskinfo *, u_int, int, void *);
+       int (*strategy)(void *, int, daddr_t, size_t, void *, size_t *);
+
+       TAILQ_ENTRY(diskinfo) list;
+};
+TAILQ_HEAD(disklist_lh, diskinfo);
+
+extern struct diskinfo *bootdev_dip;
+
+extern struct disklist_lh disklist;
+
+#endif /* _DISK_H */
diff --git a/sys/arch/riscv64/stand/efiboot/efiboot.c b/sys/arch/riscv64/stand/efiboot/efiboot.c
new file mode 100644 (file)
index 0000000..6564837
--- /dev/null
@@ -0,0 +1,1010 @@
+/*     $OpenBSD: efiboot.c,v 1.1 2021/04/28 19:01:00 drahn Exp $       */
+
+/*
+ * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
+ * Copyright (c) 2016 Mark Kettenis
+ *
+ * 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/stat.h>
+#include <dev/cons.h>
+#include <sys/disklabel.h>
+
+#include <efi.h>
+#include <efiapi.h>
+#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_riscv64.h"
+
+#include "efidev.h"
+#include "efiboot.h"
+#include "eficall.h"
+#include "fdt.h"
+
+EFI_SYSTEM_TABLE       *ST;
+EFI_BOOT_SERVICES      *BS;
+EFI_RUNTIME_SERVICES   *RS;
+EFI_HANDLE              IH, efi_bootdp;
+
+EFI_PHYSICAL_ADDRESS    heap;
+UINTN                   heapsiz = 1 * 1024 * 1024;
+EFI_MEMORY_DESCRIPTOR  *mmap;
+UINTN                   mmap_key;
+UINTN                   mmap_ndesc;
+UINTN                   mmap_descsiz;
+UINT32                  mmap_version;
+
+static EFI_GUID                 imgp_guid = LOADED_IMAGE_PROTOCOL;
+static EFI_GUID                 blkio_guid = BLOCK_IO_PROTOCOL;
+static EFI_GUID                 devp_guid = DEVICE_PATH_PROTOCOL;
+static EFI_GUID                 gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+
+int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
+int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
+static void efi_heap_init(void);
+static void efi_memprobe_internal(void);
+static void efi_timer_init(void);
+static void efi_timer_cleanup(void);
+static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *);
+
+EFI_STATUS
+efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
+{
+       extern char             *progname;
+       EFI_LOADED_IMAGE        *imgp;
+       EFI_DEVICE_PATH         *dp = NULL;
+       EFI_STATUS               status;
+
+       ST = systab;
+       BS = ST->BootServices;
+       RS = ST->RuntimeServices;
+       IH = image;
+
+       /* disable reset by watchdog after 5 minutes */
+       EFI_CALL(BS->SetWatchdogTimer, 0, 0, 0, NULL);
+
+       status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid,
+           (void **)&imgp);
+       if (status == EFI_SUCCESS)
+               status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle,
+                   &devp_guid, (void **)&dp);
+       if (status == EFI_SUCCESS)
+               efi_bootdp = dp;
+
+       progname = "BOOTRISCV64";
+
+       boot(0);
+
+       return (EFI_SUCCESS);
+}
+
+static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
+static SIMPLE_INPUT_INTERFACE *conin;
+
+/*
+ * The device majors for these don't match the ones used by the
+ * kernel.  That's fine.  They're just used as an index into the cdevs
+ * array and never passed on to the kernel.
+ */
+static dev_t serial = makedev(0, 0);
+static dev_t framebuffer = makedev(1, 0);
+
+static char framebuffer_path[128];
+
+void
+efi_cons_probe(struct consdev *cn)
+{
+       cn->cn_pri = CN_MIDPRI;
+       cn->cn_dev = serial;
+}
+
+void
+efi_cons_init(struct consdev *cp)
+{
+       conin = ST->ConIn;
+       conout = ST->ConOut;
+}
+
+int
+efi_cons_getc(dev_t dev)
+{
+       EFI_INPUT_KEY    key;
+       EFI_STATUS       status;
+#if 0
+       UINTN            dummy;
+#endif
+       static int       lastchar = 0;
+
+       if (lastchar) {
+               int r = lastchar;
+               if ((dev & 0x80) == 0)
+                       lastchar = 0;
+               return (r);
+       }
+
+       status = conin->ReadKeyStroke(conin, &key);
+       while (status == EFI_NOT_READY || key.UnicodeChar == 0) {
+               if (dev & 0x80)
+                       return (0);
+               /*
+                * XXX The implementation of WaitForEvent() in U-boot
+                * is broken and neverreturns.
+                */
+#if 0
+               BS->WaitForEvent(1, &conin->WaitForKey, &dummy);
+#endif
+               status = conin->ReadKeyStroke(conin, &key);
+       }
+
+       if (dev & 0x80)
+               lastchar = key.UnicodeChar;
+
+       return (key.UnicodeChar);
+}
+
+void
+efi_cons_putc(dev_t dev, int c)
+{
+       CHAR16  buf[2];
+
+       if (c == '\n')
+               efi_cons_putc(dev, '\r');
+
+       buf[0] = c;
+       buf[1] = 0;
+
+       conout->OutputString(conout, buf);
+}
+
+void
+efi_fb_probe(struct consdev *cn)
+{
+       cn->cn_pri = CN_LOWPRI;
+       cn->cn_dev = framebuffer;
+}
+
+void
+efi_fb_init(struct consdev *cn)
+{
+       conin = ST->ConIn;
+       conout = ST->ConOut;
+}
+
+int
+efi_fb_getc(dev_t dev)
+{
+       return efi_cons_getc(dev);
+}
+
+void
+efi_fb_putc(dev_t dev, int c)
+{
+       efi_cons_putc(dev, c);
+}
+
+static void
+efi_heap_init(void)
+{
+       EFI_STATUS       status;
+
+       status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData,
+           EFI_SIZE_TO_PAGES(heapsiz), &heap);
+       if (status != EFI_SUCCESS)
+               panic("BS->AllocatePages()");
+}
+
+struct disklist_lh disklist;
+struct diskinfo *bootdev_dip;
+
+void
+efi_diskprobe(void)
+{
+       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) {
+               handles = alloc(sz);
+               status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid,
+                   0, &sz, handles);
+       }
+       if (handles == NULL || EFI_ERROR(status))
+               return;
+
+       if (efi_bootdp != NULL)
+               depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
+
+       /*
+        * U-Boot incorrectly represents devices with a single
+        * MEDIA_DEVICE_PATH component.  In that case include that
+        * component into the matching, otherwise we'll blindly select
+        * the first device.
+        */
+       if (depth == 0)
+               depth = 1;
+
+       for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
+               status = EFI_CALL(BS->HandleProtocol, handles[i], &blkio_guid,
+                   (void **)&blkio);
+               if (EFI_ERROR(status))
+                       panic("BS->HandleProtocol() returns %d", status);
+
+               media = blkio->Media;
+               if (media->LogicalPartition || !media->MediaPresent)
+                       continue;
+               di = alloc(sizeof(struct diskinfo));
+               efid_init(di, blkio);
+
+               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))
+                       goto next;
+               if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
+                       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");
+}
+
+/*
+ * Determine the number of nodes up to, but not including, the first
+ * node of the specified type.
+ */
+int
+efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
+{
+       int     i;
+
+       for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) {
+               if (DevicePathType(dp) == dptype)
+                       return (i);
+       }
+
+       return (i);
+}
+
+int
+efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
+{
+       int      i, cmp;
+
+       for (i = 0; i < deptn; i++) {
+               if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb))
+                       return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb))
+                           ? 0 : (IsDevicePathEnd(dpa))? -1 : 1);
+               cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb);
+               if (cmp)
+                       return (cmp);
+               cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa));
+               if (cmp)
+                       return (cmp);
+               dpa = NextDevicePathNode(dpa);
+               dpb = NextDevicePathNode(dpb);
+       }
+
+       return (0);
+}
+
+void
+efi_framebuffer(void)
+{
+       EFI_GRAPHICS_OUTPUT *gop;
+       EFI_STATUS status;
+       void *node, *child;
+       uint32_t acells, scells;
+       uint64_t base, size;
+       uint32_t reg[4];
+       uint32_t width, height, stride;
+       char *format;
+       char *prop;
+
+       /*
+        * Don't create a "simple-framebuffer" node if we already have
+        * one.  Besides "/chosen", we also check under "/" since that
+        * is where the Raspberry Pi firmware puts it.
+        */
+       node = fdt_find_node("/chosen");
+       for (child = fdt_child_node(node); child;
+            child = fdt_next_node(child)) {
+               if (!fdt_node_is_compatible(child, "simple-framebuffer"))
+                       continue;
+               if (!fdt_node_property(child, "status", &prop) ||
+                   strcmp(prop, "okay") == 0) {
+                       strlcpy(framebuffer_path, "/chosen/",
+                           sizeof(framebuffer_path));
+                       strlcat(framebuffer_path, fdt_node_name(child),
+                           sizeof(framebuffer_path));
+                       return;
+               }
+       }
+       node = fdt_find_node("/");
+       for (child = fdt_child_node(node); child;
+            child = fdt_next_node(child)) {
+               if (!fdt_node_is_compatible(child, "simple-framebuffer"))
+                       continue;
+               if (!fdt_node_property(child, "status", &prop) ||
+                   strcmp(prop, "okay") == 0) {
+                       strlcpy(framebuffer_path, "/",
+                           sizeof(framebuffer_path));
+                       strlcat(framebuffer_path, fdt_node_name(child),
+                           sizeof(framebuffer_path));
+                       return;
+               }
+       }
+
+       status = EFI_CALL(BS->LocateProtocol, &gop_guid, NULL, (void **)&gop);
+       if (status != EFI_SUCCESS)
+               return;
+
+       /* Paranoia! */
+       if (gop == NULL || gop->Mode == NULL || gop->Mode->Info == NULL)
+               return;
+
+       /* We only support 32-bit pixel modes for now. */
+       switch (gop->Mode->Info->PixelFormat) {
+       case PixelRedGreenBlueReserved8BitPerColor:
+               format = "x8b8g8r8";
+               break;
+       case PixelBlueGreenRedReserved8BitPerColor:
+               format = "x8r8g8b8";
+               break;
+       default:
+               return;
+       }
+
+       base = gop->Mode->FrameBufferBase;
+       size = gop->Mode->FrameBufferSize;
+       width = htobe32(gop->Mode->Info->HorizontalResolution);
+       height = htobe32(gop->Mode->Info->VerticalResolution);
+       stride = htobe32(gop->Mode->Info->PixelsPerScanLine * 4);
+
+       node = fdt_find_node("/");
+       if (fdt_node_property_int(node, "#address-cells", &acells) != 1)
+               acells = 1;
+       if (fdt_node_property_int(node, "#size-cells", &scells) != 1)
+               scells = 1;
+       if (acells > 2 || scells > 2)
+               return;
+       if (acells >= 1)
+               reg[0] = htobe32(base);
+       if (acells == 2) {
+               reg[1] = reg[0];
+               reg[0] = htobe32(base >> 32);
+       }
+       if (scells >= 1)
+               reg[acells] = htobe32(size);
+       if (scells == 2) {
+               reg[acells + 1] = reg[acells];
+               reg[acells] = htobe32(size >> 32);
+       }
+
+       node = fdt_find_node("/chosen");
+       fdt_node_add_node(node, "framebuffer", &child);
+       fdt_node_add_property(child, "status", "okay", strlen("okay") + 1);
+       fdt_node_add_property(child, "format", format, strlen(format) + 1);
+       fdt_node_add_property(child, "stride", &stride, 4);
+       fdt_node_add_property(child, "height", &height, 4);
+       fdt_node_add_property(child, "width", &width, 4);
+       fdt_node_add_property(child, "reg", reg, (acells + scells) * 4);
+       fdt_node_add_property(child, "compatible",
+           "simple-framebuffer", strlen("simple-framebuffer") + 1);
+
+       strlcpy(framebuffer_path, "/chosen/framebuffer",
+           sizeof(framebuffer_path));
+}
+
+void
+efi_console(void)
+{
+       void *node;
+
+       if (cn_tab->cn_dev != framebuffer)
+               return;
+
+       if (strlen(framebuffer_path) == 0)
+               return;
+
+       /* Point stdout-path at the framebuffer node. */
+       node = fdt_find_node("/chosen");
+       fdt_node_add_property(node, "stdout-path",
+           framebuffer_path, strlen(framebuffer_path) + 1);
+}
+
+uint64_t dma_constraint[2] = { 0, -1 };
+
+void
+efi_dma_constraint(void)
+{
+       void *node;
+
+       /* Raspberry Pi 4 is "special". */
+       node = fdt_find_node("/");
+       if (fdt_node_is_compatible(node, "brcm,bcm2711"))
+               dma_constraint[1] = htobe64(0x3bffffff);
+
+       /* Pass DMA constraint. */
+       node = fdt_find_node("/chosen");
+       fdt_node_add_property(node, "openbsd,dma-constraint",
+           dma_constraint, sizeof(dma_constraint));
+}
+
+void *fdt = NULL;
+char *bootmac = NULL;
+static EFI_GUID fdt_guid = FDT_TABLE_GUID;
+
+#define        efi_guidcmp(_a, _b)     memcmp((_a), (_b), sizeof(EFI_GUID))
+
+void *
+efi_makebootargs(char *bootargs, int howto)
+{
+       struct sr_boot_volume *bv;
+       u_char bootduid[8];
+       u_char zero[8] = { 0 };
+       uint64_t uefi_system_table = htobe64((uintptr_t)ST);
+       uint32_t boothowto = htobe32(howto);
+       void *node;
+       size_t len;
+       int i;
+
+       if (fdt == NULL) {
+               for (i = 0; i < ST->NumberOfTableEntries; i++) {
+                       if (efi_guidcmp(&fdt_guid,
+                           &ST->ConfigurationTable[i].VendorGuid) == 0)
+                               fdt = ST->ConfigurationTable[i].VendorTable;
+               }
+       }
+
+       if (!fdt_init(fdt))
+               return NULL;
+
+       node = fdt_find_node("/chosen");
+       if (!node)
+               return NULL;
+
+       len = strlen(bootargs) + 1;
+       fdt_node_add_property(node, "bootargs", bootargs, len);
+       fdt_node_add_property(node, "openbsd,boothowto",
+           &boothowto, sizeof(boothowto));
+
+       /* Pass DUID of the boot disk. */
+       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,sr-bootuuid",
+                           &bv->sbv_uuid, sizeof(bv->sbv_uuid));
+                       if (bv->sbv_maskkey != NULL)
+                               fdt_node_add_property(node,
+                                   "openbsd,sr-bootkey", bv->sbv_maskkey,
+                                   SR_CRYPTO_MAXKEYBYTES);
+               }
+       }
+
+       sr_clear_keys();
+
+       /* Pass netboot interface address. */
+       if (bootmac)
+               fdt_node_add_property(node, "openbsd,bootmac", bootmac, 6);
+
+       /* Pass EFI system table. */
+       fdt_node_add_property(node, "openbsd,uefi-system-table",
+           &uefi_system_table, sizeof(uefi_system_table));
+
+       /* Placeholders for EFI memory map. */
+       fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8);
+       fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4);
+       fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4);
+       fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4);
+
+       efi_framebuffer();
+       efi_console();
+       efi_dma_constraint();
+
+       fdt_finalize();
+
+       return fdt;
+}
+
+void
+efi_updatefdt(void)
+{
+       uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap);
+       uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz);
+       uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz);
+       uint32_t uefi_mmap_desc_ver = htobe32(mmap_version);
+       void *node;
+
+       node = fdt_find_node("/chosen");
+       if (!node)
+               return;
+
+       /* Pass EFI memory map. */
+       fdt_node_set_property(node, "openbsd,uefi-mmap-start",
+           &uefi_mmap_start, sizeof(uefi_mmap_start));
+       fdt_node_set_property(node, "openbsd,uefi-mmap-size",
+           &uefi_mmap_size, sizeof(uefi_mmap_size));
+       fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size",
+           &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size));
+       fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver",
+           &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver));
+
+       fdt_finalize();
+}
+
+u_long efi_loadaddr;
+
+void
+machdep(void)
+{
+       EFI_PHYSICAL_ADDRESS addr;
+
+       cninit();
+       efi_heap_init();
+
+       /*
+        * The kernel expects to be loaded into a block of memory aligned
+        * on a 2MB boundary.  We allocate a block of 64MB of memory, which
+        * gives us plenty of room for growth.
+        */
+       if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024),
+           0x200000, &addr) != EFI_SUCCESS)
+               printf("Can't allocate memory\n");
+       efi_loadaddr = addr;
+
+       efi_timer_init();
+       efi_diskprobe();
+       efi_pxeprobe();
+}
+
+void
+efi_cleanup(void)
+{
+       int              retry;
+       EFI_STATUS       status;
+
+       efi_timer_cleanup();
+
+       /* retry once in case of failure */
+       for (retry = 1; retry >= 0; retry--) {
+               efi_memprobe_internal();        /* sync the current map */
+               efi_updatefdt();
+               status = EFI_CALL(BS->ExitBootServices, IH, mmap_key);
+               if (status == EFI_SUCCESS)
+                       break;
+               if (retry == 0)
+                       panic("ExitBootServices failed (%d)", status);
+       }
+}
+
+void
+_rtt(void)
+{
+#ifdef EFI_DEBUG
+       printf("Hit any key to reboot\n");
+       efi_cons_getc(0);
+#endif
+       RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
+       for (;;)
+               continue;
+}
+
+/*
+ * U-Boot only implements the GetTime() Runtime Service if it has been
+ * configured with CONFIG_DM_RTC.  Most board configurations don't
+ * include that option, so we can't use it to implement our boot
+ * prompt timeout.  Instead we use timer events to simulate a clock
+ * that ticks ever second.
+ */
+
+EFI_EVENT timer;
+int ticks;
+
+static VOID
+efi_timer(EFI_EVENT event, VOID *context)
+{
+       ticks++;
+}
+
+static void
+efi_timer_init(void)
+{
+       EFI_STATUS status;
+
+       status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+           efi_timer, NULL, &timer);
+       if (status == EFI_SUCCESS)
+               status = BS->SetTimer(timer, TimerPeriodic, 10000000);
+       if (EFI_ERROR(status))
+               printf("Can't create timer\n");
+}
+
+static void
+efi_timer_cleanup(void)
+{
+       BS->CloseEvent(timer);
+}
+
+time_t
+getsecs(void)
+{
+       return ticks;
+}
+
+/*
+ * Various device-related bits.
+ */
+
+void
+devboot(dev_t dev, char *p)
+{
+       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;
+}
+
+const char cdevs[][4] = { "com", "fb" };
+const int ncdevs = nitems(cdevs);
+
+int
+cnspeed(dev_t dev, int sp)
+{
+       return 115200;
+}
+
+char ttyname_buf[8];
+
+char *
+ttyname(int fd)
+{
+       snprintf(ttyname_buf, sizeof ttyname_buf, "%s%d",
+           cdevs[major(cn_tab->cn_dev)], minor(cn_tab->cn_dev));
+
+       return ttyname_buf;
+}
+
+dev_t
+ttydev(char *name)
+{
+       int i, unit = -1;
+       char *no = name + strlen(name) - 1;
+
+       while (no >= name && *no >= '0' && *no <= '9')
+               unit = (unit < 0 ? 0 : (unit * 10)) + *no-- - '0';
+       if (no < name || unit < 0)
+               return NODEV;
+       for (i = 0; i < ncdevs; i++)
+               if (strncmp(name, cdevs[i], no - name + 1) == 0)
+                       return makedev(i, unit);
+       return NODEV;
+}
+
+#define MAXDEVNAME     16
+
+/*
+ * Parse a device spec.
+ *
+ * [A-Za-z]*[0-9]*[A-Za-z]:file
+ *    dev   uint    part
+ */
+int
+devparse(const char *fname, int *dev, int *unit, int *part, const char **file)
+{
+       const char *s;
+
+       *unit = 0;      /* default to wd0a */
+       *part = 0;
+       *dev  = 0;
+
+       s = strchr(fname, ':');
+       if (s != NULL) {
+               int devlen;
+               int i, u, p = 0;
+               struct devsw *dp;
+               char devname[MAXDEVNAME];
+
+               devlen = s - fname;
+               if (devlen > MAXDEVNAME)
+                       return (EINVAL);
+
+               /* extract device name */
+               for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
+                       devname[i] = fname[i];
+               devname[i] = 0;
+
+               if (!isdigit(fname[i]))
+                       return (EUNIT);
+
+               /* device number */
+               for (u = 0; isdigit(fname[i]) && (i < devlen); i++)
+                       u = u * 10 + (fname[i] - '0');
+
+               if (!isalpha(fname[i]))
+                       return (EPART);
+
+               /* partition number */
+               if (i < devlen)
+                       p = fname[i++] - 'a';
+
+               if (i != devlen)
+                       return (ENXIO);
+
+               /* check device name */
+               for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
+                       if (dp->dv_name && !strcmp(devname, dp->dv_name))
+                               break;
+               }
+
+               if (i >= ndevs)
+                       return (ENXIO);
+
+               *unit = u;
+               *part = p;
+               *dev  = i;
+               fname = ++s;
+       }
+
+       *file = fname;
+
+       return (0);
+}
+
+int
+devopen(struct open_file *f, const char *fname, char **file)
+{
+       struct devsw *dp;
+       int dev, unit, part, error;
+
+       error = devparse(fname, &dev, &unit, &part, (const char **)file);
+       if (error)
+               return (error);
+
+       dp = &devsw[dev];
+       f->f_dev = dp;
+
+       if (strcmp("tftp", dp->dv_name) != 0) {
+               /*
+                * Clear bootmac, to signal that we loaded this file from a
+                * non-network device.
+                */
+               bootmac = NULL;
+       }
+
+       return (*dp->dv_open)(f, unit, part);
+}
+
+static void
+efi_memprobe_internal(void)
+{
+       EFI_STATUS               status;
+       UINTN                    mapkey, mmsiz, siz;
+       UINT32                   mmver;
+       EFI_MEMORY_DESCRIPTOR   *mm;
+       int                      n;
+
+       free(mmap, mmap_ndesc * mmap_descsiz);
+
+       siz = 0;
+       status = EFI_CALL(BS->GetMemoryMap, &siz, NULL, &mapkey, &mmsiz,
+           &mmver);
+       if (status != EFI_BUFFER_TOO_SMALL)
+               panic("cannot get the size of memory map");
+       mm = alloc(siz);
+       status = EFI_CALL(BS->GetMemoryMap, &siz, mm, &mapkey, &mmsiz, &mmver);
+       if (status != EFI_SUCCESS)
+               panic("cannot get the memory map");
+       n = siz / mmsiz;
+       mmap = mm;
+       mmap_key = mapkey;
+       mmap_ndesc = n;
+       mmap_descsiz = mmsiz;
+       mmap_version = mmver;
+}
+
+/*
+ * 64-bit ARMs can have a much wider memory mapping, as in somewhere
+ * after the 32-bit region.  To cope with our alignment requirement,
+ * use the memory table to find a place where we can fit.
+ */
+static EFI_STATUS
+efi_memprobe_find(UINTN pages, UINTN align, EFI_PHYSICAL_ADDRESS *addr)
+{
+       EFI_MEMORY_DESCRIPTOR   *mm;
+       int                      i, j;
+
+       if (align < EFI_PAGE_SIZE)
+               return EFI_INVALID_PARAMETER;
+
+       efi_memprobe_internal();        /* sync the current map */
+
+       for (i = 0, mm = mmap; i < mmap_ndesc;
+           i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) {
+               if (mm->Type != EfiConventionalMemory)
+                       continue;
+
+               if (mm->NumberOfPages < pages)
+                       continue;
+
+               for (j = 0; j < mm->NumberOfPages; j++) {
+                       EFI_PHYSICAL_ADDRESS paddr;
+
+                       if (mm->NumberOfPages - j < pages)
+                               break;
+
+                       paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE);
+                       if (paddr & (align - 1))
+                               continue;
+
+                       if (EFI_CALL(BS->AllocatePages, AllocateAddress,
+                           EfiLoaderData, pages, &paddr) == EFI_SUCCESS) {
+                               *addr = paddr;
+                               return EFI_SUCCESS;
+                       }
+               }
+       }
+       return EFI_OUT_OF_RESOURCES;
+}
+
+/*
+ * Commands
+ */
+
+int Xdtb_efi(void);
+int Xexit_efi(void);
+int Xpoweroff_efi(void);
+
+const struct cmd_table cmd_machine[] = {
+       { "dtb",        CMDT_CMD, Xdtb_efi },
+       { "exit",       CMDT_CMD, Xexit_efi },
+       { "poweroff",   CMDT_CMD, Xpoweroff_efi },
+       { NULL, 0 }
+};
+
+int
+Xdtb_efi(void)
+{
+       EFI_PHYSICAL_ADDRESS addr;
+       char path[MAXPATHLEN];
+       struct stat sb;
+       int fd;
+
+#define O_RDONLY       0
+
+       if (cmd.argc != 2) {
+               printf("dtb file\n");
+               return (0);
+       }
+
+       snprintf(path, sizeof(path), "%s:%s", cmd.bootdev, cmd.argv[1]);
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0 || fstat(fd, &sb) == -1) {
+               printf("cannot open %s\n", path);
+               return (0);
+       }
+       if (efi_memprobe_find(EFI_SIZE_TO_PAGES(sb.st_size),
+           0x1000, &addr) != EFI_SUCCESS) {
+               printf("cannot allocate memory for %s\n", path);
+               return (0);
+       }
+       if (read(fd, (void *)addr, sb.st_size) != sb.st_size) {
+               printf("cannot read from %s\n", path);
+               return (0);
+       }
+
+       fdt = (void *)addr;
+       return (0);
+}
+
+int
+Xexit_efi(void)
+{
+       EFI_CALL(BS->Exit, IH, 0, 0, NULL);
+       for (;;)
+               continue;
+       return (0);
+}
+
+int
+Xpoweroff_efi(void)
+{
+       EFI_CALL(RS->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
+       return (0);
+}
diff --git a/sys/arch/riscv64/stand/efiboot/efiboot.h b/sys/arch/riscv64/stand/efiboot/efiboot.h
new file mode 100644 (file)
index 0000000..18fea50
--- /dev/null
@@ -0,0 +1,30 @@
+/*     $OpenBSD: efiboot.h,v 1.1 2021/04/28 19:01:00 drahn Exp $       */
+
+/*
+ * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
+ *
+ * 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.
+ */
+
+void   efi_cleanup(void);
+void   efi_diskprobe(void);
+void   efi_pxeprobe(void);
+void   *efi_makebootargs(char *, int);
+void   efi_cons_probe(struct consdev *);
+void   efi_cons_init(struct consdev *);
+int    efi_cons_getc(dev_t);
+void   efi_cons_putc(dev_t, int);
+void   efi_fb_probe(struct consdev *);
+void   efi_fb_init(struct consdev *);
+int    efi_fb_getc(dev_t);
+void   efi_fb_putc(dev_t, int);
diff --git a/sys/arch/riscv64/stand/efiboot/eficall.h b/sys/arch/riscv64/stand/efiboot/eficall.h
new file mode 100644 (file)
index 0000000..7c33b66
--- /dev/null
@@ -0,0 +1,55 @@
+/*     $OpenBSD: eficall.h,v 1.1 2021/04/28 19:01:00 drahn Exp $       */
+
+/*
+ * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
+ *
+ * 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.
+ */
+
+#if !defined(__amd64__)
+
+#define        EFI_CALL(_func_, ...) (_func_)(__VA_ARGS__)
+
+#else
+
+extern uint64_t efi_call(int, void *, ...);
+
+#define        _call_0(_func) \
+    efi_call(0, (_func))
+#define        _call_1(_func, _1) \
+    efi_call(1, (_func), (_1))
+#define        _call_2(_func, _1, _2) \
+    efi_call(2, (_func), (_1), (_2))
+#define        _call_3(_func, _1, _2, _3) \
+    efi_call(3, (_func), (_1), (_2), (_3))
+#define        _call_4(_func, _1, _2, _3, _4) \
+    efi_call(4, (_func), (_1), (_2), (_3), (_4))
+#define        _call_5(_func, _1, _2, _3, _4, _5) \
+    efi_call(5, (_func), (_1), (_2), (_3), (_4), (_5))
+#define        _call_6(_func, _1, _2, _3, _4, _5, _6) \
+    efi_call(6, (_func), (_1), (_2), (_3), (_4), (_5), (_6))
+#define        _call_7(_func, _1, _2, _3, _4, _5, _6, _7) \
+    efi_call(7, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7))
+#define        _call_8(_func, _1, _2, _3, _4, _5, _6, _7, _8) \
+    efi_call(8, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8))
+#define        _call_9(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
+    efi_call(9, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8), (_9))
+#define        _call_10(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
+    efi_call(10, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8), (_9), (_10))
+
+#define _efi_call_fn(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10,  _fn, ...) _fn
+
+#define        EFI_CALL(...)   \
+    _efi_call_fn(__VA_ARGS__, _call_10, _call_9, _call_8,  _call_7, _call_6, \
+           _call_5, _call_4, _call_3, _call_2, _call_1, _call_0)(__VA_ARGS__)
+#endif
diff --git a/sys/arch/riscv64/stand/efiboot/efidev.c b/sys/arch/riscv64/stand/efiboot/efidev.c
new file mode 100644 (file)
index 0000000..fd8855b
--- /dev/null
@@ -0,0 +1,558 @@
+/*     $OpenBSD: efidev.c,v 1.1 2021/04/28 19:01:00 drahn Exp $        */
+
+/*
+ * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
+ * Copyright (c) 2016 Mark Kettenis
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/disklabel.h>
+#include <lib/libz/zlib.h>
+#include <isofs/cd9660/iso.h>
+
+#include "libsa.h"
+
+#include <efi.h>
+#include "eficall.h"
+
+extern EFI_BOOT_SERVICES *BS;
+
+extern int debug;
+
+#include "disk.h"
+#include "efidev.h"
+
+#define EFI_BLKSPERSEC(_ed)    ((_ed)->blkio->Media->BlockSize / DEV_BSIZE)
+#define EFI_SECTOBLK(_ed, _n)  ((_n) * EFI_BLKSPERSEC(_ed))
+
+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)
+{
+       EFI_STATUS status = EFI_SUCCESS;
+       EFI_PHYSICAL_ADDRESS addr;
+       caddr_t data;
+
+       if (ed->blkio->Media->BlockSize != DEV_BSIZE)
+               return (EFI_UNSUPPORTED);
+
+       status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
+           EFI_SIZE_TO_PAGES(nsect * DEV_BSIZE), &addr);
+       if (EFI_ERROR(status))
+               goto on_eio;
+       data = (caddr_t)(uintptr_t)addr;
+
+       switch (rw) {
+       case F_READ:
+               status = EFI_CALL(ed->blkio->ReadBlocks,
+                   ed->blkio, ed->mediaid, off,
+                   nsect * DEV_BSIZE, data);
+               if (EFI_ERROR(status))
+                       goto on_eio;
+               memcpy(buf, data, nsect * DEV_BSIZE);
+               break;
+       case F_WRITE:
+               if (ed->blkio->Media->ReadOnly)
+                       goto on_eio;
+               memcpy(data, buf, nsect * DEV_BSIZE);
+               status = EFI_CALL(ed->blkio->WriteBlocks,
+                   ed->blkio, ed->mediaid, off,
+                   nsect * DEV_BSIZE, data);
+               if (EFI_ERROR(status))
+                       goto on_eio;
+               break;
+       }
+       return (EFI_SUCCESS);
+
+on_eio:
+       BS->FreePages(addr, EFI_SIZE_TO_PAGES(nsect * DEV_BSIZE));
+
+       return (status);
+}
+
+static int
+efid_diskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf)
+{
+       EFI_STATUS status;
+
+       status = efid_io(rw, &dip->ed, off, nsect, buf);
+
+       return ((EFI_ERROR(status))? -1 : 0);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Try to find the disk address of the first MBR OpenBSD partition.
+ *
+ * N.B.: must boot from a partition within first 2^32-1 sectors!
+ *
+ * Called only if the MBR on sector 0 is *not* a protective MBR
+ * and *does* have a valid signature.
+ *
+ * We don't check the signatures of EBR's, and they cannot be
+ * protective MBR's so there is no need to check for that.
+ */
+static u_int
+findopenbsd(efi_diskinfo_t ed, const char **err)
+{
+       EFI_STATUS status;
+       struct dos_mbr mbr;
+       struct dos_partition *dp;
+       u_int mbroff = DOSBBSECTOR;
+       u_int mbr_eoff = DOSBBSECTOR;   /* Offset of MBR extended partition. */
+       int i, maxebr = DOS_MAXEBR, nextebr;
+
+again:
+       if (!maxebr--) {
+               *err = "too many extended partitions";
+               return (-1);
+       }
+
+       /* Read MBR */
+       bzero(&mbr, sizeof(mbr));
+       status = efid_io(F_READ, ed, mbroff, 1, &mbr);
+       if (EFI_ERROR(status)) {
+               *err = "Disk I/O Error";
+               return (-1);
+       }
+
+       /* Search for OpenBSD partition */
+       nextebr = 0;
+       for (i = 0; i < NDOSPART; i++) {
+               dp = &mbr.dmbr_parts[i];
+               if (!dp->dp_size)
+                       continue;
+#ifdef BIOS_DEBUG
+               if (debug)
+                       printf("found partition %u: "
+                           "type %u (0x%x) offset %u (0x%x)\n",
+                           (int)(dp - mbr.dmbr_parts),
+                           dp->dp_typ, dp->dp_typ,
+                           dp->dp_start, dp->dp_start);
+#endif
+               if (dp->dp_typ == DOSPTYP_OPENBSD) {
+                       if (dp->dp_start > (dp->dp_start + mbroff))
+                               continue;
+                       return (dp->dp_start + mbroff);
+               }
+
+               /*
+                * Record location of next ebr if and only if this is the first
+                * extended partition in this boot record!
+                */
+               if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
+                   dp->dp_typ == DOSPTYP_EXTENDL)) {
+                       nextebr = dp->dp_start + mbr_eoff;
+                       if (nextebr < dp->dp_start)
+                               nextebr = (u_int)-1;
+                       if (mbr_eoff == DOSBBSECTOR)
+                               mbr_eoff = dp->dp_start;
+               }
+       }
+
+       if (nextebr && nextebr != (u_int)-1) {
+               mbroff = nextebr;
+               goto again;
+       }
+
+       return (-1);
+}
+
+/*
+ * Try to find the disk address of the first GPT OpenBSD partition.
+ *
+ * N.B.: must boot from a partition within first 2^32-1 sectors!
+ *
+ * Called only if the MBR on sector 0 *is* a protective MBR
+ * with a valid signature and sector 1 is a valid GPT header.
+ */
+static u_int
+findopenbsd_gpt(efi_diskinfo_t ed, const char **err)
+{
+       EFI_STATUS               status;
+       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;
+       static u_char            buf[4096];
+
+       /* 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 (EFI_BLKSPERSEC(ed) > 8) {
+               *err = "disk sector > 4096 bytes\n";
+               return (-1);
+       }
+
+       /* LBA1: GPT Header */
+       lba = 1;
+       status = efid_io(F_READ, ed, EFI_SECTOBLK(ed, lba), EFI_BLKSPERSEC(ed),
+           buf);
+       if (EFI_ERROR(status)) {
+               *err = "Disk I/O Error";
+               return (-1);
+       }
+       memcpy(&gh, buf, sizeof(gh));
+
+       /* Check signature */
+       if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
+               *err = "bad GPT signature\n";
+               return (-1);
+       }
+
+       if (letoh32(gh.gh_rev) != GPTREVISION) {
+               *err = "bad GPT revision\n";
+               return (-1);
+       }
+
+       ghsize = letoh32(gh.gh_size);
+       if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) {
+               *err = "bad GPT header size\n";
+               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";
+               return (-1);
+       }
+
+       lba = letoh64(gh.gh_part_lba);
+       ghpartsize = letoh32(gh.gh_part_size);
+       ghpartspersec = ed->blkio->Media->BlockSize / 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++) {
+               status = efid_io(F_READ, ed, EFI_SECTOBLK(ed, lba),
+                   EFI_BLKSPERSEC(ed), buf);
+               if (EFI_ERROR(status)) {
+                       *err = "Disk I/O Error";
+                       return (-1);
+               }
+               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;
+               }
+       }
+       if (new_csum != letoh32(gh.gh_part_csum)) {
+               *err = "bad GPT entries checksum\n";
+               return (-1);
+       }
+       if (found) {
+               lba = letoh64(gp.gp_lba_start);
+               /* Bootloaders do not current handle addresses > UINT_MAX! */
+               if (lba > UINT_MAX || EFI_SECTOBLK(ed, lba) > UINT_MAX) {
+                       *err = "OpenBSD Partition LBA > 2**32 - 1";
+                       return (-1);
+               }
+               return (u_int)lba;
+       }
+
+       return (-1);
+}
+
+const char *
+efi_getdisklabel(efi_diskinfo_t ed, struct disklabel *label)
+{
+       u_int start = 0;
+       uint8_t buf[DEV_BSIZE];
+       struct dos_partition dosparts[NDOSPART];
+       EFI_STATUS status;
+       const char *err = NULL;
+       int error;
+
+       /*
+        * Read sector 0. Ensure it has a valid MBR signature.
+        *
+        * If it's a protective MBR then try to find the disklabel via
+        * GPT. If it's not a protective MBR, try to find the disklabel
+        * via MBR.
+        */
+       memset(buf, 0, sizeof(buf));
+       status = efid_io(F_READ, ed, DOSBBSECTOR, 1, buf);
+       if (EFI_ERROR(status))
+               return ("Disk I/O Error");
+
+       /* Check MBR signature. */
+       if (buf[510] != 0x55 || buf[511] != 0xaa) {
+               if (efi_getdisklabel_cd9660(ed, label) == 0)
+                       return (NULL);
+               return ("invalid MBR signature");
+       }
+
+       memcpy(dosparts, buf+DOSPARTOFF, sizeof(dosparts));
+
+       /* check for GPT protective MBR. */
+       if (gpt_chk_mbr(dosparts, ed->blkio->Media->LastBlock + 1) == 0) {
+               start = findopenbsd_gpt(ed, &err);
+               if (start == (u_int)-1) {
+                       if (err != NULL)
+                               return (err);
+                       return ("no OpenBSD GPT partition");
+               }
+       } else {
+               start = findopenbsd(ed, &err);
+               if (start == (u_int)-1) {
+                       if (err != NULL)
+                               return (err);
+                       return "no OpenBSD MBR partition\n";
+               }
+       }
+
+       /* Load BSD disklabel */
+#ifdef BIOS_DEBUG
+       if (debug)
+               printf("loading disklabel @ %u\n", start + DOS_LABELSECTOR);
+#endif
+       /* read disklabel */
+       error = efid_io(F_READ, ed, EFI_SECTOBLK(ed, start) + DOS_LABELSECTOR,
+           1, buf);
+
+       if (error)
+               return "failed to read disklabel";
+
+       /* Fill in disklabel */
+       return (getdisklabel(buf, label));
+}
+
+static int
+efi_getdisklabel_cd9660(efi_diskinfo_t ed, struct disklabel *label)
+{
+       int              off;
+       uint8_t          buf[DEV_BSIZE];
+       EFI_STATUS       status;
+
+       for (off = 0; off < 100; off++) {
+               status = efid_io(F_READ, ed,
+                   EFI_BLKSPERSEC(ed) * (16 + off), 1, buf);
+               if (EFI_ERROR(status))
+                       return (-1);
+               if (bcmp(buf + 1, ISO_STANDARD_ID, 5) != 0 ||
+                   buf[0] == ISO_VD_END)
+                       return (-1);
+               if (buf[0] == ISO_VD_PRIMARY)
+                       break;
+       }
+       if (off >= 100)
+               return (-1);
+
+       /* Create an imaginary disk label */
+       label->d_secsize = 2048;
+       label->d_ntracks = 1;
+       label->d_nsectors = 100;
+       label->d_ncylinders = 1;
+       label->d_secpercyl = label->d_ntracks * label->d_nsectors;
+
+       strncpy(label->d_typename, "ATAPI CD-ROM", sizeof(label->d_typename));
+       label->d_type = DTYPE_ATAPI;
+
+       strncpy(label->d_packname, "fictitious", sizeof(label->d_packname));
+       DL_SETDSIZE(label, 100);
+
+       label->d_bbsize = 2048;
+       label->d_sbsize = 2048;
+
+       /* 'a' partition covering the "whole" disk */
+       DL_SETPOFFSET(&label->d_partitions[0], 0);
+       DL_SETPSIZE(&label->d_partitions[0], 100);
+       label->d_partitions[0].p_fstype = FS_UNUSED;
+
+       /* The raw partition is special */
+       DL_SETPOFFSET(&label->d_partitions[RAW_PART], 0);
+       DL_SETPSIZE(&label->d_partitions[RAW_PART], 100);
+       label->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
+
+       label->d_npartitions = MAXPARTITIONS;
+
+       label->d_magic = DISKMAGIC;
+       label->d_magic2 = DISKMAGIC;
+       label->d_checksum = dkcksum(label);
+
+       return (0);
+}
+
+int
+efiopen(struct open_file *f, ...)
+{
+       struct diskinfo *dip = NULL;
+       va_list ap;
+       u_int unit, part;
+       int i = 0;
+
+       va_start(ap, f);
+       unit = va_arg(ap, u_int);
+       part = va_arg(ap, u_int);
+       va_end(ap);
+
+       if (part >= MAXPARTITIONS)
+               return (ENXIO);
+
+       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;
+}
+
+int
+efistrategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
+    size_t *rsize)
+{
+       struct diskinfo *dip = (struct diskinfo *)devdata;
+       int error = 0;
+       size_t nsect;
+
+       nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
+       blk += DL_SECTOBLK(&dip->disklabel,
+           dip->disklabel.d_partitions[dip->part].p_offset);
+
+       if (blk < 0)
+               error = EINVAL;
+       else
+               error = efid_diskio(rw, dip, blk, nsect, buf);
+
+       if (rsize != NULL)
+               *rsize = nsect * DEV_BSIZE;
+
+       return (error);
+}
+
+int
+eficlose(struct open_file *f)
+{
+       f->f_devdata = NULL;
+
+       return 0;
+}
+
+int
+efiioctl(struct open_file *f, u_long cmd, void *data)
+{
+       return 0;
+}
diff --git a/sys/arch/riscv64/stand/efiboot/efidev.h b/sys/arch/riscv64/stand/efiboot/efidev.h
new file mode 100644 (file)
index 0000000..b7e5aa8
--- /dev/null
@@ -0,0 +1,36 @@
+/*     $OpenBSD: efidev.h,v 1.1 2021/04/28 19:01:00 drahn Exp $        */
+
+/*
+ * Copyright (c) 1996 Michael Shalayeff
+ * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* efidev.c */
+void            efid_init(struct diskinfo *, void *handle);
+int             efiopen(struct open_file *, ...);
+int             efistrategy(void *, int, daddr_t, size_t, void *, size_t *);
+int             eficlose(struct open_file *);
+int             efiioctl(struct open_file *, u_long, void *);
diff --git a/sys/arch/riscv64/stand/efiboot/efipxe.c b/sys/arch/riscv64/stand/efiboot/efipxe.c
new file mode 100644 (file)
index 0000000..7fe706e
--- /dev/null
@@ -0,0 +1,524 @@
+/*     $OpenBSD: efipxe.c,v 1.1 2021/04/28 19:01:00 drahn Exp $        */
+/*
+ * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * 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/disklabel.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <libsa.h>
+#include <lib/libsa/tftp.h>
+#include <lib/libsa/net.h>
+#include <lib/libsa/netif.h>
+
+#include <efi.h>
+#include <efiapi.h>
+#include "eficall.h"
+#include "efiboot.h"
+#include "disk.h"
+
+extern EFI_BOOT_SERVICES       *BS;
+extern EFI_DEVICE_PATH         *efi_bootdp;
+
+extern char                    *bootmac;
+static UINT8                    boothw[16];
+struct in_addr                  bootip, servip;
+extern struct in_addr           gateip;
+static EFI_GUID                         devp_guid = DEVICE_PATH_PROTOCOL;
+static EFI_GUID                         net_guid = EFI_SIMPLE_NETWORK_PROTOCOL;
+static EFI_GUID                         pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL;
+static EFI_SIMPLE_NETWORK      *NET = NULL;
+static EFI_PXE_BASE_CODE       *PXE = NULL;
+static EFI_PHYSICAL_ADDRESS     txbuf;
+static int                      use_mtftp = 0;
+
+extern int      efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
+extern int      efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
+
+int             efinet_probe(struct netif *, void *);
+int             efinet_match(struct netif *, void *);
+void            efinet_init(struct iodesc *, void *);
+int             efinet_get(struct iodesc *, void *, size_t, time_t);
+int             efinet_put(struct iodesc *, void *, size_t);
+void            efinet_end(struct netif *);
+
+/*
+ * TFTP initial probe.  This function discovers PXE handles and tries
+ * to figure out if there has already been a successful PXE handshake.
+ * If so, set the PXE variable.
+ */
+void
+efi_pxeprobe(void)
+{
+       EFI_SIMPLE_NETWORK *net;
+       EFI_PXE_BASE_CODE *pxe;
+       EFI_DEVICE_PATH *dp0;
+       EFI_HANDLE *handles;
+       EFI_STATUS status;
+       UINTN nhandles;
+       int i, depth;
+
+       if (efi_bootdp == NULL)
+               return;
+
+       status = EFI_CALL(BS->LocateHandleBuffer, ByProtocol, &pxe_guid, NULL,
+           &nhandles, &handles);
+       if (status != EFI_SUCCESS)
+               return;
+
+       for (i = 0; i < nhandles; i++) {
+               EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp;
+
+               status = EFI_CALL(BS->HandleProtocol, handles[i],
+                   &devp_guid, (void **)&dp0);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               depth = efi_device_path_depth(efi_bootdp, MESSAGING_DEVICE_PATH);
+               if (depth == -1 || efi_device_path_ncmp(efi_bootdp, dp0, depth))
+                       continue;
+
+               status = EFI_CALL(BS->HandleProtocol, handles[i], &net_guid,
+                   (void **)&net);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               status = EFI_CALL(BS->HandleProtocol, handles[i], &pxe_guid,
+                   (void **)&pxe);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               if (pxe->Mode == NULL)
+                       continue;
+
+               if (pxe->Mtftp != NULL) {
+                       status = EFI_CALL(pxe->Mtftp, NULL, 0, NULL,
+                           FALSE, NULL, NULL, NULL, NULL, NULL, FALSE);
+                       if (status != EFI_UNSUPPORTED)
+                               use_mtftp = 1;
+               }
+
+               dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *)&pxe->Mode->DhcpAck;
+               memcpy(&bootip, dhcp->BootpYiAddr, sizeof(bootip));
+               memcpy(&servip, dhcp->BootpSiAddr, sizeof(servip));
+               memcpy(&gateip, dhcp->BootpSiAddr, sizeof(gateip));
+               memcpy(boothw, dhcp->BootpHwAddr, sizeof(boothw));
+               bootmac = boothw;
+               NET = net;
+               PXE = pxe;
+               break;
+       }
+}
+
+/*
+ * TFTP filesystem layer implementation.
+ */
+struct mtftp_handle {
+       unsigned char   *inbuf; /* input buffer */
+       size_t           inbufsize;
+       off_t            inbufoff;
+};
+
+int
+mtftp_open(char *path, struct open_file *f)
+{
+       struct mtftp_handle *tftpfile;
+       EFI_PHYSICAL_ADDRESS addr;
+       EFI_IP_ADDRESS dstip;
+       EFI_STATUS status;
+       UINT64 size;
+
+       if (strcmp("tftp", f->f_dev->dv_name) != 0)
+               return ENXIO;
+
+       if (PXE == NULL)
+               return ENXIO;
+
+       if (!use_mtftp)
+               return ENXIO;
+
+       tftpfile = alloc(sizeof(*tftpfile));
+       if (tftpfile == NULL)
+               return ENOMEM;
+       memset(tftpfile, 0, sizeof(*tftpfile));
+
+       memcpy(&dstip, &servip, sizeof(servip));
+       status = EFI_CALL(PXE->Mtftp, PXE, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+           NULL, FALSE, &size, NULL, &dstip, path, NULL, FALSE);
+       if (status != EFI_SUCCESS) {
+               free(tftpfile, sizeof(*tftpfile));
+               return ENOENT;
+       }
+       tftpfile->inbufsize = size;
+
+       if (tftpfile->inbufsize == 0)
+               goto out;
+
+       status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData,
+           EFI_SIZE_TO_PAGES(tftpfile->inbufsize), &addr);
+       if (status != EFI_SUCCESS) {
+               free(tftpfile, sizeof(*tftpfile));
+               return ENOMEM;
+       }
+       tftpfile->inbuf = (unsigned char *)((paddr_t)addr);
+
+       status = EFI_CALL(PXE->Mtftp, PXE, EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+           tftpfile->inbuf, FALSE, &size, NULL, &dstip, path, NULL, FALSE);
+       if (status != EFI_SUCCESS) {
+               free(tftpfile, sizeof(*tftpfile));
+               return ENXIO;
+       }
+out:
+       f->f_fsdata = tftpfile;
+       return 0;
+}
+
+int
+mtftp_close(struct open_file *f)
+{
+       struct mtftp_handle *tftpfile = f->f_fsdata;
+
+       if (tftpfile->inbuf != NULL)
+               EFI_CALL(BS->FreePages, (paddr_t)tftpfile->inbuf,
+                   EFI_SIZE_TO_PAGES(tftpfile->inbufsize));
+       free(tftpfile, sizeof(*tftpfile));
+       return 0;
+}
+
+int
+mtftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
+{
+       struct mtftp_handle *tftpfile = f->f_fsdata;
+       size_t toread;
+
+       if (size > tftpfile->inbufsize - tftpfile->inbufoff)
+               toread = tftpfile->inbufsize - tftpfile->inbufoff;
+       else
+               toread = size;
+
+       if (toread != 0) {
+               memcpy(addr, tftpfile->inbuf + tftpfile->inbufoff, toread);
+               tftpfile->inbufoff += toread;
+       }
+
+       if (resid != NULL)
+               *resid = size - toread;
+       return 0;
+}
+
+int
+mtftp_write(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+       return EROFS;
+}
+
+off_t
+mtftp_seek(struct open_file *f, off_t offset, int where)
+{
+       struct mtftp_handle *tftpfile = f->f_fsdata;
+
+       switch(where) {
+       case SEEK_CUR:
+               if (tftpfile->inbufoff + offset < 0 ||
+                   tftpfile->inbufoff + offset > tftpfile->inbufsize) {
+                       errno = EOFFSET;
+                       break;
+               }
+               tftpfile->inbufoff += offset;
+               return (tftpfile->inbufoff);
+       case SEEK_SET:
+               if (offset < 0 || offset > tftpfile->inbufsize) {
+                       errno = EOFFSET;
+                       break;
+               }
+               tftpfile->inbufoff = offset;
+               return (tftpfile->inbufoff);
+       case SEEK_END:
+               tftpfile->inbufoff = tftpfile->inbufsize;
+               return (tftpfile->inbufoff);
+       default:
+               errno = EINVAL;
+       }
+       return((off_t)-1);
+}
+
+int
+mtftp_stat(struct open_file *f, struct stat *sb)
+{
+       struct mtftp_handle *tftpfile = f->f_fsdata;
+
+       sb->st_mode = 0444;
+       sb->st_nlink = 1;
+       sb->st_uid = 0;
+       sb->st_gid = 0;
+       sb->st_size = tftpfile->inbufsize;
+
+       return 0;
+}
+
+int
+mtftp_readdir(struct open_file *f, char *name)
+{
+       return EOPNOTSUPP;
+}
+
+/*
+ * Overload generic TFTP implementation to check that
+ * we actually have a driver.
+ */
+int
+efitftp_open(char *path, struct open_file *f)
+{
+       if (strcmp("tftp", f->f_dev->dv_name) != 0)
+               return ENXIO;
+
+       if (NET == NULL || PXE == NULL)
+               return ENXIO;
+
+       if (use_mtftp)
+               return ENXIO;
+
+       return tftp_open(path, f);
+}
+
+/*
+ * Dummy network device.
+ */
+int tftpdev_sock = -1;
+
+int
+tftpopen(struct open_file *f, ...)
+{
+       EFI_STATUS status;
+       u_int unit, part;
+       va_list ap;
+
+       va_start(ap, f);
+       unit = va_arg(ap, u_int);
+       part = va_arg(ap, u_int);
+       va_end(ap);
+
+       /* No PXE set -> no PXE available */
+       if (PXE == NULL)
+               return 1;
+
+       if (unit != 0)
+               return 1;
+
+       if (!use_mtftp) {
+               status = EFI_CALL(BS->AllocatePages, AllocateAnyPages,
+                   EfiLoaderData, EFI_SIZE_TO_PAGES(RECV_SIZE), &txbuf);
+               if (status != EFI_SUCCESS)
+                       return ENOMEM;
+
+               if ((tftpdev_sock = netif_open("efinet")) < 0) {
+                       EFI_CALL(BS->FreePages, txbuf,
+                           EFI_SIZE_TO_PAGES(RECV_SIZE));
+                       return ENXIO;
+               }
+
+               f->f_devdata = &tftpdev_sock;
+       }
+
+       return 0;
+}
+
+int
+tftpclose(struct open_file *f)
+{
+       int ret = 0;
+
+       if (!use_mtftp) {
+               ret = netif_close(*(int *)f->f_devdata);
+               EFI_CALL(BS->FreePages, txbuf, EFI_SIZE_TO_PAGES(RECV_SIZE));
+               txbuf = 0;
+       }
+
+       return ret;
+}
+
+int
+tftpioctl(struct open_file *f, u_long cmd, void *data)
+{
+       return EOPNOTSUPP;
+}
+
+int
+tftpstrategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
+       size_t *rsize)
+{
+       return EOPNOTSUPP;
+}
+
+/*
+ * Simple Network Protocol driver.
+ */
+struct netif_stats efinet_stats;
+struct netif_dif efinet_ifs[] = {
+       { 0, 1, &efinet_stats, 0, 0, },
+};
+
+struct netif_driver efinet_driver = {
+       "efinet",
+       efinet_match,
+       efinet_probe,
+       efinet_init,
+       efinet_get,
+       efinet_put,
+       efinet_end,
+       efinet_ifs,
+       nitems(efinet_ifs)
+};
+
+int
+efinet_match(struct netif *nif, void *v)
+{
+       return 1;
+}
+
+int
+efinet_probe(struct netif *nif, void *v)
+{
+       if (strncmp(v, efinet_driver.netif_bname, 3))
+               return -1;
+
+       return 0;
+}
+
+void
+efinet_init(struct iodesc *desc, void *v)
+{
+       EFI_SIMPLE_NETWORK *net = NET;
+       EFI_STATUS status;
+
+       if (net == NULL)
+               return;
+
+       if (net->Mode->State == EfiSimpleNetworkStopped) {
+               status = EFI_CALL(net->Start, net);
+               if (status != EFI_SUCCESS)
+                       return;
+       }
+
+       if (net->Mode->State != EfiSimpleNetworkInitialized) {
+               status = EFI_CALL(net->Initialize, net, 0, 0);
+               if (status != EFI_SUCCESS)
+                       return;
+       }
+
+       EFI_CALL(net->ReceiveFilters, net,
+           EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+           EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST,
+           0, FALSE, 0, NULL);
+
+       memcpy(desc->myea, net->Mode->CurrentAddress.Addr, 6);
+       memcpy(&desc->myip, &bootip, sizeof(bootip));
+       desc->xid = 1;
+}
+
+int
+efinet_get(struct iodesc *desc, void *pkt, size_t len, time_t tmo)
+{
+       EFI_SIMPLE_NETWORK *net = NET;
+       EFI_STATUS status;
+       UINTN bufsz, pktsz;
+       time_t t;
+       char *buf, *ptr;
+       ssize_t ret = -1;
+
+       if (net == NULL)
+               return ret;
+
+       bufsz = net->Mode->MaxPacketSize + ETHER_HDR_LEN + ETHER_CRC_LEN;
+       buf = alloc(bufsz + ETHER_ALIGN);
+       if (buf == NULL)
+               return ret;
+       ptr = buf + ETHER_ALIGN;
+
+       t = getsecs();
+       status = EFI_NOT_READY;
+       while ((getsecs() - t) < tmo) {
+               pktsz = bufsz;
+               status = EFI_CALL(net->Receive, net, NULL, &pktsz, ptr,
+                   NULL, NULL, NULL);
+               if (status == EFI_SUCCESS)
+                       break;
+               if (status != EFI_NOT_READY)
+                       break;
+       }
+
+       if (status == EFI_SUCCESS) {
+               memcpy(pkt, ptr, min((ssize_t)pktsz, len));
+               ret = min((ssize_t)pktsz, len);
+       }
+
+       free(buf, bufsz + ETHER_ALIGN);
+       return ret;
+}
+
+int
+efinet_put(struct iodesc *desc, void *pkt, size_t len)
+{
+       EFI_SIMPLE_NETWORK *net = NET;
+       EFI_STATUS status;
+       void *buf = NULL;
+       int ret = -1;
+
+       if (net == NULL)
+               goto out;
+
+       if (len > RECV_SIZE)
+               goto out;
+
+       memcpy((void *)txbuf, pkt, len);
+       status = EFI_CALL(net->Transmit, net, 0, len, (void *)txbuf,
+           NULL, NULL, NULL);
+       if (status != EFI_SUCCESS)
+               goto out;
+
+       buf = NULL;
+       while (status == EFI_SUCCESS) {
+               status = EFI_CALL(net->GetStatus, net, NULL, &buf);
+               if (buf)
+                       break;
+       }
+
+       if (status == EFI_SUCCESS)
+               ret = len;
+
+out:
+       return ret;
+}
+
+void
+efinet_end(struct netif *nif)
+{
+       EFI_SIMPLE_NETWORK *net = NET;
+
+       if (net == NULL)
+               return;
+
+       EFI_CALL(net->Shutdown, net);
+}
diff --git a/sys/arch/riscv64/stand/efiboot/efipxe.h b/sys/arch/riscv64/stand/efiboot/efipxe.h
new file mode 100644 (file)
index 0000000..3a0529a
--- /dev/null
@@ -0,0 +1,33 @@
+/*     $OpenBSD: efipxe.h,v 1.1 2021/04/28 19:01:00 drahn Exp $        */
+/*
+ * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * 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.
+ */
+
+extern struct netif_driver efinet_driver;
+
+int     efitftp_open(char *path, struct open_file *f);
+
+int     mtftp_open(char *, struct open_file *);
+int     mtftp_close(struct open_file *);
+int     mtftp_read(struct open_file *, void *, size_t, size_t *);
+int     mtftp_write(struct open_file *, void *, size_t, size_t *);
+off_t   mtftp_seek(struct open_file *, off_t, int);
+int     mtftp_stat(struct open_file *, struct stat *);
+int     mtftp_readdir(struct open_file *, char *);
+
+int     tftpopen(struct open_file *, ...);
+int     tftpclose(struct open_file *);
+int     tftpioctl(struct open_file *, u_long, void *);
+int     tftpstrategy(void *, int, daddr_t, size_t, void *, size_t *);
diff --git a/sys/arch/riscv64/stand/efiboot/efirng.c b/sys/arch/riscv64/stand/efiboot/efirng.c
new file mode 100644 (file)
index 0000000..1662e44
--- /dev/null
@@ -0,0 +1,90 @@
+/*     $OpenBSD: efirng.c,v 1.1 2021/04/28 19:01:00 drahn Exp $        */
+
+/*
+ * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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 <efi.h>
+#include <efiapi.h>
+
+#include "eficall.h"
+#include "libsa.h"
+
+extern EFI_BOOT_SERVICES       *BS;
+
+/* Random Number Generator Protocol */
+
+#define EFI_RNG_PROTOCOL_GUID \
+    { 0x3152bca5, 0xeade, 0x433d, {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44} }
+
+INTERFACE_DECL(_EFI_RNG_PROTOCOL);
+
+typedef EFI_GUID EFI_RNG_ALGORITHM;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RNG_GET_INFO) (
+    IN struct _EFI_RNG_PROTOCOL                *This,
+    IN  OUT UINTN                      *RNGAlgorithmListSize,
+    OUT EFI_RNG_ALGORITHM              *RNGAlgorithmList
+    );
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RNG_GET_RNG) (
+    IN struct _EFI_RNG_PROTOCOL                *This,
+    IN EFI_RNG_ALGORITHM               *RNGAlgorithm, OPTIONAL
+    IN UINTN                           RNGValueLength,
+    OUT UINT8                          *RNGValue
+    );
+
+typedef struct _EFI_RNG_PROTOCOL {
+       EFI_RNG_GET_INFO        GetInfo;
+       EFI_RNG_GET_RNG         GetRNG;
+} EFI_RNG_PROTOCOL;
+
+static EFI_GUID                        rng_guid = EFI_RNG_PROTOCOL_GUID;
+
+int
+fwrandom(char *buf, size_t buflen)
+{
+       EFI_STATUS               status;
+       EFI_RNG_PROTOCOL        *rng = NULL;
+       UINT8                   *random;
+       size_t                   i;
+       int                      ret = 0;
+
+       status = EFI_CALL(BS->LocateProtocol, &rng_guid, NULL, (void **)&rng);
+       if (rng == NULL || EFI_ERROR(status))
+               return -1;
+
+       random = alloc(buflen);
+
+       status = EFI_CALL(rng->GetRNG, rng, NULL, buflen, random);
+       if (EFI_ERROR(status)) {
+               printf("RNG GetRNG() failed (%d)\n", status);
+               ret = -1;
+               goto out;
+       }
+
+       for (i = 0; i < buflen; i++)
+               buf[i] ^= random[i];
+
+out:
+       free(random, buflen);
+       return ret;
+}
diff --git a/sys/arch/riscv64/stand/efiboot/exec.c b/sys/arch/riscv64/stand/efiboot/exec.c
new file mode 100644 (file)
index 0000000..0a71cce
--- /dev/null
@@ -0,0 +1,89 @@
+/*     $OpenBSD: exec.c,v 1.1 2021/04/28 19:01:00 drahn Exp $  */
+
+/*
+ * Copyright (c) 2006, 2016 Mark Kettenis
+ *
+ * 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/reboot.h>
+#include <dev/cons.h>
+
+#include <lib/libkern/libkern.h>
+#include <lib/libsa/loadfile.h>
+#include <sys/exec_elf.h>
+
+#include <efi.h>
+#include <stand/boot/cmd.h>
+
+#include "efiboot.h"
+#include "libsa.h"
+#include "fdt.h"
+
+typedef void (*startfuncp)(void *, void *, void *) __attribute__ ((noreturn));
+
+unsigned int cpu_get_dcache_line_size(void);
+void cpu_flush_dcache(vaddr_t, vsize_t);
+void cpu_inval_icache(void);
+
+void
+cpu_flush_dcache(vaddr_t addr, vsize_t len)
+{
+       __asm volatile("fence" ::: "memory");
+}
+
+void
+cpu_inval_icache(void)
+{
+       __asm volatile("fence.i" ::: "memory");
+}
+
+void
+run_loadfile(uint64_t *marks, int howto)
+{
+       char args[256];
+       char *cp;
+       void *fdt;
+
+       strlcpy(args, cmd.path, sizeof(args));
+       cp = args + strlen(args);
+
+       *cp++ = ' ';
+       *cp = '-';
+       if (howto & RB_ASKNAME)
+               *++cp = 'a';
+       if (howto & RB_CONFIG)
+               *++cp = 'c';
+       if (howto & RB_SINGLE)
+               *++cp = 's';
+       if (howto & RB_KDB)
+               *++cp = 'd';
+       if (*cp == '-')
+               *--cp = 0;
+       else
+               *++cp = 0;
+
+       fdt = efi_makebootargs(args, howto);
+
+       efi_cleanup();
+
+       cpu_flush_dcache(marks[MARK_ENTRY], marks[MARK_END] - marks[MARK_ENTRY]);
+       cpu_inval_icache();
+
+       cpu_flush_dcache((vaddr_t)fdt, fdt_get_size(fdt));
+
+       (*(startfuncp)(marks[MARK_ENTRY]))((void *)marks[MARK_END], 0, fdt);
+
+       /* NOTREACHED */
+}
diff --git a/sys/arch/riscv64/stand/efiboot/fdt.c b/sys/arch/riscv64/stand/efiboot/fdt.c
new file mode 100644 (file)
index 0000000..0985fa3
--- /dev/null
@@ -0,0 +1,641 @@
+/*     $OpenBSD: fdt.c,v 1.1 2021/04/28 19:01:00 drahn Exp $   */
+
+/*
+ * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
+ * Copyright (c) 2009, 2016 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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/systm.h>
+
+#include <lib/libkern/libkern.h>
+
+#include "fdt.h"
+
+unsigned int fdt_check_head(void *);
+char   *fdt_get_str(uint32_t);
+void   *skip_property(uint32_t *);
+void   *skip_props(uint32_t *);
+void   *skip_node_name(uint32_t *);
+void   *skip_node(void *);
+void   *fdt_parent_node_recurse(void *, void *);
+
+static int tree_inited = 0;
+static struct fdt tree;
+
+unsigned int
+fdt_check_head(void *fdt)
+{
+       struct fdt_head *fh;
+       uint32_t *ptr;
+
+       fh = fdt;
+       ptr = (uint32_t *)fdt;
+
+       if (betoh32(fh->fh_magic) != FDT_MAGIC)
+               return 0;
+
+       if (betoh32(fh->fh_version) > FDT_CODE_VERSION)
+               return 0;
+
+       if (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4))) !=
+           FDT_NODE_BEGIN)
+               return 0;
+
+       /* check for end signature on version 17 blob */
+       if ((betoh32(fh->fh_version) >= 17) &&
+           (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) +
+           (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END))
+               return 0;
+
+       return betoh32(fh->fh_version);
+}
+
+/*
+ * Initializes internal structures of module.
+ * Has to be called once.
+ */
+int
+fdt_init(void *fdt)
+{
+       int version;
+
+       memset(&tree, 0, sizeof(struct fdt));
+       tree_inited = 0;
+
+       if (!fdt)
+               return 0;
+
+       if (!(version = fdt_check_head(fdt)))
+               return 0;
+
+       tree.header = (struct fdt_head *)fdt;
+       tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off);
+       tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off);
+       tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off);
+       tree.end = (char *)fdt + betoh32(tree.header->fh_size);
+       tree.version = version;
+       tree.strings_size = betoh32(tree.header->fh_strings_size);
+       if (tree.version >= 17)
+               tree.struct_size = betoh32(tree.header->fh_struct_size);
+       tree_inited = 1;
+
+       return version;
+}
+
+void
+fdt_finalize(void)
+{
+       char *start = (char *)tree.header;
+
+       tree.header->fh_size = htobe32(tree.end - start);
+       tree.header->fh_struct_off = htobe32(tree.tree - start);
+       tree.header->fh_strings_off = htobe32(tree.strings - start);
+       tree.header->fh_reserve_off = htobe32(tree.memory - start);
+       tree.header->fh_strings_size = htobe32(tree.strings_size);
+       if (tree.version >= 17)
+               tree.header->fh_struct_size = htobe32(tree.struct_size);
+}
+
+/*
+ * Return the size of the FDT.
+ */
+size_t
+fdt_get_size(void *fdt)
+{
+       if (!fdt)
+               return 0;
+
+       if (!fdt_check_head(fdt))
+               return 0;
+
+       return betoh32(((struct fdt_head *)fdt)->fh_size);
+}
+
+/*
+ * Retrieve string pointer from strings table.
+ */
+char *
+fdt_get_str(uint32_t num)
+{
+       if (num > tree.strings_size)
+               return NULL;
+       return (tree.strings) ? (tree.strings + num) : NULL;
+}
+
+int
+fdt_add_str(char *name)
+{
+       size_t len = roundup(strlen(name) + 1, sizeof(uint32_t));
+       char *end = tree.strings + tree.strings_size;
+
+       memmove(end + len, end, tree.end - end);
+       tree.strings_size += len;
+       if (tree.tree > tree.strings)
+               tree.tree += len;
+       if (tree.memory > tree.strings)
+               tree.memory += len;
+       tree.end += len;
+       memset(end, 0, len);
+       memcpy(end, name, strlen(name));
+
+       return (end - tree.strings);
+}
+
+/*
+ * Utility functions for skipping parts of tree.
+ */
+void *
+skip_property(uint32_t *ptr)
+{
+       uint32_t size;
+
+       size = betoh32(*(ptr + 1));
+       /* move forward by magic + size + nameid + rounded up property size */
+       ptr += 3 + roundup(size, sizeof(uint32_t)) / sizeof(uint32_t);
+
+       return ptr;
+}
+
+void *
+skip_props(uint32_t *ptr)
+{
+       while (betoh32(*ptr) == FDT_PROPERTY) {
+               ptr = skip_property(ptr);
+       }
+       return ptr;
+}
+
+void *
+skip_node_name(uint32_t *ptr)
+{
+       /* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */
+       return ptr + roundup(strlen((char *)ptr) + 1,
+           sizeof(uint32_t)) / sizeof(uint32_t);
+}
+
+/*
+ * Retrieves node property, the returned pointer is inside the fdt tree,
+ * so we should not modify content pointed by it directly.
+ */
+int
+fdt_node_property(void *node, char *name, char **out)
+{
+       uint32_t *ptr;
+       uint32_t nameid;
+       char *tmp;
+       
+       if (!tree_inited)
+               return 0;
+
+       ptr = (uint32_t *)node;
+
+       if (betoh32(*ptr) != FDT_NODE_BEGIN)
+               return 0;
+
+       ptr = skip_node_name(ptr + 1);
+
+       while (betoh32(*ptr) == FDT_PROPERTY) {
+               nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
+               tmp = fdt_get_str(nameid);
+               if (!strcmp(name, tmp)) {
+                       *out = (char *)(ptr + 3); /* beginning of the value */
+                       return betoh32(*(ptr + 1)); /* size of value */
+               }
+               ptr = skip_property(ptr);
+       }
+       return 0;
+}
+
+int
+fdt_node_set_property(void *node, char *name, void *data, int len)
+{
+       uint32_t *ptr, *next;
+       uint32_t nameid;
+       uint32_t curlen;
+       size_t delta;
+       char *tmp;
+
+       if (!tree_inited)
+               return 0;
+
+       ptr = (uint32_t *)node;
+
+       if (betoh32(*ptr) != FDT_NODE_BEGIN)
+               return 0;
+
+       ptr = skip_node_name(ptr + 1);
+
+       while (betoh32(*ptr) == FDT_PROPERTY) {
+               nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
+               tmp = fdt_get_str(nameid);
+               next = skip_property(ptr);
+               if (!strcmp(name, tmp)) {
+                       curlen = betoh32(*(ptr + 1));
+                       delta = roundup(len, sizeof(uint32_t)) -
+                           roundup(curlen, sizeof(uint32_t));
+                       memmove((char *)next + delta, next,
+                           tree.end - (char *)next);
+                       tree.struct_size += delta;
+                       if (tree.strings > tree.tree)
+                               tree.strings += delta;
+                       if (tree.memory > tree.tree)
+                               tree.memory += delta;
+                       tree.end += delta;
+                       *(ptr + 1) = htobe32(len);
+                       memcpy(ptr + 3, data, len);
+                       return 1;
+               }
+               ptr = next;
+       }
+       return 0;
+}
+
+int
+fdt_node_add_property(void *node, char *name, void *data, int len)
+{
+       char *dummy;
+
+       if (!tree_inited)
+               return 0;
+
+       if (!fdt_node_property(node, name, &dummy)) {
+               uint32_t *ptr = (uint32_t *)node;
+
+               if (betoh32(*ptr) != FDT_NODE_BEGIN)
+                       return 0;
+
+               ptr = skip_node_name(ptr + 1);
+
+               memmove(ptr + 3, ptr, tree.end - (char *)ptr);
+               tree.struct_size += 3 * sizeof(uint32_t);
+               if (tree.strings > tree.tree)
+                       tree.strings += 3 * sizeof(uint32_t);
+               if (tree.memory > tree.tree)
+                       tree.memory += 3 * sizeof(uint32_t);
+               tree.end += 3 * sizeof(uint32_t);
+               *ptr++ = htobe32(FDT_PROPERTY);
+               *ptr++ = htobe32(0);
+               *ptr++ = htobe32(fdt_add_str(name));
+       }
+
+       return fdt_node_set_property(node, name, data, len);
+}
+
+int
+fdt_node_add_node(void *node, char *name, void **child)
+{
+       size_t len = roundup(strlen(name) + 1, sizeof(uint32_t)) + 8;
+       uint32_t *ptr = (uint32_t *)node;
+
+       if (!tree_inited)
+               return 0;
+
+       if (betoh32(*ptr) != FDT_NODE_BEGIN)
+               return 0;
+
+       ptr = skip_node_name(ptr + 1);
+       ptr = skip_props(ptr);
+
+       /* skip children */
+       while (betoh32(*ptr) == FDT_NODE_BEGIN)
+               ptr = skip_node(ptr);
+
+       memmove((char *)ptr + len, ptr, tree.end - (char *)ptr);
+       tree.struct_size += len;
+       if (tree.strings > tree.tree)
+               tree.strings += len;
+       if (tree.memory > tree.tree)
+               tree.memory += len;
+       tree.end += len;
+
+       *child = ptr;
+       *ptr++ = htobe32(FDT_NODE_BEGIN);
+       memset(ptr, 0, len - 8);
+       memcpy(ptr, name, strlen(name));
+       ptr += (len - 8) / sizeof(uint32_t);
+       *ptr++ = htobe32(FDT_NODE_END);
+
+       return 1;
+}
+
+/*
+ * Retrieves next node, skipping all the children nodes of the pointed node,
+ * returns pointer to next node, no matter if it exists or not.
+ */
+void *
+skip_node(void *node)
+{
+       uint32_t *ptr = node;
+
+       ptr++;
+
+       ptr = skip_node_name(ptr);
+       ptr = skip_props(ptr);
+
+       /* skip children */
+       while (betoh32(*ptr) == FDT_NODE_BEGIN)
+               ptr = skip_node(ptr);
+
+       return (ptr + 1);
+}
+
+/*
+ * Retrieves next node, skipping all the children nodes of the pointed node,
+ * returns pointer to next node if exists, otherwise returns NULL.
+ * If passed 0 will return first node of the tree (root).
+ */
+void *
+fdt_next_node(void *node)
+{
+       uint32_t *ptr;
+
+       if (!tree_inited)
+               return NULL;
+
+       ptr = node;
+
+       if (node == NULL) {
+               ptr = (uint32_t *)tree.tree;
+               return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL;
+       }
+
+       if (betoh32(*ptr) != FDT_NODE_BEGIN)
+               return NULL;
+
+       ptr++;
+
+       ptr = skip_node_name(ptr);
+       ptr = skip_props(ptr);
+
+       /* skip children */
+       while (betoh32(*ptr) == FDT_NODE_BEGIN)
+               ptr = skip_node(ptr);
+
+       if (betoh32(*ptr) != FDT_NODE_END)
+               return NULL;
+
+       if (betoh32(*(ptr + 1)) != FDT_NODE_BEGIN)
+               return NULL;
+
+       return (ptr + 1);
+}
+
+/*
+ * Retrieves node property as integers and puts them in the given
+ * integer array.
+ */
+int
+fdt_node_property_ints(void *node, char *name, int *out, int outlen)
+{
+       int *data;
+       int i, inlen;
+
+       inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int);
+       if (inlen <= 0)
+               return -1;
+
+       for (i = 0; i < inlen && i < outlen; i++)
+               out[i] = betoh32(data[i]);
+
+       return i;
+}
+
+/*
+ * Retrieves node property as an integer.
+ */
+int
+fdt_node_property_int(void *node, char *name, int *out)
+{
+       return fdt_node_property_ints(node, name, out, 1);
+}
+
+/*
+ * Retrieves next node, skipping all the children nodes of the pointed node
+ */
+void *
+fdt_child_node(void *node)
+{
+       uint32_t *ptr;
+
+       if (!tree_inited)
+               return NULL;
+
+       ptr = node;
+
+       if (betoh32(*ptr) != FDT_NODE_BEGIN)
+               return NULL;
+
+       ptr++;
+
+       ptr = skip_node_name(ptr);
+       ptr = skip_props(ptr);
+       /* check if there is a child node */
+       return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL;
+}
+
+/*
+ * Retrieves node name.
+ */
+char *
+fdt_node_name(void *node)
+{
+       uint32_t *ptr;
+
+       if (!tree_inited)
+               return NULL;
+
+       ptr = node;
+
+       if (betoh32(*ptr) != FDT_NODE_BEGIN)
+               return NULL;
+
+       return (char *)(ptr + 1);
+}
+
+void *
+fdt_find_node(char *name)
+{
+       void *node = fdt_next_node(0);
+       const char *p = name;
+
+       if (!tree_inited)
+               return NULL;
+
+       if (*p != '/')
+               return NULL;
+
+       while (*p) {
+               void *child;
+               const char *q;
+
+               while (*p == '/')
+                       p++;
+               if (*p == 0)
+                       return node;
+               q = strchr(p, '/');
+               if (q == NULL)
+                       q = p + strlen(p);
+
+               for (child = fdt_child_node(node); child;
+                    child = fdt_next_node(child)) {
+                       if (strncmp(p, fdt_node_name(child), q - p) == 0) {
+                               node = child;
+                               break;
+                       }
+               }
+
+               p = q;
+       }
+
+       return node;
+}
+
+void *
+fdt_parent_node_recurse(void *pnode, void *child)
+{
+       void *node = fdt_child_node(pnode);
+       void *tmp;
+
+       while (node && (node != child)) {
+               if ((tmp = fdt_parent_node_recurse(node, child)))
+                       return tmp;
+               node = fdt_next_node(node);
+       }
+       return (node) ? pnode : NULL;
+}
+
+void *
+fdt_parent_node(void *node)
+{
+       void *pnode = fdt_next_node(0);
+
+       if (!tree_inited)
+               return NULL;
+
+       if (node == pnode)
+               return NULL;
+
+       return fdt_parent_node_recurse(pnode, node);
+}
+
+int
+fdt_node_is_compatible(void *node, const char *name)
+{
+       char *data;
+       int len;
+
+       len = fdt_node_property(node, "compatible", &data);
+       while (len > 0) {
+               if (strcmp(data, name) == 0)
+                       return 1;
+               len -= strlen(data) + 1;
+               data += strlen(data) + 1;
+       }
+
+       return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Debug methods for printing whole tree, particular nodes and properties
+ */
+void *
+fdt_print_property(void *node, int level)
+{
+       uint32_t *ptr;
+       char *tmp, *value;
+       int cnt;
+       uint32_t nameid, size;
+
+       ptr = (uint32_t *)node;
+
+       if (!tree_inited)
+               return NULL;
+
+       if (betoh32(*ptr) != FDT_PROPERTY)
+               return ptr; /* should never happen */
+
+       /* extract property name_id and size */
+       size = betoh32(*++ptr);
+       nameid = betoh32(*++ptr);
+
+       for (cnt = 0; cnt < level; cnt++)
+               printf("\t");
+
+       tmp = fdt_get_str(nameid);
+       printf("\t%s : ", tmp ? tmp : "NO_NAME");
+
+       ptr++;
+       value = (char *)ptr;
+
+       if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") ||
+           !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") ||
+           !strcmp(tmp, "linux,stdout-path")) {
+               printf("%s", value);
+       } else if (!strcmp(tmp, "clock-frequency") ||
+           !strcmp(tmp, "timebase-frequency")) {
+               printf("%d", betoh32(*((unsigned int *)value)));
+       } else {
+               for (cnt = 0; cnt < size; cnt++) {
+                       if ((cnt % sizeof(uint32_t)) == 0)
+                               printf(" ");
+                       printf("%x%x", value[cnt] >> 4, value[cnt] & 0xf);
+               }
+       }
+       ptr += roundup(size, sizeof(uint32_t)) / sizeof(uint32_t);
+       printf("\n");
+
+       return ptr;
+}
+
+void
+fdt_print_node(void *node, int level)
+{
+       uint32_t *ptr;
+       int cnt;
+       
+       ptr = (uint32_t *)node;
+
+       if (betoh32(*ptr) != FDT_NODE_BEGIN)
+               return;
+
+       ptr++;
+
+       for (cnt = 0; cnt < level; cnt++)
+               printf("\t");
+       printf("%s :\n", fdt_node_name(node));
+       ptr = skip_node_name(ptr);
+
+       while (betoh32(*ptr) == FDT_PROPERTY)
+               ptr = fdt_print_property(ptr, level);
+}
+
+void
+fdt_print_node_recurse(void *node, int level)
+{
+       void *child;
+
+       fdt_print_node(node, level);
+       for (child = fdt_child_node(node); child; child = fdt_next_node(child))
+               fdt_print_node_recurse(child, level + 1);
+}
+
+void
+fdt_print_tree(void)
+{
+       fdt_print_node_recurse(fdt_next_node(0), 0);
+}
+#endif
diff --git a/sys/arch/riscv64/stand/efiboot/fdt.h b/sys/arch/riscv64/stand/efiboot/fdt.h
new file mode 100644 (file)
index 0000000..283a8dc
--- /dev/null
@@ -0,0 +1,71 @@
+/*     $OpenBSD: fdt.h,v 1.1 2021/04/28 19:01:00 drahn Exp $   */
+
+/*
+ * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
+ *
+ * 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.
+ */
+
+struct fdt_head {
+       uint32_t fh_magic;
+       uint32_t fh_size;
+       uint32_t fh_struct_off;
+       uint32_t fh_strings_off;
+       uint32_t fh_reserve_off;
+       uint32_t fh_version;
+       uint32_t fh_comp_ver;     /* last compatible version */
+       uint32_t fh_boot_cpu_id;  /* fh_version >=2 */
+       uint32_t fh_strings_size; /* fh_version >=3 */
+       uint32_t fh_struct_size;  /* fh_version >=17 */
+};
+
+struct fdt {
+       struct fdt_head *header;
+       char            *tree;
+       char            *strings;
+       char            *memory;
+       char            *end;
+       int             version;
+       int             strings_size;
+       int             struct_size;
+};
+
+#define FDT_MAGIC      0xd00dfeed
+#define FDT_NODE_BEGIN 0x01
+#define FDT_NODE_END   0x02
+#define FDT_PROPERTY   0x03
+#define FDT_NOP                0x04
+#define FDT_END                0x09
+
+#define FDT_CODE_VERSION 0x11
+
+int     fdt_init(void *);
+void    fdt_finalize(void);
+size_t  fdt_get_size(void *);
+void   *fdt_next_node(void *);
+void   *fdt_child_node(void *);
+char   *fdt_node_name(void *);
+void   *fdt_find_node(char *);
+int     fdt_node_property(void *, char *, char **);
+int     fdt_node_property_int(void *, char *, int *);
+int     fdt_node_property_ints(void *, char *, int *, int);
+int     fdt_node_set_property(void *, char *, void *, int);
+int     fdt_node_add_property(void *, char *, void *, int);
+int     fdt_node_add_node(void *, char *, void **);
+void   *fdt_parent_node(void *);
+int     fdt_node_is_compatible(void *, const char *);
+#ifdef DEBUG
+void   *fdt_print_property(void *, int);
+void    fdt_print_node(void *, int);
+void    fdt_print_tree(void);
+#endif
diff --git a/sys/arch/riscv64/stand/efiboot/heap.h b/sys/arch/riscv64/stand/efiboot/heap.h
new file mode 100644 (file)
index 0000000..960c7e7
--- /dev/null
@@ -0,0 +1,29 @@
+/*     $OpenBSD: heap.h,v 1.1 2021/04/28 19:01:00 drahn Exp $  */
+
+/*
+ * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
+ *
+ * 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 <efi.h>
+
+static char *top = NULL;
+#define        NEEDS_HEAP_INIT 1
+
+static void
+heap_init(void)
+{
+       extern EFI_PHYSICAL_ADDRESS heap;
+       if (top == NULL)
+               top = (char *)(uintptr_t)heap;
+}
diff --git a/sys/arch/riscv64/stand/efiboot/ldscript.riscv64 b/sys/arch/riscv64/stand/efiboot/ldscript.riscv64
new file mode 100644 (file)
index 0000000..7075787
--- /dev/null
@@ -0,0 +1,85 @@
+/* $OpenBSD: ldscript.riscv64,v 1.1 2021/04/28 19:01:00 drahn Exp $ */
+/*
+OUTPUT_FORMAT("elf64-aarch64-freebsd", "elf64-aarch64-freebsd", "elf64-aarch64-freebsd")
+*/
+OUTPUT_ARCH(elf64-littleriscv)
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0;
+  ImageBase = .;
+  .text                : {
+    *(.peheader)
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em. */
+    *(.gnu.warning)
+    *(.plt)
+  } =0xD4200000
+  . = ALIGN(16);
+  .data                : {
+    *(.rodata .rodata.* .gnu.linkonce.r.*)
+    *(.rodata1)
+    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+    *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
+    *(.opd)
+    *(.data .data.* .gnu.linkonce.d.*)
+    *(.data1)
+    *(.plabel)
+
+    . = ALIGN(16);
+    __bss_start = .;
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+    *(.dynbss)
+    *(.bss *.bss.*)
+    *(COMMON)
+    . = ALIGN(16);
+    __bss_end = .;
+  }
+  . = ALIGN(16);
+  set_Xcommand_set     : {
+    __start_set_Xcommand_set = .;
+    *(set_Xcommand_set)
+    __stop_set_Xcommand_set = .;
+  }
+  set_Xficl_compile_set        : {
+    __start_set_Xficl_compile_set = .;
+    *(set_Xficl_compile_set)
+    __stop_set_Xficl_compile_set = .;
+  }
+  . = ALIGN(16);
+  __gp = .;
+  .sdata       : {
+    *(.got.plt .got)
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+    *(dynsbss)
+    *(.scommon)
+  }
+  . = ALIGN(16);
+  .dynamic     : { *(.dynamic) }
+  . = ALIGN(16);
+  .rela.dyn    : {
+    *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+    *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+    *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+    *(.rela.got)
+    *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+    *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+    *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+    *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+    *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+    *(.rela.plt)
+    *(.relset_*)
+    *(.rela.dyn .rela.dyn.*)
+  }
+  . = ALIGN(16);
+  .reloc       : { *(.reloc) }
+  . = ALIGN(16);
+  .dynsym      : { *(.dynsym) }
+  .dynstr      : { *(.dynstr) }
+  _edata = .;
+
+  /* Unused sections */
+  .hash                : { *(.hash) }
+}
diff --git a/sys/arch/riscv64/stand/efiboot/libsa.h b/sys/arch/riscv64/stand/efiboot/libsa.h
new file mode 100644 (file)
index 0000000..aed844e
--- /dev/null
@@ -0,0 +1,32 @@
+/*     $OpenBSD: libsa.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */
+
+/*
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * 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 <lib/libsa/stand.h>
+
+#define DEFAULT_KERNEL_ADDRESS  0
+
+#ifdef DEBUG
+#define DPRINTF(x)      printf x;
+#else
+#define DPRINTF(x)
+#endif
+
+void machdep(void);
+void devboot(dev_t, char *);
+
+#define MACHINE_CMD    cmd_machine
diff --git a/sys/arch/riscv64/stand/efiboot/self_reloc.c b/sys/arch/riscv64/stand/efiboot/self_reloc.c
new file mode 100644 (file)
index 0000000..21d1e52
--- /dev/null
@@ -0,0 +1,127 @@
+/* $OpenBSD: self_reloc.c,v 1.1 2021/04/28 19:01:00 drahn Exp $ */
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <machine/reloc.h>
+
+#if defined(__aarch64__) || defined(__amd64__) || defined(__riscv)
+#define        ELFSIZE         64
+#define        ElfW_Rel        Elf64_Rela
+#define        ElfW_Dyn        Elf64_Dyn
+#define        ELFW_R_TYPE     ELF64_R_TYPE
+#define        ELF_RELA
+#elif defined(__arm__) || defined(__i386__)
+#define        ELFSIZE         32
+#define        ElfW_Rel        Elf32_Rel
+#define        ElfW_Dyn        Elf32_Dyn
+#define        ELFW_R_TYPE     ELF32_R_TYPE
+#else
+#error architecture not supported
+#endif
+
+#include <sys/exec_elf.h>
+
+#if defined(__aarch64__)
+#define        RELOC_TYPE_NONE         R_AARCH64_NONE
+#define        RELOC_TYPE_RELATIVE     R_AARCH64_RELATIVE
+#elif defined(__amd64__)
+#define        RELOC_TYPE_NONE         R_X86_64_NONE
+#define        RELOC_TYPE_RELATIVE     R_X86_64_RELATIVE
+#elif defined(__arm__)
+#define        RELOC_TYPE_NONE         R_ARM_NONE
+#define        RELOC_TYPE_RELATIVE     R_ARM_RELATIVE
+#elif defined(__i386__)
+#define        RELOC_TYPE_NONE         R_386_NONE
+#define        RELOC_TYPE_RELATIVE     R_386_RELATIVE
+#elif defined(__riscv)
+#define RELOC_TYPE_NONE                R_RISCV_NONE
+#define RELOC_TYPE_RELATIVE    R_RISCV_RELATIVE
+#endif
+
+/*
+ * A simple elf relocator.
+ */
+void
+self_reloc(Elf_Addr baseaddr, ElfW_Dyn *dynamic)
+{
+       Elf_Word relsz, relent;
+       Elf_Addr *newaddr;
+       ElfW_Rel *rel = NULL;
+       ElfW_Dyn *dynp;
+
+       /*
+        * Find the relocation address, its size and the relocation entry.
+        */
+       relsz = 0;
+       relent = 0;
+       for (dynp = dynamic; dynp->d_tag != DT_NULL; dynp++) {
+               switch (dynp->d_tag) {
+               case DT_REL:
+               case DT_RELA:
+                       rel = (ElfW_Rel *)(dynp->d_un.d_ptr + baseaddr);
+                       break;
+               case DT_RELSZ:
+               case DT_RELASZ:
+                       relsz = dynp->d_un.d_val;
+                       break;
+               case DT_RELENT:
+               case DT_RELAENT:
+                       relent = dynp->d_un.d_val;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /*
+        * Perform the actual relocation. We rely on the object having been
+        * linked at 0, so that the difference between the load and link
+        * address is the same as the load address.
+        */
+       for (; relsz > 0; relsz -= relent) {
+               switch (ELFW_R_TYPE(rel->r_info)) {
+               case RELOC_TYPE_NONE:
+                       /* No relocation needs be performed. */
+                       break;
+
+               case RELOC_TYPE_RELATIVE:
+                       newaddr = (Elf_Addr *)(rel->r_offset + baseaddr);
+#ifdef ELF_RELA
+                       /* Addend relative to the base address. */
+                       *newaddr = baseaddr + rel->r_addend;
+#else
+                       /* Address relative to the base address. */
+                       *newaddr += baseaddr;
+#endif
+                       break;
+               default:
+                       /* XXX: do we need other relocations ? */
+                       break;
+               }
+               rel = (ElfW_Rel *) ((caddr_t) rel + relent);
+       }
+}
diff --git a/sys/arch/riscv64/stand/efiboot/softraid_riscv64.c b/sys/arch/riscv64/stand/efiboot/softraid_riscv64.c
new file mode 100644 (file)
index 0000000..0a521cc
--- /dev/null
@@ -0,0 +1,665 @@
+/*     $OpenBSD: softraid_riscv64.c,v 1.1 2021/04/28 19:01:00 drahn Exp $      */
+
+/*
+ * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
+ *
+ * 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_riscv64.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, daddr_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, daddr_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 a/sys/arch/riscv64/stand/efiboot/softraid_riscv64.h b/sys/arch/riscv64/stand/efiboot/softraid_riscv64.h
new file mode 100644 (file)
index 0000000..db0c522
--- /dev/null
@@ -0,0 +1,33 @@
+/*     $OpenBSD: softraid_riscv64.h,v 1.1 2021/04/28 19:01:00 drahn Exp $      */
+
+/*
+ * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
+ *
+ * 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, daddr_t, size_t,
+           void *, size_t *);
+int    sropen(struct open_file *, ...);
+int    srstrategy(void *, int, daddr_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 */
diff --git a/sys/arch/riscv64/stand/efiboot/start.S b/sys/arch/riscv64/stand/efiboot/start.S
new file mode 100644 (file)
index 0000000..6811bf9
--- /dev/null
@@ -0,0 +1,170 @@
+/* $OpenBSD: start.S,v 1.1 2021/04/28 19:01:00 drahn Exp $ */
+/*-
+ * Copyright (c) 2014 Andrew Turner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/sys/boot/efi/loader/arch/arm64/start.S 282727 2015-05-10 13:24:26Z ian $
+ */
+
+/*
+ * We need to be a PE32+ file for EFI. On some architectures we can use
+ * objcopy to create the correct file, however on arm64 we need to do
+ * it ourselves.
+ */
+
+#define        IMAGE_FILE_MACHINE_RISCV64      0x5064
+
+#define        IMAGE_SCN_CNT_CODE              0x00000020
+#define        IMAGE_SCN_CNT_INITIALIZED_DATA  0x00000040
+#define        IMAGE_SCN_MEM_DISCARDABLE       0x02000000
+#define        IMAGE_SCN_MEM_EXECUTE           0x20000000
+#define        IMAGE_SCN_MEM_READ              0x40000000
+
+       .section .peheader, "a"
+efi_start:
+       /* The MS-DOS Stub, only used to get the offset of the COFF header */
+       .ascii  "MZ"
+       .short  0
+       .space  0x38
+       .long   pe_sig - efi_start
+
+       /* The PE32 Signature. Needs to be 8-byte aligned */
+       .align  3
+pe_sig:
+       .ascii  "PE"
+       .short  0
+coff_head:
+       .short  IMAGE_FILE_MACHINE_RISCV64      /* RISCV64 file */
+       .short  2                               /* 2 Sections */
+       .long   0                               /* Timestamp */
+       .long   0                               /* No symbol table */
+       .long   0                               /* No symbols */
+       .short  section_table - optional_header /* Optional header size */
+       .short  0       /* Characteristics TODO: Fill in */
+
+optional_header:
+       .short  0x020b                          /* PE32+ (64-bit addressing) */
+       .byte   0                               /* Major linker version */
+       .byte   0                               /* Minor linker version */
+       .long   _edata - _end_header            /* Code size */
+       .long   0                               /* No initialized data */
+       .long   0                               /* No uninitialized data */
+       .long   _start - efi_start              /* Entry point */
+       .long   _end_header - efi_start         /* Start of code */
+
+optional_windows_header:
+       .quad   0                               /* Image base */
+       .long   32                              /* Section Alignment */
+       .long   8                               /* File alignment */
+       .short  0                               /* Major OS version */
+       .short  0                               /* Minor OS version */
+       .short  0                               /* Major image version */
+       .short  0                               /* Minor image version */
+       .short  0                               /* Major subsystem version */
+       .short  0                               /* Minor subsystem version */
+       .long   0                               /* Win32 version */
+       .long   _edata - efi_start              /* Image size */
+       .long   _end_header - efi_start         /* Header size */
+       .long   0                               /* Checksum */
+       .short  0xa                             /* Subsystem (EFI app) */
+       .short  0                               /* DLL Characteristics */
+       .quad   0                               /* Stack reserve */
+       .quad   0                               /* Stack commit */
+       .quad   0                               /* Heap reserve */
+       .quad   0                               /* Heap commit */
+       .long   0                               /* Loader flags */
+       .long   6                               /* Number of RVAs */
+
+       /* RVAs: */
+       .quad   0
+       .quad   0
+       .quad   0
+       .quad   0
+       .quad   0
+       .quad   0
+
+section_table:
+       /* We need a .reloc section for EFI */
+       .ascii  ".reloc"
+       .byte   0
+       .byte   0                               /* Pad to 8 bytes */
+       .long   0                               /* Virtual size */
+       .long   0                               /* Virtual address */
+       .long   0                               /* Size of raw data */
+       .long   0                               /* Pointer to raw data */
+       .long   0                               /* Pointer to relocations */
+       .long   0                               /* Pointer to line numbers */
+       .short  0                               /* Number of relocations */
+       .short  0                               /* Number of line numbers */
+       .long   (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | \
+                IMAGE_SCN_MEM_DISCARDABLE)     /* Characteristics */
+
+       /* The contents of the loader */
+       .ascii  ".text"
+       .byte   0
+       .byte   0
+       .byte   0                               /* Pad to 8 bytes */
+       .long   _edata - _end_header            /* Virtual size */
+       .long   _end_header - efi_start         /* Virtual address */
+       .long   _edata - _end_header            /* Size of raw data */
+       .long   _end_header - efi_start         /* Pointer to raw data */
+       .long   0                               /* Pointer to relocations */
+       .long   0                               /* Pointer to line numbers */
+       .short  0                               /* Number of relocations */
+       .short  0                               /* Number of line numbers */
+       .long   (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | \
+                IMAGE_SCN_MEM_READ)            /* Characteristics */
+_end_header:
+
+       .text
+       .globl  _start
+_start:
+       /* Save the boot params to the stack */
+       addi    sp, sp, -16
+       sd      a0, 0(sp)
+       sd      a1, 8(sp)
+
+       la      a0, __bss_start
+       la      a1, __bss_end
+
+       j 2f
+
+1:
+       sd      zero, 0(a0)
+       addi    a0, a0, 8
+2:
+       bltu    a0, a1, 1b
+
+       la      a0, ImageBase
+       la      a1, _DYNAMIC
+
+       call    self_reloc
+
+       ld      a0, 0(sp)
+       ld      a1, 8(sp)
+       addi    sp, sp, 16
+
+       call    efi_main
+
+1:     j       1b