From a7214d25c2bb1af3d9c9521c7f07602b193bede4 Mon Sep 17 00:00:00 2001 From: drahn Date: Wed, 28 Apr 2021 19:01:00 +0000 Subject: [PATCH] riscv64 efiboot bootloader Ported from arm64 with changes for riscv64. Initial effort by Mickael Torres, with additional updates ACPI api removed per request ok kettenis@ --- sys/arch/riscv64/stand/Makefile | 5 + sys/arch/riscv64/stand/efiboot/Makefile | 85 ++ sys/arch/riscv64/stand/efiboot/conf.c | 81 ++ sys/arch/riscv64/stand/efiboot/disk.h | 33 + sys/arch/riscv64/stand/efiboot/efiboot.c | 1010 +++++++++++++++++ sys/arch/riscv64/stand/efiboot/efiboot.h | 30 + sys/arch/riscv64/stand/efiboot/eficall.h | 55 + sys/arch/riscv64/stand/efiboot/efidev.c | 558 +++++++++ sys/arch/riscv64/stand/efiboot/efidev.h | 36 + sys/arch/riscv64/stand/efiboot/efipxe.c | 524 +++++++++ sys/arch/riscv64/stand/efiboot/efipxe.h | 33 + sys/arch/riscv64/stand/efiboot/efirng.c | 90 ++ sys/arch/riscv64/stand/efiboot/exec.c | 89 ++ sys/arch/riscv64/stand/efiboot/fdt.c | 641 +++++++++++ sys/arch/riscv64/stand/efiboot/fdt.h | 71 ++ sys/arch/riscv64/stand/efiboot/heap.h | 29 + .../riscv64/stand/efiboot/ldscript.riscv64 | 85 ++ sys/arch/riscv64/stand/efiboot/libsa.h | 32 + sys/arch/riscv64/stand/efiboot/self_reloc.c | 127 +++ .../riscv64/stand/efiboot/softraid_riscv64.c | 665 +++++++++++ .../riscv64/stand/efiboot/softraid_riscv64.h | 33 + sys/arch/riscv64/stand/efiboot/start.S | 170 +++ 22 files changed, 4482 insertions(+) create mode 100644 sys/arch/riscv64/stand/Makefile create mode 100644 sys/arch/riscv64/stand/efiboot/Makefile create mode 100644 sys/arch/riscv64/stand/efiboot/conf.c create mode 100644 sys/arch/riscv64/stand/efiboot/disk.h create mode 100644 sys/arch/riscv64/stand/efiboot/efiboot.c create mode 100644 sys/arch/riscv64/stand/efiboot/efiboot.h create mode 100644 sys/arch/riscv64/stand/efiboot/eficall.h create mode 100644 sys/arch/riscv64/stand/efiboot/efidev.c create mode 100644 sys/arch/riscv64/stand/efiboot/efidev.h create mode 100644 sys/arch/riscv64/stand/efiboot/efipxe.c create mode 100644 sys/arch/riscv64/stand/efiboot/efipxe.h create mode 100644 sys/arch/riscv64/stand/efiboot/efirng.c create mode 100644 sys/arch/riscv64/stand/efiboot/exec.c create mode 100644 sys/arch/riscv64/stand/efiboot/fdt.c create mode 100644 sys/arch/riscv64/stand/efiboot/fdt.h create mode 100644 sys/arch/riscv64/stand/efiboot/heap.h create mode 100644 sys/arch/riscv64/stand/efiboot/ldscript.riscv64 create mode 100644 sys/arch/riscv64/stand/efiboot/libsa.h create mode 100644 sys/arch/riscv64/stand/efiboot/self_reloc.c create mode 100644 sys/arch/riscv64/stand/efiboot/softraid_riscv64.c create mode 100644 sys/arch/riscv64/stand/efiboot/softraid_riscv64.h create mode 100644 sys/arch/riscv64/stand/efiboot/start.S diff --git a/sys/arch/riscv64/stand/Makefile b/sys/arch/riscv64/stand/Makefile new file mode 100644 index 00000000000..7fe46900923 --- /dev/null +++ b/sys/arch/riscv64/stand/Makefile @@ -0,0 +1,5 @@ +# $OpenBSD: Makefile,v 1.1 2021/04/28 19:01:00 drahn Exp $ + +SUBDIR= efiboot + +.include diff --git a/sys/arch/riscv64/stand/efiboot/Makefile b/sys/arch/riscv64/stand/efiboot/Makefile new file mode 100644 index 00000000000..5f5eee08dfb --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/Makefile @@ -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 + +${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 +.endif diff --git a/sys/arch/riscv64/stand/efiboot/conf.c b/sys/arch/riscv64/stand/efiboot/conf.c new file mode 100644 index 00000000000..4fd71531e9a --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/conf.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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 index 00000000000..1492c4f5e31 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/disk.h @@ -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 + +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 index 00000000000..6564837fca7 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/efiboot.c @@ -0,0 +1,1010 @@ +/* $OpenBSD: efiboot.c,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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 index 00000000000..18fea5026e5 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/efiboot.h @@ -0,0 +1,30 @@ +/* $OpenBSD: efiboot.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko + * + * 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 index 00000000000..7c33b66d1c5 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/eficall.h @@ -0,0 +1,55 @@ +/* $OpenBSD: eficall.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko + * + * 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 index 00000000000..fd8855b4171 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/efidev.c @@ -0,0 +1,558 @@ +/* $OpenBSD: efidev.c,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko + * 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 +#include +#include +#include +#include + +#include "libsa.h" + +#include +#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 index 00000000000..b7e5aa86430 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/efidev.h @@ -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 + * 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 index 00000000000..7fe706e8167 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/efipxe.c @@ -0,0 +1,524 @@ +/* $OpenBSD: efipxe.c,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ +/* + * Copyright (c) 2017 Patrick Wildt + * + * 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 +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#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 index 00000000000..3a0529a0cc5 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/efipxe.h @@ -0,0 +1,33 @@ +/* $OpenBSD: efipxe.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ +/* + * Copyright (c) 2017 Patrick Wildt + * + * 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 index 00000000000..1662e44cd39 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/efirng.c @@ -0,0 +1,90 @@ +/* $OpenBSD: efirng.c,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2018 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 + +#include +#include + +#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 index 00000000000..0a71ccecccb --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/exec.c @@ -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 +#include +#include + +#include +#include +#include + +#include +#include + +#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 index 00000000000..0985fa3bea6 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/fdt.c @@ -0,0 +1,641 @@ +/* $OpenBSD: fdt.c,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2009 Dariusz Swiderski + * Copyright (c) 2009, 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 +#include + +#include + +#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 index 00000000000..283a8dccbd9 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/fdt.h @@ -0,0 +1,71 @@ +/* $OpenBSD: fdt.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2009 Dariusz Swiderski + * + * 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 index 00000000000..960c7e7d129 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/heap.h @@ -0,0 +1,29 @@ +/* $OpenBSD: heap.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko + * + * 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 + +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 index 00000000000..7075787c2cd --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/ldscript.riscv64 @@ -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 index 00000000000..aed844e501a --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/libsa.h @@ -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 + +#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 index 00000000000..21d1e5208b7 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/self_reloc.c @@ -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 + * 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 +#include + +#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 + +#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 index 00000000000..0a521cc6427 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/softraid_riscv64.c @@ -0,0 +1,665 @@ +/* $OpenBSD: softraid_riscv64.c,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2012 Joel Sing + * + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#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 index 00000000000..db0c5227d99 --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/softraid_riscv64.h @@ -0,0 +1,33 @@ +/* $OpenBSD: softraid_riscv64.h,v 1.1 2021/04/28 19:01:00 drahn Exp $ */ + +/* + * Copyright (c) 2012 Joel Sing + * + * 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 index 00000000000..6811bf94a7f --- /dev/null +++ b/sys/arch/riscv64/stand/efiboot/start.S @@ -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 -- 2.20.1