diff options
author | Mike Larkin <mlarkin@cvs.openbsd.org> | 2019-05-11 02:33:35 +0000 |
---|---|---|
committer | Mike Larkin <mlarkin@cvs.openbsd.org> | 2019-05-11 02:33:35 +0000 |
commit | 65b504b73b92f2381ae03528d65eb0d0aa5a2c30 (patch) | |
tree | 89202c27f5062e44b78bab9a14dd1d816de0891b /sys/arch | |
parent | 6070324fbf2dcaf994ee99e84a5f6bfbd4860086 (diff) |
Refactor efiboot into 32 and 64 bit copies.
Make 2 separate efiboots, one for 32 bit and one for 64 bit to allow
us to remove lots of #ifdef code. Needed to ease the development effort
for random-VA linked kernels
ok tedu, deraadt
Diffstat (limited to 'sys/arch')
28 files changed, 4382 insertions, 0 deletions
diff --git a/sys/arch/amd64/stand/efi32/Makefile b/sys/arch/amd64/stand/efi32/Makefile new file mode 100644 index 00000000000..d1e9f399611 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/Makefile @@ -0,0 +1,9 @@ +# $OpenBSD: Makefile,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ + +.if ${MACHINE} == "amd64" + +SUBDIR= bootx64 bootia32 + +.endif + +.include <bsd.subdir.mk> diff --git a/sys/arch/amd64/stand/efi32/Makefile.common b/sys/arch/amd64/stand/efi32/Makefile.common new file mode 100644 index 00000000000..010b5252e49 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/Makefile.common @@ -0,0 +1,78 @@ +# $OpenBSD: Makefile.common,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ + +S= ${.CURDIR}/../../../../.. +SADIR= ${.CURDIR}/../.. +EFIDIR= ${S}/stand/efi + +OBJCOPY?= objcopy +OBJDUMP?= objdump + +EFI_HEAP_LIMIT= 0xc00000 + +LDFLAGS+= -nostdlib -T${.CURDIR}/../${LDSCRIPT} -Bsymbolic -shared + +COPTS+= -DEFIBOOT -DFWRANDOM -DNEEDS_HEAP_H -I${.CURDIR}/.. +COPTS+= -I${EFIDIR}/include -I${S}/stand/boot +COPTS+= -ffreestanding -std=gnu99 +COPTS+= -fshort-wchar -fPIC -mno-red-zone +.if ${SOFTRAID:L} == "yes" +COPTS+= -DSOFTRAID +.endif +COPTS+= -D_STANDALONE -nostdinc -fno-builtin + +AFLAGS+= -pipe -fPIC + +.PATH: ${.CURDIR}/.. +SRCS+= self_reloc.c +SRCS+= efiboot.c efidev.c efipxe.c efirng.c +SRCS+= conf.c dev_i386.c cmd_i386.c diskprobe.c exec_i386.c machdep.c +SRCS+= memprobe.c + +.PATH: ${S}/stand/boot +SRCS+= boot.c bootarg.c cmd.c vars.c + +.PATH: ${S}/lib/libsa +SRCS+= alloc.c ctime.c exit.c getchar.c memcmp.c memcpy.c memmove.c memset.c printf.c \ + putchar.c snprintf.c strcmp.c strerror.c strlen.c strncmp.c strncpy.c \ + strtol.c strtoll.c +SRCS+= close.c closeall.c cons.c cread.c dev.c disklabel.c dkcksum.c fstat.c \ + lseek.c open.c read.c readdir.c stat.c +SRCS+= ufs.c cd9660.c +.if ${SOFTRAID:L} == "yes" +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 +.endif + +.PATH: ${S}/lib/libz +SRCS+= adler32.c crc32.c inflate.c inftrees.c + +.PATH: ${S}/lib/libkern +SRCS+= divdi3.c moddi3.c qdivrem.c +SRCS+= strlcpy.c + +.PATH: ${SADIR}/libsa +SRCS+= loadfile.c elf64.c elf32.c + +.if ${SOFTRAID:L} == "yes" +SRCS+= softraid_amd64.c +.endif + +PROG.so= ${PROG:S/.EFI/.so/} +CLEANFILES+= ${PROG.so} ${PROG.so}.tmp + +${PROG}: ${PROG.so} + ${OBJCOPY} -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \ + -j .rel.dyn -j .rela -j .rela.dyn -j .reloc \ + --target=${OBJFMT} ${PROG.so} ${.TARGET} + +.include <bsd.prog.mk> +CFLAGS+= -Wno-pointer-sign +CPPFLAGS+= -DSMALL -DSLOW -DNOBYFOUR -D__INTERNAL_LIBSA_CREAD +CPPFLAGS+= -DHEAP_LIMIT=${EFI_HEAP_LIMIT} -DHIBERNATE + +${PROG.so}: ${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} diff --git a/sys/arch/amd64/stand/efi32/Makefile.inc b/sys/arch/amd64/stand/efi32/Makefile.inc new file mode 100644 index 00000000000..8b33a7be00c --- /dev/null +++ b/sys/arch/amd64/stand/efi32/Makefile.inc @@ -0,0 +1,3 @@ +# $OpenBSD: Makefile.inc,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ + +.include "${.CURDIR}/../../Makefile.inc" diff --git a/sys/arch/amd64/stand/efi32/bootia32/Makefile b/sys/arch/amd64/stand/efi32/bootia32/Makefile new file mode 100644 index 00000000000..4186e53b1fc --- /dev/null +++ b/sys/arch/amd64/stand/efi32/bootia32/Makefile @@ -0,0 +1,18 @@ +# $OpenBSD: Makefile,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ + +.include <bsd.own.mk> + +PROG= BOOTIA32.EFI +NOMAN= # +OBJFMT= efi-app-ia32 +SOFTRAID= yes +SRCS+= start_i386.S random_i386.S +LDSCRIPT= ldscript.i386 + +COPTS= -I${EFIDIR}/include/i386 + +.include "${.CURDIR}/../Makefile.common" + +CFLAGS+= -m32 +AFLAGS+= -m32 +LDFLAGS+= -N diff --git a/sys/arch/amd64/stand/efi32/cmd_i386.c b/sys/arch/amd64/stand/efi32/cmd_i386.c new file mode 100644 index 00000000000..460c618273c --- /dev/null +++ b/sys/arch/amd64/stand/efi32/cmd_i386.c @@ -0,0 +1,162 @@ +/* $OpenBSD: cmd_i386.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1997-1999 Michael Shalayeff + * Copyright (c) 1997 Tobias Weingartner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/param.h> +#include <sys/reboot.h> +#include <machine/biosvar.h> +#include <sys/disklabel.h> +#include "disk.h" +#include "biosdev.h" +#include "libsa.h" +#include <cmd.h> + +#include "efiboot.h" +#include "efidev.h" + +extern const char version[]; + +int Xboot(void); +int Xcomaddr(void); +int Xdiskinfo(void); +int Xmemory(void); +int Xregs(void); + +/* From gidt.S */ +int bootbuf(void *, int); + +const struct cmd_table cmd_machine[] = { + { "comaddr", CMDT_CMD, Xcomaddr }, + { "diskinfo", CMDT_CMD, Xdiskinfo }, + { "memory", CMDT_CMD, Xmemory }, + { "video", CMDT_CMD, Xvideo_efi }, + { "gop", CMDT_CMD, Xgop_efi }, + { "exit", CMDT_CMD, Xexit_efi }, + { "poweroff", CMDT_CMD, Xpoweroff_efi }, +#ifdef DEBUG + { "regs", CMDT_CMD, Xregs }, +#endif + { NULL, 0 } +}; + +int +Xdiskinfo(void) +{ + efi_dump_diskinfo(); + return 0; +} + +#ifdef DEBUG +int +Xregs(void) +{ + DUMP_REGS; + return 0; +} +#endif + +int +Xmemory(void) +{ + if (cmd.argc >= 2) { + int i; + /* parse the memory specs */ + + for (i = 1; i < cmd.argc; i++) { + char *p; + long long addr, size; + + p = cmd.argv[i]; + + size = strtoll(p + 1, &p, 0); + /* Size the size */ + switch (*p) { + case 'G': + case 'g': + size *= 1024; + case 'M': + case 'm': + size *= 1024; + case 'K': + case 'k': + size *= 1024; + p++; + } + + /* Handle (possibly non-existent) address part */ + switch (*p) { + case '@': + addr = strtoll(p + 1, NULL, 0); + break; + + /* Adjust address if we don't need it */ + default: + if (cmd.argv[i][0] == '=') + addr = -1; + else + addr = 0; + } + + if (addr == 0 || size == 0) { + printf("bad language\n"); + return 0; + } else { + switch (cmd.argv[i][0]) { + case '-': + mem_delete(addr, addr + size); + break; + case '+': + mem_add(addr, addr + size); + break; + case '=': + mem_limit(size); + break; + default : + printf("bad OP\n"); + return 0; + } + } + } + } + + dump_biosmem(NULL); + + return 0; +} + +int +Xcomaddr(void) +{ + extern int com_addr; + + if (cmd.argc >= 2) + com_addr = (int)strtol(cmd.argv[1], NULL, 0); + + return 0; +} diff --git a/sys/arch/amd64/stand/efi32/conf.c b/sys/arch/amd64/stand/efi32/conf.c new file mode 100644 index 00000000000..6791259ea38 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/conf.c @@ -0,0 +1,92 @@ +/* $OpenBSD: conf.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1996 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/disklabel.h> +#include <libsa.h> +#include <lib/libsa/ufs.h> +#include <lib/libsa/tftp.h> +#include <lib/libsa/cd9660.h> +#include <dev/cons.h> + +#include "disk.h" +#include "efiboot.h" +#include "efidev.h" +#include "efipxe.h" + +const char version[] = "3.44"; + +#ifdef EFI_DEBUG +int debug = 0; +#endif + +void (*sa_cleanup)(void) = NULL; + + +void (*i386_probe1[])(void) = { + cninit, efi_memprobe +}; +void (*i386_probe2[])(void) = { + efi_pxeprobe, efi_diskprobe, diskprobe +}; + +struct i386_boot_probes probe_list[] = { + { "probing", i386_probe1, nitems(i386_probe1) }, + { "disk", i386_probe2, nitems(i386_probe2) } +}; +int nibprobes = nitems(probe_list); + + +struct fs_ops file_system[] = { + { tftp_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 }, + { cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek, + cd9660_stat, cd9660_readdir }, +#ifdef notdef + { fat_open, fat_close, fat_read, fat_write, fat_seek, + fat_stat, fat_readdir }, + { nfs_open, nfs_close, nfs_read, nfs_write, nfs_seek, + nfs_stat, nfs_readdir }, +#endif +}; +int nfsys = nitems(file_system); + +struct devsw devsw[] = { + { "TFTP", tftpstrategy, tftpopen, tftpclose, tftpioctl }, + { "EFI", efistrategy, efiopen, eficlose, efiioctl }, +}; +int ndevs = nitems(devsw); + +struct consdev constab[] = { + { efi_cons_probe, efi_cons_init, efi_cons_getc, efi_cons_putc }, + { efi_com_probe, efi_com_init, efi_com_getc, efi_com_putc }, + { NULL } +}; +struct consdev *cn_tab = constab; diff --git a/sys/arch/amd64/stand/efi32/dev_i386.c b/sys/arch/amd64/stand/efi32/dev_i386.c new file mode 100644 index 00000000000..33ce6bf67d8 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/dev_i386.c @@ -0,0 +1,207 @@ +/* $OpenBSD: dev_i386.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1996-1999 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 AUTHOR OR HIS RELATIVES 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 MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/disklabel.h> +#include <dev/cons.h> + +#include "libsa.h" +#include "biosdev.h" +#include "disk.h" + +#ifdef SOFTRAID +#include <dev/biovar.h> +#include <dev/softraidvar.h> +#include <lib/libsa/softraid.h> +#include "softraid_amd64.h" +#endif + +extern int debug; + +/* XXX use slot for 'rd' for 'hd' pseudo-device */ +const char bdevs[][4] = { + "wd", "", "fd", "", "sd", "st", "cd", "", + "", "", "", "", "", "", "", "", "", "hd", "" +}; +const int nbdevs = nitems(bdevs); + +const char cdevs[][4] = { + "cn", "", "", "", "", "", "", "", + "com", "", "", "", "pc" +}; +const int ncdevs = nitems(cdevs); + +/* pass dev_t to the open routines */ +int +devopen(struct open_file *f, const char *fname, char **file) +{ + struct devsw *dp = devsw; + register int i, rc = 1; + + *file = (char *)fname; + +#ifdef DEBUG + if (debug) + printf("devopen:"); +#endif + + for (i = 0; i < ndevs && rc != 0; dp++, i++) { +#ifdef DEBUG + if (debug) + printf(" %s: ", dp->dv_name); +#endif + if ((rc = (*dp->dv_open)(f, file)) == 0) { + 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. + */ + extern char *bootmac; + bootmac = NULL; + } + return 0; + } +#ifdef DEBUG + else if (debug) + printf("%d", rc); +#endif + + } +#ifdef DEBUG + if (debug) + putchar('\n'); +#endif + + if ((f->f_flags & F_NODEV) == 0) + f->f_dev = dp; + + return rc; +} + +void +devboot(dev_t bootdev, char *p) +{ +#ifdef SOFTRAID + struct sr_boot_volume *bv; + struct sr_boot_chunk *bc; + struct diskinfo *dip = NULL; +#endif + int sr_boot_vol = -1; + int part_type = FS_UNUSED; + + if (!bootdev) { + *p++ = 't'; + *p++ = 'f'; + *p++ = 't'; + *p++ = 'p'; + *p = '\0'; + return; + } + +#ifdef SOFTRAID + /* + * Determine the partition type for the 'a' partition of the + * boot device. + */ + TAILQ_FOREACH(dip, &disklist, list) + if (dip->bios_info.bios_number == bootdev && + (dip->bios_info.flags & BDI_BADLABEL) == 0) + part_type = 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) { + if (bv->sbv_flags & BIOC_SCBOOTABLE) + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) + if (bc->sbc_disk == bootdev) + sr_boot_vol = bv->sbv_unit; + if (sr_boot_vol != -1) + break; + } +#endif + + if (sr_boot_vol != -1 && part_type != FS_BSDFFS) { + *p++ = 's'; + *p++ = 'r'; + *p++ = '0' + sr_boot_vol; + } else if (bootdev & 0x100) { + *p++ = 'c'; + *p++ = 'd'; + *p++ = '0'; + } else { + if (bootdev & 0x80) + *p++ = 'h'; + else + *p++ = 'f'; + *p++ = 'd'; + *p++ = '0' + (bootdev & 0x7f); + } + *p++ = 'a'; + *p = '\0'; +} + +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; +} + +int +cnspeed(dev_t dev, int sp) +{ + if (major(dev) == 8) /* comN */ + return comspeed(dev, sp); + + /* pc0 and anything else */ + return 9600; +} diff --git a/sys/arch/amd64/stand/efi32/diskprobe.c b/sys/arch/amd64/stand/efi32/diskprobe.c new file mode 100644 index 00000000000..7f5c42cd9a1 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/diskprobe.c @@ -0,0 +1,301 @@ +/* $OpenBSD: diskprobe.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1997 Tobias Weingartner + * 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. + * + */ + +/* We want the disk type names from disklabel.h */ +#undef DKTYPENAMES + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/reboot.h> +#include <sys/disklabel.h> +#include <sys/hibernate.h> + +#include <lib/libz/zlib.h> +#include <machine/biosvar.h> +#include <stand/boot/bootarg.h> + +#include "disk.h" +#include "biosdev.h" +#include "libsa.h" + +#ifdef SOFTRAID +#include "softraid_amd64.h" +#endif +#include "efidev.h" + +#define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */ + +/* Local Prototypes */ +static int disksum(int); + +int bootdev_has_hibernate(void); /* export for loadfile() */ + +/* List of disk devices we found/probed */ +struct disklist_lh disklist; + +/* Pointer to boot device */ +struct diskinfo *bootdev_dip; + +extern int debug; +extern int bios_bootdev; +extern int bios_cddev; + +static void +efi_hardprobe(void) +{ + int n; + struct diskinfo *dip, *dipt; + u_int bsdunit, type = 0; + u_int scsi= 0, ide = 0, atapi = 0; + extern struct disklist_lh + efi_disklist; + + n = 0; + TAILQ_FOREACH_SAFE(dip, &efi_disklist, list, dipt) { + TAILQ_REMOVE(&efi_disklist, dip, list); + n = scsi + ide; + + /* Try to find the label, to figure out device type */ + if ((efi_getdisklabel(dip->efi_info, &dip->disklabel))) { + type = 0; + printf(" hd%d*", n); + bsdunit = ide++; + } else { + /* Best guess */ + switch (dip->disklabel.d_type) { + case DTYPE_SCSI: + type = 4; + bsdunit = scsi++; + dip->bios_info.flags |= BDI_GOODLABEL; + break; + + case DTYPE_ESDI: + case DTYPE_ST506: + type = 0; + bsdunit = ide++; + dip->bios_info.flags |= BDI_GOODLABEL; + break; + + case DTYPE_ATAPI: + type = 6; + n = atapi; + bsdunit = atapi++; + dip->bios_info.flags |= BDI_GOODLABEL + | BDI_EL_TORITO; + break; + + default: + dip->bios_info.flags |= BDI_BADLABEL; + type = 0; /* XXX Suggest IDE */ + bsdunit = ide++; + } + printf(" %cd%d", (type == 6)? 'c' : 'h', n); + } + if (type != 6) + dip->bios_info.bios_number = 0x80 | n; + else + dip->bios_info.bios_number = 0xe0 | n; + + dip->bios_info.checksum = 0; /* just in case */ + /* Fill out best we can */ + dip->bsddev = dip->bios_info.bsd_dev = + MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); + check_hibernate(dip); + + /* Add to queue of disks */ + TAILQ_INSERT_TAIL(&disklist, dip, list); + n++; + } +} + +/* Probe for all BIOS supported disks */ +u_int32_t bios_cksumlen; +void +diskprobe(void) +{ + struct diskinfo *dip; + int i; + + /* These get passed to kernel */ + bios_diskinfo_t *bios_diskinfo; + + /* Init stuff */ + TAILQ_INIT(&disklist); + + efi_hardprobe(); + +#ifdef SOFTRAID + srprobe(); +#endif + + /* Checksumming of hard disks */ + for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) + ; + bios_cksumlen = i; + + /* Get space for passing bios_diskinfo stuff to kernel */ + for (i = 0, dip = TAILQ_FIRST(&disklist); dip; + dip = TAILQ_NEXT(dip, list)) + i++; + bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t)); + + /* Copy out the bios_diskinfo stuff */ + for (i = 0, dip = TAILQ_FIRST(&disklist); dip; + dip = TAILQ_NEXT(dip, list)) + bios_diskinfo[i++] = dip->bios_info; + + bios_diskinfo[i++].bios_number = -1; + /* Register for kernel use */ + addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen); + addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), + bios_diskinfo); +} + +/* Find info on given BIOS disk */ +struct diskinfo * +dklookup(int dev) +{ + struct diskinfo *dip; + + for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) + if (dip->bios_info.bios_number == dev) + return dip; + + return NULL; +} + +void +dump_diskinfo(void) +{ + struct diskinfo *dip; + + printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n"); + for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { + bios_diskinfo_t *bdi = &dip->bios_info; + int d = bdi->bios_number; + int u = d & 0x7f; + char c; + + if (bdi->flags & BDI_EL_TORITO) { + c = 'c'; + u = 0; + } else { + c = (d & 0x80) ? 'h' : 'f'; + } + + printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n", + c, u, d, + (bdi->flags & BDI_BADLABEL)?"*none*":"label", + bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors, + bdi->flags, bdi->checksum); + } +} + +/* Find BIOS portion on given BIOS disk + * XXX - Use dklookup() instead. + */ +bios_diskinfo_t * +bios_dklookup(int dev) +{ + struct diskinfo *dip; + + dip = dklookup(dev); + if (dip) + return &dip->bios_info; + + return NULL; +} + +/* + * Checksum one more block on all harddrives + * + * Use the adler32() function from libz, + * as it is quick, small, and available. + */ +int +disksum(int blk) +{ + struct diskinfo *dip, *dip2; + int st, reprobe = 0; + char buf[DEV_BSIZE]; + + for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { + bios_diskinfo_t *bdi = &dip->bios_info; + + /* Skip this disk if it is not a HD or has had an I/O error */ + if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID) + continue; + + /* Adler32 checksum */ + st = dip->diskio(F_READ, dip, blk, 1, buf); + if (st) { + bdi->flags |= BDI_INVALID; + continue; + } + bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE); + + for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip; + dip2 = TAILQ_NEXT(dip2, list)) { + bios_diskinfo_t *bd = &dip2->bios_info; + if ((bd->bios_number & 0x80) && + !(bd->flags & BDI_INVALID) && + bdi->checksum == bd->checksum) + reprobe = 1; + } + } + + return reprobe; +} + +int +bootdev_has_hibernate(void) +{ + return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0); +} + +void +check_hibernate(struct diskinfo *dip) +{ + daddr_t sec; + int error; + union hibernate_info hib; + + /* read hibernate */ + if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP || + DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0) + return; + + sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) + + DL_GETPSIZE(&dip->disklabel.d_partitions[1]) - + (sizeof(union hibernate_info) / DEV_BSIZE); + + error = dip->strategy(dip, F_READ, (daddr32_t)sec, sizeof hib, &hib, NULL); + if (error == 0 && hib.magic == HIBERNATE_MAGIC) + dip->bios_info.flags |= BDI_HIBVALID; /* Hibernate present */ +} diff --git a/sys/arch/amd64/stand/efi32/efiboot.c b/sys/arch/amd64/stand/efi32/efiboot.c new file mode 100644 index 00000000000..72b0e45ac20 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/efiboot.c @@ -0,0 +1,1045 @@ +/* $OpenBSD: efiboot.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <dev/cons.h> +#include <dev/isa/isareg.h> +#include <dev/ic/comreg.h> +#include <sys/disklabel.h> +#include <cmd.h> +#include <stand/boot/bootarg.h> +#include <machine/pio.h> + +#include "libsa.h" +#include "disk.h" + +#include <efi.h> +#include <efiapi.h> +#include <efiprot.h> +#include <eficonsctl.h> + +#include "efidev.h" +#include "efiboot.h" +#include "eficall.h" +#include "run_i386.h" + +#define KERN_LOADSPACE_SIZE (32 * 1024 * 1024) + +EFI_SYSTEM_TABLE *ST; +EFI_BOOT_SERVICES *BS; +EFI_RUNTIME_SERVICES *RS; +EFI_HANDLE IH; +EFI_DEVICE_PATH *efi_bootdp = NULL; +EFI_PHYSICAL_ADDRESS heap; +EFI_LOADED_IMAGE *loadedImage; +UINTN heapsiz = 1 * 1024 * 1024; +UINTN mmap_key; +static EFI_GUID imgp_guid = LOADED_IMAGE_PROTOCOL; +static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; +static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; +u_long efi_loadaddr; + +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_video_init(void); +static void efi_video_reset(void); +static EFI_STATUS + efi_gop_setmode(int mode); +EFI_STATUS efi_main(EFI_HANDLE, EFI_SYSTEM_TABLE *); + +void (*run_i386)(u_long, u_long, int, int, int, int, int, int, int, int) + __attribute__((noreturn)); + +extern int bios_bootdev; + +EFI_STATUS +efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) +{ + extern char *progname; + EFI_LOADED_IMAGE *imgp; + EFI_DEVICE_PATH *dp0 = NULL, *dp; + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS stack; + + 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); + + efi_video_init(); + efi_heap_init(); + + status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid, + (void **)&imgp); + if (status == EFI_SUCCESS) + status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle, + &devp_guid, (void **)&dp0); + if (status == EFI_SUCCESS) { + for (dp = dp0; !IsDevicePathEnd(dp); + dp = NextDevicePathNode(dp)) { + if (DevicePathType(dp) == MEDIA_DEVICE_PATH && + (DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP || + DevicePathSubType(dp) == MEDIA_CDROM_DP)) { + bios_bootdev = + (DevicePathSubType(dp) == MEDIA_CDROM_DP) + ? 0x1e0 : 0x80; + efi_bootdp = dp0; + break; + } else if (DevicePathType(dp) == MESSAGING_DEVICE_PATH&& + DevicePathSubType(dp) == MSG_MAC_ADDR_DP) { + bios_bootdev = 0x0; + efi_bootdp = dp0; + break; + } + } + } + +#ifdef __amd64__ + /* allocate run_i386_start() on heap */ + if ((run_i386 = alloc(run_i386_size)) == NULL) + panic("alloc() failed"); + memcpy(run_i386, run_i386_start, run_i386_size); +#endif + + /* can't use sa_cleanup since printf is used after sa_cleanup() */ + /* sa_cleanup = efi_cleanup; */ + +#ifdef __amd64__ + progname = "BOOTX64"; +#else + progname = "BOOTIA32"; +#endif + + /* + * Move the stack before calling boot(). UEFI on some machines + * locate the stack on our kernel load address. + */ + stack = heap + heapsiz; +#if defined(__amd64__) + asm("movq %0, %%rsp;" + "mov %1, %%edi;" + "call boot;" + :: "r"(stack - 32), "r"(bios_bootdev)); +#else + asm("movl %0, %%esp;" + "movl %1, (%%esp);" + "call boot;" + :: "r"(stack - 32), "r"(bios_bootdev)); +#endif + /* must not reach here */ + return (EFI_SUCCESS); +} + +void +efi_cleanup(void) +{ + int retry; + EFI_STATUS status; + + /* retry once in case of failure */ + for (retry = 1; retry >= 0; retry--) { + efi_memprobe_internal(); /* sync the current map */ + status = EFI_CALL(BS->ExitBootServices, IH, mmap_key); + if (status == EFI_SUCCESS) + break; + if (retry == 0) + panic("ExitBootServices failed (%d)", status); + } +} + +/*********************************************************************** + * Disk + ***********************************************************************/ +struct disklist_lh efi_disklist; + +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(&efi_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)) + panic("BS->LocateHandle() returns %d", status); + + 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(&efi_disklist, di, list); + bootdev = 1; + continue; + } +next: + TAILQ_INSERT_TAIL(&efi_disklist, di, list); + } + + free(handles, sz); +} + +/* + * 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 (-1); +} + +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); +} + +/*********************************************************************** + * Memory + ***********************************************************************/ +bios_memmap_t bios_memmap[64]; + +static void +efi_heap_init(void) +{ + EFI_STATUS status; + + heap = HEAP_LIMIT; + status = EFI_CALL(BS->AllocatePages, AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(heapsiz), &heap); + if (status != EFI_SUCCESS) + panic("BS->AllocatePages()"); +} + +void +efi_memprobe(void) +{ + u_int n = 0; + bios_memmap_t *bm; + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS + addr = 0x10000000ULL; /* Below 256MB */ + + status = EFI_CALL(BS->AllocatePages, AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &addr); + if (status != EFI_SUCCESS) + panic("BS->AllocatePages()"); + efi_loadaddr = addr; + + printf(" mem["); + efi_memprobe_internal(); + for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) { + if (bm->type == BIOS_MAP_FREE && bm->size > 12 * 1024) { + if (n++ != 0) + printf(" "); + if (bm->size > 1024 * 1024) + printf("%uM", bm->size / 1024 / 1024); + else + printf("%uK", bm->size / 1024); + } + } + printf("]"); +} + +static void +efi_memprobe_internal(void) +{ + EFI_STATUS status; + UINTN mapkey, mmsiz, siz; + UINT32 mmver; + EFI_MEMORY_DESCRIPTOR *mm0, *mm; + int i, n; + bios_memmap_t *bm, bm0; + + cnvmem = extmem = 0; + bios_memmap[0].type = BIOS_MAP_END; + + 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"); + mm0 = alloc(siz); + status = EFI_CALL(BS->GetMemoryMap, &siz, mm0, &mapkey, &mmsiz, &mmver); + if (status != EFI_SUCCESS) + panic("cannot get the memory map"); + n = siz / mmsiz; + mmap_key = mapkey; + + for (i = 0, mm = mm0; i < n; i++, mm = NextMemoryDescriptor(mm, mmsiz)){ + bm0.type = BIOS_MAP_END; + bm0.addr = mm->PhysicalStart; + bm0.size = mm->NumberOfPages * EFI_PAGE_SIZE; + if (mm->Type == EfiReservedMemoryType || + mm->Type == EfiUnusableMemory || + mm->Type == EfiRuntimeServicesCode || + mm->Type == EfiRuntimeServicesData) + bm0.type = BIOS_MAP_RES; + else if (mm->Type == EfiLoaderCode || + mm->Type == EfiLoaderData || + mm->Type == EfiBootServicesCode || + mm->Type == EfiBootServicesData || + mm->Type == EfiConventionalMemory) + bm0.type = BIOS_MAP_FREE; + else if (mm->Type == EfiACPIReclaimMemory) + bm0.type = BIOS_MAP_ACPI; + else if (mm->Type == EfiACPIMemoryNVS) + bm0.type = BIOS_MAP_NVS; + else + /* + * XXX Is there anything to do for EfiMemoryMappedIO + * XXX EfiMemoryMappedIOPortSpace EfiPalCode? + */ + bm0.type = BIOS_MAP_RES; + + for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) { + if (bm->type != bm0.type) + continue; + if (bm->addr <= bm0.addr && + bm0.addr <= bm->addr + bm->size) { + bm->size = bm0.addr + bm0.size - bm->addr; + break; + } else if (bm0.addr <= bm->addr && + bm->addr <= bm0.addr + bm0.size) { + bm->size = bm->addr + bm->size - bm0.addr; + bm->addr = bm0.addr; + break; + } + } + if (bm->type == BIOS_MAP_END) { + *bm = bm0; + (++bm)->type = BIOS_MAP_END; + } + } + for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) { + if (bm->addr < IOM_BEGIN) /* Below memory hole */ + cnvmem = + max(cnvmem, (bm->addr + bm->size) / 1024); + if (bm->addr >= IOM_END /* Above the memory hole */ && + bm->addr / 1024 == extmem + 1024) + extmem += bm->size / 1024; + } + free(mm0, siz); +} + +/*********************************************************************** + * Console + ***********************************************************************/ +static SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; +static SIMPLE_INPUT_INTERFACE *conin; +static EFI_GUID con_guid + = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; +static EFI_GUID gop_guid + = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; +static EFI_GUID serio_guid + = SERIAL_IO_PROTOCOL; +struct efi_video { + int cols; + int rows; +} efi_video[32]; + +static void +efi_video_init(void) +{ + EFI_CONSOLE_CONTROL_PROTOCOL *conctrl = NULL; + int i, mode80x25, mode100x31; + UINTN cols, rows; + EFI_STATUS status; + + conout = ST->ConOut; + status = EFI_CALL(BS->LocateProtocol, &con_guid, NULL, + (void **)&conctrl); + if (status == EFI_SUCCESS) + (void)EFI_CALL(conctrl->SetMode, conctrl, + EfiConsoleControlScreenText); + mode80x25 = -1; + mode100x31 = -1; + for (i = 0; i < conout->Mode->MaxMode; i++) { + status = EFI_CALL(conout->QueryMode, conout, i, &cols, &rows); + if (EFI_ERROR(status)) + continue; + if (mode80x25 < 0 && cols == 80 && rows == 25) + mode80x25 = i; + if (mode100x31 < 0 && cols == 100 && rows == 31) + mode100x31 = i; + if (i < nitems(efi_video)) { + efi_video[i].cols = cols; + efi_video[i].rows = rows; + } + } + if (mode100x31 >= 0) + EFI_CALL(conout->SetMode, conout, mode100x31); + else if (mode80x25 >= 0) + EFI_CALL(conout->SetMode, conout, mode80x25); + conin = ST->ConIn; + efi_video_reset(); +} + +static void +efi_video_reset(void) +{ + EFI_CALL(conout->EnableCursor, conout, TRUE); + EFI_CALL(conout->SetAttribute, conout, + EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK)); + EFI_CALL(conout->ClearScreen, conout); +} + +void +efi_cons_probe(struct consdev *cn) +{ + cn->cn_pri = CN_MIDPRI; + cn->cn_dev = makedev(12, 0); + printf(" pc%d", minor(cn->cn_dev)); +} + +void +efi_cons_init(struct consdev *cp) +{ +} + +int +efi_cons_getc(dev_t dev) +{ + EFI_INPUT_KEY key; + EFI_STATUS status; + UINTN dummy; + static int lastchar = 0; + + if (lastchar) { + int r = lastchar; + if ((dev & 0x80) == 0) + lastchar = 0; + return (r); + } + + status = EFI_CALL(conin->ReadKeyStroke, conin, &key); + while (status == EFI_NOT_READY || key.UnicodeChar == 0) { + if (dev & 0x80) + return (0); + EFI_CALL(BS->WaitForEvent, 1, &conin->WaitForKey, &dummy); + status = EFI_CALL(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; + + EFI_CALL(conout->OutputString, conout, buf); +} + +int +efi_cons_getshifts(dev_t dev) +{ + /* XXX */ + return (0); +} + +int com_addr = -1; +int com_speed = -1; + +static SERIAL_IO_INTERFACE *serios[4]; +const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + +/* call with sp == 0 to query the current speed */ +int +pio_comspeed(dev_t dev, int sp) +{ + int port = (com_addr == -1) ? comports[minor(dev)] : com_addr; + int i, newsp; + int err; + + if (sp <= 0) + return com_speed; + /* valid baud rate? */ + if (115200 < sp || sp < 75) + return -1; + + /* + * Accepted speeds: + * 75 150 300 600 1200 2400 4800 9600 19200 38400 76800 and + * 14400 28800 57600 115200 + */ + for (i = sp; i != 75 && i != 14400; i >>= 1) + if (i & 1) + return -1; + +/* ripped screaming from dev/ic/com.c */ +#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */ + newsp = divrnd((COM_FREQ / 16), sp); + if (newsp <= 0) + return -1; + err = divrnd((COM_FREQ / 16) * 1000, sp * newsp) - 1000; + if (err < 0) + err = -err; + if (err > COM_TOLERANCE) + return -1; +#undef divrnd + + if (com_speed != -1 && cn_tab && cn_tab->cn_dev == dev && + com_speed != sp) { + printf("com%d: changing speed to %d baud in 5 seconds, " + "change your terminal to match!\n\a", + minor(dev), sp); + sleep(5); + } + + outb(port + com_cfcr, LCR_DLAB); + outb(port + com_dlbl, newsp); + outb(port + com_dlbh, newsp>>8); + outb(port + com_cfcr, LCR_8BITS); + if (com_speed != -1) + printf("\ncom%d: %d baud\n", minor(dev), sp); + + newsp = com_speed; + com_speed = sp; + return newsp; +} + +int +pio_com_getc(dev_t dev) +{ + int port = (com_addr == -1) ? comports[minor(dev & 0x7f)] : com_addr; + + if (dev & 0x80) + return (inb(port + com_lsr) & LSR_RXRDY); + + while ((inb(port + com_lsr) & LSR_RXRDY) == 0) + ; + + return (inb(port + com_data) & 0xff); +} + +void +pio_com_putc(dev_t dev, int c) +{ + int port = (com_addr == -1) ? comports[minor(dev)] : com_addr; + + while ((inb(port + com_lsr) & LSR_TXRDY) == 0) + ; + + outb(port + com_data, c); +} + +void +efi_com_probe(struct consdev *cn) +{ + EFI_HANDLE *handles = NULL; + SERIAL_IO_INTERFACE *serio; + EFI_STATUS status; + EFI_DEVICE_PATH *dp, *dp0; + EFI_DEV_PATH_PTR dpp; + UINTN sz; + int i, uid = -1; + + cn->cn_pri = CN_LOWPRI; + cn->cn_dev = makedev(8, 0); + + sz = 0; + status = EFI_CALL(BS->LocateHandle, ByProtocol, &serio_guid, 0, &sz, 0); + if (status == EFI_BUFFER_TOO_SMALL) { + handles = alloc(sz); + status = EFI_CALL(BS->LocateHandle, ByProtocol, &serio_guid, + 0, &sz, handles); + } + if (handles == NULL || EFI_ERROR(status)) { + free(handles, sz); + return; + } + + for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { + /* + * Identify port number of the handle. This assumes ACPI + * UID 0-3 map to legacy COM[1-4] and they use the legacy + * port address. + */ + status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid, + (void **)&dp0); + if (EFI_ERROR(status)) + continue; + uid = -1; + for (dp = dp0; !IsDevicePathEnd(dp); + dp = NextDevicePathNode(dp)) { + dpp = (EFI_DEV_PATH_PTR)dp; + if (DevicePathType(dp) == ACPI_DEVICE_PATH && + DevicePathSubType(dp) == ACPI_DP) + if (dpp.Acpi->HID == EFI_PNP_ID(0x0501)) { + uid = dpp.Acpi->UID; + break; + } + } + if (uid < 0 || nitems(serios) <= uid) + continue; + + /* Prepare SERIAL_IO_INTERFACE */ + status = EFI_CALL(BS->HandleProtocol, handles[i], &serio_guid, + (void **)&serio); + if (EFI_ERROR(status)) + continue; + serios[uid] = serio; + } + free(handles, sz); + + for (i = 0; i < nitems(serios); i++) { + if (serios[i] != NULL) + printf(" com%d", i); + } +} + +int +efi_valid_com(dev_t dev) +{ + return (minor(dev) < nitems(serios) && serios[minor(dev)] != NULL); +} + +int +comspeed(dev_t dev, int sp) +{ + EFI_STATUS status; + SERIAL_IO_INTERFACE *serio = serios[minor(dev)]; + int newsp; + + if (sp <= 0) + return com_speed; + + if (!efi_valid_com(dev)) + return pio_comspeed(dev, sp); + + if (serio->Mode->BaudRate != sp) { + status = EFI_CALL(serio->SetAttributes, serio, + sp, serio->Mode->ReceiveFifoDepth, + serio->Mode->Timeout, serio->Mode->Parity, + serio->Mode->DataBits, serio->Mode->StopBits); + if (EFI_ERROR(status)) { + printf("com%d: SetAttribute() failed with status=%d\n", + minor(dev), status); + return (-1); + } + if (com_speed != -1) + printf("\ncom%d: %d baud\n", minor(dev), sp); + } + + /* same as comspeed() in libsa/bioscons.c */ + newsp = com_speed; + com_speed = sp; + + return (newsp); +} + +void +efi_com_init(struct consdev *cn) +{ + if (!efi_valid_com(cn->cn_dev)) + /* This actually happens if the machine has another serial. */ + return; + + if (com_speed == -1) + comspeed(cn->cn_dev, 9600); /* default speed is 9600 baud */ +} + +int +efi_com_getc(dev_t dev) +{ + EFI_STATUS status; + SERIAL_IO_INTERFACE *serio; + UINTN sz; + u_char buf; + static u_char lastchar = 0; + + if (!efi_valid_com(dev & 0x7f)) + return pio_com_getc(dev); + serio = serios[minor(dev & 0x7f)]; + + if (lastchar != 0) { + int r = lastchar; + if ((dev & 0x80) == 0) + lastchar = 0; + return (r); + } + + for (;;) { + sz = 1; + status = EFI_CALL(serio->Read, serio, &sz, &buf); + if (status == EFI_SUCCESS && sz > 0) + break; + if (status != EFI_TIMEOUT && EFI_ERROR(status)) + panic("Error reading from serial status=%d", status); + if (dev & 0x80) + return (0); + } + + if (dev & 0x80) + lastchar = buf; + + return (buf); +} + +void +efi_com_putc(dev_t dev, int c) +{ + SERIAL_IO_INTERFACE *serio; + UINTN sz = 1; + u_char buf; + + if (!efi_valid_com(dev)) { + pio_com_putc(dev, c); + return; + } + serio = serios[minor(dev)]; + buf = c; + EFI_CALL(serio->Write, serio, &sz, &buf); +} + +/*********************************************************************** + * Miscellaneous + ***********************************************************************/ +/* + * ACPI GUID is confusing in UEFI spec. + * {EFI_,}_ACPI_20_TABLE_GUID or EFI_ACPI_TABLE_GUID means + * ACPI 2.0 or above. + */ +static EFI_GUID acpi_guid = ACPI_20_TABLE_GUID; +static EFI_GUID smbios_guid = SMBIOS_TABLE_GUID; +static EFI_GRAPHICS_OUTPUT *gop; +static int gopmode = -1; + +#define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID)) + +static EFI_STATUS +efi_gop_setmode(int mode) +{ + EFI_STATUS status; + + status = EFI_CALL(gop->SetMode, gop, mode); + if (EFI_ERROR(status) || gop->Mode->Mode != mode) + printf("GOP SetMode() failed (%d)\n", status); + + return (status); +} + +void +efi_makebootargs(void) +{ + int i; + EFI_STATUS status; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION + *gopi; + bios_efiinfo_t ei; + int curmode; + UINTN sz, gopsiz, bestsiz = 0; + + memset(&ei, 0, sizeof(ei)); + /* + * ACPI, BIOS configuration table + */ + for (i = 0; i < ST->NumberOfTableEntries; i++) { + if (efi_guidcmp(&acpi_guid, + &ST->ConfigurationTable[i].VendorGuid) == 0) + ei.config_acpi = (intptr_t) + ST->ConfigurationTable[i].VendorTable; + else if (efi_guidcmp(&smbios_guid, + &ST->ConfigurationTable[i].VendorGuid) == 0) + ei.config_smbios = (intptr_t) + ST->ConfigurationTable[i].VendorTable; + } + + /* + * Frame buffer + */ + status = EFI_CALL(BS->LocateProtocol, &gop_guid, NULL, + (void **)&gop); + if (!EFI_ERROR(status)) { + if (gopmode < 0) { + for (i = 0; i < gop->Mode->MaxMode; i++) { + status = EFI_CALL(gop->QueryMode, gop, + i, &sz, &gopi); + if (EFI_ERROR(status)) + continue; + gopsiz = gopi->HorizontalResolution * + gopi->VerticalResolution; + if (gopsiz > bestsiz) { + gopmode = i; + bestsiz = gopsiz; + } + } + } + if (gopmode >= 0 && gopmode != gop->Mode->Mode) { + curmode = gop->Mode->Mode; + if (efi_gop_setmode(gopmode) != EFI_SUCCESS) + (void)efi_gop_setmode(curmode); + } + + gopi = gop->Mode->Info; + switch (gopi->PixelFormat) { + case PixelBlueGreenRedReserved8BitPerColor: + ei.fb_red_mask = 0x00ff0000; + ei.fb_green_mask = 0x0000ff00; + ei.fb_blue_mask = 0x000000ff; + ei.fb_reserved_mask = 0xff000000; + break; + case PixelRedGreenBlueReserved8BitPerColor: + ei.fb_red_mask = 0x000000ff; + ei.fb_green_mask = 0x0000ff00; + ei.fb_blue_mask = 0x00ff0000; + ei.fb_reserved_mask = 0xff000000; + break; + case PixelBitMask: + ei.fb_red_mask = gopi->PixelInformation.RedMask; + ei.fb_green_mask = gopi->PixelInformation.GreenMask; + ei.fb_blue_mask = gopi->PixelInformation.BlueMask; + ei.fb_reserved_mask = + gopi->PixelInformation.ReservedMask; + break; + default: + break; + } + ei.fb_addr = gop->Mode->FrameBufferBase; + ei.fb_size = gop->Mode->FrameBufferSize; + ei.fb_height = gopi->VerticalResolution; + ei.fb_width = gopi->HorizontalResolution; + ei.fb_pixpsl = gopi->PixelsPerScanLine; + } + + addbootarg(BOOTARG_EFIINFO, sizeof(ei), &ei); +} + +void +_rtt(void) +{ +#ifdef EFI_DEBUG + printf("Hit any key to reboot\n"); + efi_cons_getc(0); +#endif + EFI_CALL(RS->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL); + for (;;) + continue; +} + +time_t +getsecs(void) +{ + EFI_TIME t; + time_t r = 0; + int y = 0; + const int daytab[][14] = { + { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }, + { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + }; +#define isleap(_y) (((_y) % 4) == 0 && (((_y) % 100) != 0 || ((_y) % 400) == 0)) + + EFI_CALL(ST->RuntimeServices->GetTime, &t, NULL); + + /* Calc days from UNIX epoch */ + r = (t.Year - 1970) * 365; + for (y = 1970; y < t.Year; y++) { + if (isleap(y)) + r++; + } + r += daytab[isleap(t.Year)? 1 : 0][t.Month] + t.Day; + + /* Calc secs */ + r *= 60 * 60 * 24; + r += ((t.Hour * 60) + t.Minute) * 60 + t.Second; + if (-24 * 60 < t.TimeZone && t.TimeZone < 24 * 60) + r += t.TimeZone * 60; + + return (r); +} + +u_int +sleep(u_int i) +{ + time_t t; + + /* + * Loop for the requested number of seconds, polling, + * so that it may handle interrupts. + */ + for (t = getsecs() + i; getsecs() < t; cnischar()) + ; + + return 0; +} + +/*********************************************************************** + * Commands + ***********************************************************************/ +int +Xexit_efi(void) +{ + EFI_CALL(BS->Exit, IH, 0, 0, NULL); + for (;;) + continue; + return (0); +} + +int +Xvideo_efi(void) +{ + int i, mode = -1; + + if (cmd.argc >= 2) { + mode = strtol(cmd.argv[1], NULL, 10); + if (0 <= mode && mode < nitems(efi_video) && + efi_video[mode].cols > 0) { + EFI_CALL(conout->SetMode, conout, mode); + efi_video_reset(); + } + } else { + for (i = 0; i < nitems(efi_video) && + i < conout->Mode->MaxMode; i++) { + if (efi_video[i].cols > 0) + printf("Mode %d: %d x %d\n", i, + efi_video[i].cols, + efi_video[i].rows); + } + printf("\n"); + } + printf("Current Mode = %d\n", conout->Mode->Mode); + + return (0); +} + +int +Xpoweroff_efi(void) +{ + EFI_CALL(RS->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL); + return (0); +} + +int +Xgop_efi(void) +{ + EFI_STATUS status; + int i, mode = -1; + UINTN sz; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION + *gopi; + + status = EFI_CALL(BS->LocateProtocol, &gop_guid, NULL, + (void **)&gop); + if (EFI_ERROR(status)) + return (0); + + if (cmd.argc >= 2) { + mode = strtol(cmd.argv[1], NULL, 10); + if (0 <= mode && mode < gop->Mode->MaxMode) { + status = EFI_CALL(gop->QueryMode, gop, mode, + &sz, &gopi); + if (!EFI_ERROR(status)) { + if (efi_gop_setmode(mode) == EFI_SUCCESS) + gopmode = mode; + } + } + } else { + for (i = 0; i < gop->Mode->MaxMode; i++) { + status = EFI_CALL(gop->QueryMode, gop, i, &sz, &gopi); + if (EFI_ERROR(status)) + continue; + printf("Mode %d: %d x %d (stride = %d)\n", i, + gopi->HorizontalResolution, + gopi->VerticalResolution, + gopi->PixelsPerScanLine); + } + printf("\n"); + } + printf("Current Mode = %d\n", gop->Mode->Mode); + + return (0); +} diff --git a/sys/arch/amd64/stand/efi32/efiboot.h b/sys/arch/amd64/stand/efi32/efiboot.h new file mode 100644 index 00000000000..beb3be554de --- /dev/null +++ b/sys/arch/amd64/stand/efi32/efiboot.h @@ -0,0 +1,41 @@ +/* $OpenBSD: efiboot.h,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void efi_cleanup(void); +void efi_cons_probe(struct consdev *); +void efi_memprobe(void); +void efi_hardprobe(void); +void efi_diskprobe(void); +void efi_pxeprobe(void); +void efi_cons_init(struct consdev *); +int efi_cons_getc(dev_t); +void efi_cons_putc(dev_t, int); +int efi_cons_getshifts(dev_t dev); +void efi_com_probe(struct consdev *); +void efi_com_init(struct consdev *); +int efi_com_getc(dev_t); +void efi_com_putc(dev_t, int); +int Xvideo_efi(void); +int Xgop_efi(void); +int Xexit_efi(void); +void efi_makebootargs(void); + +int Xpoweroff_efi(void); + +extern void (*run_i386)(u_long, u_long, int, int, int, int, int, int, int, int) + __attribute__ ((noreturn)); diff --git a/sys/arch/amd64/stand/efi32/eficall.S b/sys/arch/amd64/stand/efi32/eficall.S new file mode 100644 index 00000000000..fd73a2c6fdd --- /dev/null +++ b/sys/arch/amd64/stand/efi32/eficall.S @@ -0,0 +1,64 @@ +/* $OpenBSD: eficall.S,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <machine/asm.h> + +/* + * Rearrange the arguments to call the given function by EFI ABI. + * + * efi_call(nargs, func, arg[0], arg[1], arg[2], arg[3], arg[4], ...) + * ---------------------------------------------------------------------- + * BSD: RDI RSI RDX RCX R8 R9 stack + * EFI: - - RCX RDX R8 R9 stack (w/shadow) + */ +ENTRY(efi_call) + push %rbp + mov %rsp, %rbp + + xchg %rcx, %rdx + mov %rcx, %rax + mov %rdi, %rcx + + /* + * set "nargs - 2 + 4 + 1" (= %rdi + 3) for next call stack size. + * (nargs - 2) is for arguments in stack, +4 for shadow registers + * and +1 for alignment + */ + add $3, %rdi + + shl $3, %rdi /* 64-bit word */ + sub %rdi, %rsp /* get the stack */ + and $(-0x10), %rsp /* align 16 bytes */ + + /* copy args */ + sub $3, %rcx + cmp $1, %rcx + jle 2f + +1: /* loop arg[n-1] .. arg[4] */ + mov 0x8(%rbp, %rcx, 8), %rdi + mov %rdi, 0x18(%rsp, %rcx, 8) + loopnz 1b +2: + mov %rax, %rcx + + call *%rsi + + mov %rbp, %rsp + pop %rbp + retq diff --git a/sys/arch/amd64/stand/efi32/eficall.h b/sys/arch/amd64/stand/efi32/eficall.h new file mode 100644 index 00000000000..2d8cf2923d2 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/eficall.h @@ -0,0 +1,55 @@ +/* $OpenBSD: eficall.h,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__amd64__) + +#define EFI_CALL(_func_, ...) (_func_)(__VA_ARGS__) + +#else + +extern uint64_t efi_call(int, void *, ...); + +#define _call_0(_func) \ + efi_call(0, (_func)) +#define _call_1(_func, _1) \ + efi_call(1, (_func), (_1)) +#define _call_2(_func, _1, _2) \ + efi_call(2, (_func), (_1), (_2)) +#define _call_3(_func, _1, _2, _3) \ + efi_call(3, (_func), (_1), (_2), (_3)) +#define _call_4(_func, _1, _2, _3, _4) \ + efi_call(4, (_func), (_1), (_2), (_3), (_4)) +#define _call_5(_func, _1, _2, _3, _4, _5) \ + efi_call(5, (_func), (_1), (_2), (_3), (_4), (_5)) +#define _call_6(_func, _1, _2, _3, _4, _5, _6) \ + efi_call(6, (_func), (_1), (_2), (_3), (_4), (_5), (_6)) +#define _call_7(_func, _1, _2, _3, _4, _5, _6, _7) \ + efi_call(7, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7)) +#define _call_8(_func, _1, _2, _3, _4, _5, _6, _7, _8) \ + efi_call(8, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8)) +#define _call_9(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + efi_call(9, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8), (_9)) +#define _call_10(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + efi_call(10, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8), (_9), (_10)) + +#define _efi_call_fn(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _fn, ...) _fn + +#define EFI_CALL(...) \ + _efi_call_fn(__VA_ARGS__, _call_10, _call_9, _call_8, _call_7, _call_6, \ + _call_5, _call_4, _call_3, _call_2, _call_1, _call_0)(__VA_ARGS__) +#endif diff --git a/sys/arch/amd64/stand/efi32/efidev.c b/sys/arch/amd64/stand/efi32/efidev.c new file mode 100644 index 00000000000..19915a8993f --- /dev/null +++ b/sys/arch/amd64/stand/efi32/efidev.c @@ -0,0 +1,794 @@ +/* $OpenBSD: efidev.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1996 Michael Shalayeff + * Copyright (c) 2003 Tobias Weingartner + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/disklabel.h> +#include <lib/libz/zlib.h> +#include <isofs/cd9660/iso.h> + +#include "libsa.h" +#include "disk.h" + +#ifdef SOFTRAID +#include <dev/softraidvar.h> +#include <lib/libsa/softraid.h> +#include "softraid_amd64.h" +#endif + +#include <efi.h> +#include "eficall.h" + +extern int debug; + +#include "efidev.h" +#include "biosdev.h" /* for dklookup() */ + +#define EFI_BLKSPERSEC(_ed) ((_ed)->blkio->Media->BlockSize / DEV_BSIZE) +#define EFI_SECTOBLK(_ed, _n) ((_n) * EFI_BLKSPERSEC(_ed)) + +struct efi_diskinfo { + EFI_BLOCK_IO *blkio; + UINT32 mediaid; +}; + +int bios_bootdev; +static EFI_STATUS + efid_io(int, efi_diskinfo_t, u_int, int, void *); +static int efid_diskio(int, struct diskinfo *, u_int, int, void *); +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->efi_info = alloc(sizeof(struct efi_diskinfo)); + dip->efi_info->blkio = blkio; + dip->efi_info->mediaid = blkio->Media->MediaId; + dip->diskio = efid_diskio; + dip->strategy = efistrategy; +} + +static EFI_STATUS +efid_io(int rw, efi_diskinfo_t ed, u_int off, int nsect, void *buf) +{ + u_int blks, lba, i_lblks, i_tblks, i_nblks; + EFI_STATUS status = EFI_SUCCESS; + static u_char *iblk = NULL; + static u_int iblksz = 0; + + /* block count of the intrisic block size in DEV_BSIZE */ + blks = EFI_BLKSPERSEC(ed); + if (blks == 0) + /* block size < 512. HP Stream 13 actually has such a disk. */ + return (EFI_UNSUPPORTED); + + /* leading and trailing unaligned blocks in intrisic block */ + i_lblks = ((off % blks) == 0)? 0 : blks - (off % blks); + i_tblks = (nsect > i_lblks)? (off + nsect) % blks : 0; + + /* aligned blocks in intrisic block */ + i_nblks = (nsect > i_lblks + i_tblks)? nsect - (i_lblks + i_tblks) : 0; + + lba = (off + i_lblks) / blks; + + switch (rw) { + case F_READ: + /* allocate the space for reading unaligned blocks */ + if (ed->blkio->Media->BlockSize != DEV_BSIZE) { + if (iblk && iblksz < ed->blkio->Media->BlockSize) { + free(iblk, iblksz); + iblk = NULL; + } + if (iblk == NULL) { + iblk = alloc(ed->blkio->Media->BlockSize); + iblksz = ed->blkio->Media->BlockSize; + } + } + if (i_lblks > 0) { + status = EFI_CALL(ed->blkio->ReadBlocks, + ed->blkio, ed->mediaid, lba - 1, + ed->blkio->Media->BlockSize, iblk); + if (EFI_ERROR(status)) + goto on_eio; + memcpy(buf, iblk + (blks - i_lblks) * DEV_BSIZE, + min(nsect, i_lblks) * DEV_BSIZE); + } + if (i_nblks > 0) { + status = EFI_CALL(ed->blkio->ReadBlocks, + ed->blkio, ed->mediaid, lba, + ed->blkio->Media->BlockSize * (i_nblks / blks), + buf + (i_lblks * DEV_BSIZE)); + if (EFI_ERROR(status)) + goto on_eio; + } + if (i_tblks > 0) { + status = EFI_CALL(ed->blkio->ReadBlocks, + ed->blkio, ed->mediaid, lba + (i_nblks / blks), + ed->blkio->Media->BlockSize, iblk); + if (EFI_ERROR(status)) + goto on_eio; + memcpy(buf + (i_lblks + i_nblks) * DEV_BSIZE, iblk, + i_tblks * DEV_BSIZE); + } + break; + case F_WRITE: + if (ed->blkio->Media->ReadOnly) + goto on_eio; + /* XXX not yet */ + goto on_eio; + break; + } + return (EFI_SUCCESS); + +on_eio: + 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->efi_info, 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, ...) +{ +#ifdef SOFTRAID + struct sr_boot_volume *bv; +#endif + register char *cp, **file; + dev_t maj, unit, part; + struct diskinfo *dip; + int biosdev, devlen; +#if 0 + const char *st; +#endif + va_list ap; + char *dev; + + va_start(ap, f); + cp = *(file = va_arg(ap, char **)); + va_end(ap); + +#ifdef EFI_DEBUG + if (debug) + printf("%s\n", cp); +#endif + + f->f_devdata = NULL; + + /* Search for device specification. */ + dev = cp; + if (cp[4] == ':') + devlen = 2; + else if (cp[5] == ':') + devlen = 3; + else + return ENOENT; + cp += devlen; + + /* Get unit. */ + if ('0' <= *cp && *cp <= '9') + unit = *cp++ - '0'; + else { + printf("Bad unit number\n"); + return EUNIT; + } + + /* Get partition. */ + if ('a' <= *cp && *cp <= 'p') + part = *cp++ - 'a'; + else { + printf("Bad partition\n"); + return EPART; + } + + /* Get filename. */ + cp++; /* skip ':' */ + if (*cp != 0) + *file = cp; + else + f->f_flags |= F_RAW; + +#ifdef SOFTRAID + /* Intercept softraid disks. */ + if (strncmp("sr", dev, 2) == 0) { + + /* 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 = efid_diskio; + dip->strategy = efistrategy; + bv->sbv_diskinfo = dip; + dip->sr_vol = bv; + dip->bios_info.flags |= BDI_BADLABEL; + } + + dip = bv->sbv_diskinfo; + + if (dip->bios_info.flags & BDI_BADLABEL) { + /* Attempt to read disklabel. */ + bv->sbv_part = 'c'; + if (sr_getdisklabel(bv, &dip->disklabel)) + return ERDLAB; + dip->bios_info.flags &= ~BDI_BADLABEL; + check_hibernate(dip); + } + + bv->sbv_part = part + 'a'; + + bootdev_dip = dip; + f->f_devdata = dip; + + return 0; + } +#endif + for (maj = 0; maj < nbdevs && + strncmp(dev, bdevs[maj], devlen); maj++); + if (maj >= nbdevs) { + printf("Unknown device: "); + for (cp = *file; *cp != ':'; cp++) + putchar(*cp); + putchar('\n'); + return EADAPT; + } + + biosdev = unit; + switch (maj) { + case 0: /* wd */ + case 4: /* sd */ + case 17: /* hd */ + biosdev |= 0x80; + break; + case 2: /* fd */ + break; + case 6: /* cd */ + biosdev |= 0xe0; + break; + default: + return ENXIO; + } + + /* Find device */ + dip = dklookup(biosdev); + if (dip == NULL) + return ENXIO; + bootdev_dip = dip; + + /* Fix up bootdev */ + { dev_t bsd_dev; + bsd_dev = dip->bios_info.bsd_dev; + dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev), + B_CONTROLLER(bsd_dev), unit, part); + dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev), + B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part); + } + +#if 0 + dip->bios_info.bsd_dev = dip->bootdev; + bootdev = dip->bootdev; +#endif + +#ifdef EFI_DEBUG + if (debug) { + printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n", + dip->bios_info.bios_heads, dip->bios_info.bios_sectors, + dip->bios_info.bios_edd); + } +#endif + +#if 0 +/* + * XXX In UEFI, media change can be detected by MediaID + */ + /* Try for disklabel again (might be removable media) */ + if (dip->bios_info.flags & BDI_BADLABEL) { + st = efi_getdisklabel(dip->efi_info, &dip->disklabel); +#ifdef EFI_DEBUG + if (debug && st) + printf("%s\n", st); +#endif + if (!st) { + dip->bios_info.flags &= ~BDI_BADLABEL; + dip->bios_info.flags |= BDI_GOODLABEL; + } else + return ERDLAB; + } +#endif + f->f_devdata = dip; + + return 0; +} + +int +efistrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf, + size_t *rsize) +{ + struct diskinfo *dip = (struct diskinfo *)devdata; + u_int8_t error = 0; + size_t nsect; + +#ifdef SOFTRAID + /* Intercept strategy for softraid volumes. */ + if (dip->sr_vol) + return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize); +#endif + nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE; + blk += DL_SECTOBLK(&dip->disklabel, + dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset); + + if (blk < 0) + error = EINVAL; + else + error = dip->diskio(rw, dip, blk, nsect, buf); + +#ifdef EFI_DEBUG + if (debug) { + if (error != 0) + printf("=0x%x(%s)", error, error); + putchar('\n'); + } +#endif + 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; +} + +void +efi_dump_diskinfo(void) +{ + efi_diskinfo_t ed; + struct diskinfo *dip; + bios_diskinfo_t *bdi; + uint64_t siz; + const char *sizu; + + printf("Disk\tBlkSiz\tIoAlign\tSize\tFlags\tChecksum\n"); + TAILQ_FOREACH(dip, &disklist, list) { + bdi = &dip->bios_info; + ed = dip->efi_info; + + siz = (ed->blkio->Media->LastBlock + 1) * + ed->blkio->Media->BlockSize; + siz /= 1024 * 1024; + if (siz < 10000) + sizu = "MB"; + else { + siz /= 1024; + sizu = "GB"; + } + + printf("%cd%d\t%u\t%u\t%u%s\t0x%x\t0x%x\t%s\n", + (B_TYPE(bdi->bsd_dev) == 6)? 'c' : 'h', + (bdi->bios_number & 0x1f), + ed->blkio->Media->BlockSize, + ed->blkio->Media->IoAlign, (unsigned)siz, sizu, + bdi->flags, bdi->checksum, + (ed->blkio->Media->RemovableMedia)? "Removable" : ""); + } +} diff --git a/sys/arch/amd64/stand/efi32/efidev.h b/sys/arch/amd64/stand/efi32/efidev.h new file mode 100644 index 00000000000..1c4fe65fb85 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/efidev.h @@ -0,0 +1,38 @@ +/* $OpenBSD: efidev.h,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1996 Michael Shalayeff + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* efidev.c */ +void efid_init(struct diskinfo *, void *handle); +const char *efi_getdisklabel(efi_diskinfo_t, struct disklabel *); +int efiopen(struct open_file *, ...); +int efistrategy(void *, int, daddr32_t, size_t, void *, size_t *); +int eficlose(struct open_file *); +int efiioctl(struct open_file *, u_long, void *); +void efi_dump_diskinfo(void); diff --git a/sys/arch/amd64/stand/efi32/efipxe.c b/sys/arch/amd64/stand/efi32/efipxe.c new file mode 100644 index 00000000000..382a05382a1 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/efipxe.c @@ -0,0 +1,306 @@ +/* $OpenBSD: efipxe.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ +/* + * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/disklabel.h> +#include <machine/biosvar.h> + +#include <libsa.h> +#include <lib/libsa/tftp.h> + +#include "disk.h" + +#include <efi.h> +#include <efiapi.h> +#include "eficall.h" +#include "efiboot.h" + +extern EFI_BOOT_SERVICES *BS; +extern EFI_DEVICE_PATH *efi_bootdp; + +extern char *bootmac; +static UINT8 boothw[16]; +static EFI_IP_ADDRESS bootip, servip; +static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; +static EFI_GUID pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL; +static EFI_PXE_BASE_CODE *PXE = NULL; + +extern int efi_device_path_depth(EFI_DEVICE_PATH *dp, int); +extern int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int); + +/* + * TFTP initial probe. This function discovers PXE handles and tries + * to figure out if there has already been a successfull PXE handshake. + * If so, set the PXE variable. + */ +void +efi_pxeprobe(void) +{ + 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 = NULL; + + status = EFI_CALL(BS->HandleProtocol, handles[i], + &devp_guid, (void **)&dp0); + if (status != EFI_SUCCESS) + continue; + + depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH); + if (efi_device_path_ncmp(efi_bootdp, dp0, depth)) + continue; + + status = EFI_CALL(BS->HandleProtocol, handles[i], &pxe_guid, + (void **)&pxe); + if (status != EFI_SUCCESS) + continue; + + if (pxe->Mode == NULL) + continue; + + if (pxe->Mode->DhcpAckReceived) { + dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *) + &pxe->Mode->DhcpAck; + } + if (pxe->Mode->PxeReplyReceived) { + dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *) + &pxe->Mode->PxeReply; + } + + if (dhcp) { + memcpy(&bootip, dhcp->BootpYiAddr, sizeof(bootip)); + memcpy(&servip, dhcp->BootpSiAddr, sizeof(servip)); + memcpy(boothw, dhcp->BootpHwAddr, sizeof(boothw)); + bootmac = boothw; + PXE = pxe; + break; + } + } +} + +/* + * TFTP filesystem layer implementation. + */ +struct tftp_handle { + unsigned char *inbuf; /* input buffer */ + size_t inbufsize; + off_t inbufoff; +}; + +struct fs_ops tftp_fs = { + tftp_open, tftp_close, tftp_read, tftp_write, tftp_seek, + tftp_stat, tftp_readdir +}; + +int +tftp_open(char *path, struct open_file *f) +{ + struct tftp_handle *tftpfile; + EFI_PHYSICAL_ADDRESS addr; + EFI_STATUS status; + UINT64 size; + + if (strcmp("TFTP", f->f_dev->dv_name) != 0) + return ENXIO; + + if (PXE == NULL) + return ENXIO; + + tftpfile = alloc(sizeof(*tftpfile)); + if (tftpfile == NULL) + return ENOMEM; + memset(tftpfile, 0, sizeof(*tftpfile)); + + status = EFI_CALL(PXE->Mtftp, PXE, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, FALSE, &size, NULL, &servip, 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, &servip, path, NULL, FALSE); + if (status != EFI_SUCCESS) { + free(tftpfile, sizeof(*tftpfile)); + return ENXIO; + } +out: + f->f_fsdata = tftpfile; + return 0; +} + +int +tftp_close(struct open_file *f) +{ + struct tftp_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 +tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid) +{ + struct tftp_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 +tftp_write(struct open_file *f, void *start, size_t size, size_t *resid) +{ + return EROFS; +} + +off_t +tftp_seek(struct open_file *f, off_t offset, int where) +{ + struct tftp_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 +tftp_stat(struct open_file *f, struct stat *sb) +{ + struct tftp_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 +tftp_readdir(struct open_file *f, char *name) +{ + return EOPNOTSUPP; +} + +/* + * Dummy TFTP network device. + */ +int +tftpopen(struct open_file *f, ...) +{ + char **fname, *p; + va_list ap; + + va_start(ap, f); + fname = va_arg(ap, char **); + va_end(ap); + + /* No PXE set -> no PXE available */ + if (PXE == NULL) + return 1; + + /* Parse tftp:bsd into "tftp" and "bsd" */ + for (p = *fname; *p != ':' && *p != '\0'; p++) + ; + if (*p != ':') + return 1; + if (strncmp(*fname, "tftp", p - *fname) != 0) + return 1; + + *fname = p + 1; + return 0; +} + +int +tftpclose(struct open_file *f) +{ + return 0; +} + +int +tftpioctl(struct open_file *f, u_long cmd, void *data) +{ + return EOPNOTSUPP; +} + +int +tftpstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf, + size_t *rsize) +{ + return EOPNOTSUPP; +} diff --git a/sys/arch/amd64/stand/efi32/efipxe.h b/sys/arch/amd64/stand/efi32/efipxe.h new file mode 100644 index 00000000000..b448db8b6f7 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/efipxe.h @@ -0,0 +1,21 @@ +/* $OpenBSD: efipxe.h,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ +/* + * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int tftpopen(struct open_file *, ...); +int tftpclose(struct open_file *); +int tftpioctl(struct open_file *, u_long, void *); +int tftpstrategy(void *, int, daddr32_t, size_t, void *, size_t *); diff --git a/sys/arch/amd64/stand/efi32/efirng.c b/sys/arch/amd64/stand/efi32/efirng.c new file mode 100644 index 00000000000..0178c14551a --- /dev/null +++ b/sys/arch/amd64/stand/efi32/efirng.c @@ -0,0 +1,87 @@ +/* $OpenBSD: efirng.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> + +#include <efi.h> +#include <efiapi.h> + +#include "eficall.h" +#include "libsa.h" + +extern EFI_BOOT_SERVICES *BS; + +/* Random Number Generator Protocol */ + +#define EFI_RNG_PROTOCOL_GUID \ + { 0x3152bca5, 0xeade, 0x433d, {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44} } + +INTERFACE_DECL(_EFI_RNG_PROTOCOL); + +typedef EFI_GUID EFI_RNG_ALGORITHM; + +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_INFO) ( + IN struct _EFI_RNG_PROTOCOL *This, + IN OUT UINTN *RNGAlgorithmListSize, + OUT EFI_RNG_ALGORITHM *RNGAlgorithmList + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_RNG) ( + IN struct _EFI_RNG_PROTOCOL *This, + IN EFI_RNG_ALGORITHM *RNGAlgorithm, OPTIONAL + IN UINTN RNGValueLength, + OUT UINT8 *RNGValue + ); + +typedef struct _EFI_RNG_PROTOCOL { + EFI_RNG_GET_INFO GetInfo; + EFI_RNG_GET_RNG GetRNG; +} EFI_RNG_PROTOCOL; + +static EFI_GUID rng_guid = EFI_RNG_PROTOCOL_GUID; + +void +fwrandom(char *buf, size_t buflen) +{ + EFI_STATUS status; + EFI_RNG_PROTOCOL *rng = NULL; + UINT8 *random; + size_t i; + + status = EFI_CALL(BS->LocateProtocol, &rng_guid, NULL, (void **)&rng); + if (rng == NULL || EFI_ERROR(status)) + return; + + random = alloc(buflen); + + status = EFI_CALL(rng->GetRNG, rng, NULL, buflen, random); + if (EFI_ERROR(status)) { + printf("RNG GetRNG() failed (%d)\n", status); + goto out; + } + + for (i = 0; i < buflen; i++) + buf[i] ^= random[i]; + +out: + free(random, buflen); +} diff --git a/sys/arch/amd64/stand/efi32/exec_i386.c b/sys/arch/amd64/stand/efi32/exec_i386.c new file mode 100644 index 00000000000..87c5c98b5e9 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/exec_i386.c @@ -0,0 +1,220 @@ +/* $OpenBSD: exec_i386.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1997-1998 Michael Shalayeff + * Copyright (c) 1997 Tobias Weingartner + * 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/param.h> +#include <sys/disklabel.h> +#include <dev/cons.h> +#include <lib/libsa/loadfile.h> +#include <machine/biosvar.h> +#include <machine/specialreg.h> +#include <stand/boot/bootarg.h> + +#include "cmd.h" +#include "disk.h" +#include "libsa.h" + +#ifdef SOFTRAID +#include <dev/softraidvar.h> +#include <lib/libsa/softraid.h> +#include "softraid_amd64.h" +#endif + +#include "efiboot.h" + +typedef void (*startfuncp)(int, int, int, int, int, int, int, int) + __attribute__ ((noreturn)); + +void ucode_load(void); +extern struct cmd_state cmd; + +char *bootmac = NULL; + +void +run_loadfile(uint64_t *marks, int howto) +{ + u_long entry; +#ifdef EXEC_DEBUG + extern int debug; +#endif + dev_t bootdev = bootdev_dip->bootdev; + size_t ac = BOOTARG_LEN; + caddr_t av = (caddr_t)BOOTARG_OFF; + bios_consdev_t cd; + extern int com_speed; /* from bioscons.c */ + extern int com_addr; + bios_ddb_t ddb; + extern int db_console; + bios_bootduid_t bootduid; +#ifdef SOFTRAID + bios_bootsr_t bootsr; + struct sr_boot_volume *bv; +#endif + int i; + u_long delta; + extern u_long efi_loadaddr; + + if ((av = alloc(ac)) == NULL) + panic("alloc for bootarg"); + efi_makebootargs(); + delta = DEFAULT_KERNEL_ADDRESS - efi_loadaddr; + if (sa_cleanup != NULL) + (*sa_cleanup)(); + + cd.consdev = cn_tab->cn_dev; + cd.conspeed = com_speed; + cd.consaddr = com_addr; + cd.consfreq = 0; + addbootarg(BOOTARG_CONSDEV, sizeof(cd), &cd); + + if (bootmac != NULL) + addbootarg(BOOTARG_BOOTMAC, sizeof(bios_bootmac_t), bootmac); + + if (db_console != -1) { + ddb.db_console = db_console; + addbootarg(BOOTARG_DDB, sizeof(ddb), &ddb); + } + + bcopy(bootdev_dip->disklabel.d_uid, &bootduid.duid, sizeof(bootduid)); + addbootarg(BOOTARG_BOOTDUID, sizeof(bootduid), &bootduid); + + ucode_load(); + +#ifdef SOFTRAID + if (bootdev_dip->sr_vol != NULL) { + bv = bootdev_dip->sr_vol; + bzero(&bootsr, sizeof(bootsr)); + bcopy(&bv->sbv_uuid, &bootsr.uuid, sizeof(bootsr.uuid)); + if (bv->sbv_maskkey != NULL) + bcopy(bv->sbv_maskkey, &bootsr.maskkey, + sizeof(bootsr.maskkey)); + addbootarg(BOOTARG_BOOTSR, sizeof(bios_bootsr_t), &bootsr); + explicit_bzero(&bootsr, sizeof(bootsr)); + } + + sr_clear_keys(); +#endif + + entry = marks[MARK_ENTRY] & 0x0fffffff; + entry += delta; + + printf("entry point at 0x%lx\n", entry); + + /* Sync the memory map and call ExitBootServices() */ + efi_cleanup(); + + /* Pass memory map to the kernel */ + mem_pass(); + + /* + * This code may be used both for 64bit and 32bit. Make sure the + * bootarg is always 32bit, even on amd64. + */ +#ifdef __amd64__ + makebootargs32(av, &ac); +#else + makebootargs(av, &ac); +#endif + + /* + * Move the loaded kernel image to the usual place after calling + * ExitBootServices(). + */ + memmove((void *)marks[MARK_START] + delta, (void *)marks[MARK_START], + marks[MARK_END] - marks[MARK_START]); + for (i = 0; i < MARK_MAX; i++) + marks[i] += delta; + +#ifdef __amd64__ + (*run_i386)((u_long)run_i386, entry, howto, bootdev, BOOTARG_APIVER, + marks[MARK_END], extmem, cnvmem, ac, (intptr_t)av); +#else + /* stack and the gung is ok at this point, so, no need for asm setup */ + (*(startfuncp)entry)(howto, bootdev, BOOTARG_APIVER, marks[MARK_END], + extmem, cnvmem, ac, (int)av); +#endif + /* not reached */ +} + +void +ucode_load(void) +{ + uint32_t model, family, stepping; + uint32_t dummy, signature; + uint32_t vendor[4]; + bios_ucode_t uc; + struct stat sb; + char path[128]; + size_t buflen; + char *buf; + int fd; + + CPUID(0, dummy, vendor[0], vendor[2], vendor[1]); + vendor[3] = 0; /* NULL-terminate */ + if (strcmp((char *)vendor, "GenuineIntel") != 0) + return; + + CPUID(1, signature, dummy, dummy, dummy); + family = (signature >> 8) & 0x0f; + model = (signature >> 4) & 0x0f; + if (family == 0x6 || family == 0xf) { + family += (signature >> 20) & 0xff; + model += ((signature >> 16) & 0x0f) << 4; + } + stepping = (signature >> 0) & 0x0f; + + snprintf(path, sizeof(path), "%s:/etc/firmware/intel/%02x-%02x-%02x", + cmd.bootdev, family, model, stepping); + + fd = open(path, 0); + if (fd == -1) + return; + + if (fstat(fd, &sb) == -1) + return; + + buflen = sb.st_size; + if (buflen > 128*1024) { + printf("ucode too large\n"); + return; + } + + buf = (char *)(1*1024*1024); + + if (read(fd, buf, buflen) != buflen) { + close(fd); + return; + } + + uc.uc_addr = (uint64_t)buf; + uc.uc_size = (uint64_t)buflen; + addbootarg(BOOTARG_UCODE, sizeof(uc), &uc); + + close(fd); +} diff --git a/sys/arch/amd64/stand/efi32/heap.h b/sys/arch/amd64/stand/efi32/heap.h new file mode 100644 index 00000000000..618db452cf8 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/heap.h @@ -0,0 +1,29 @@ +/* $OpenBSD: heap.h,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <efi.h> + +static char *top = NULL; +#define NEEDS_HEAP_INIT 1 + +static void +heap_init(void) +{ + extern EFI_PHYSICAL_ADDRESS heap; + if (top == NULL) + top = (char *)(uintptr_t)heap; +} diff --git a/sys/arch/amd64/stand/efi32/ldscript.amd64 b/sys/arch/amd64/stand/efi32/ldscript.amd64 new file mode 100644 index 00000000000..ab3ab48c3a9 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/ldscript.amd64 @@ -0,0 +1,65 @@ +/* Same as elf_x86_64_fbsd_efi.lds, except for OUTPUT_FORMAT below - KEEP IN SYNC */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) +SECTIONS +{ + . = 0; + ImageBase = .; + .hash : { *(.hash) } /* this MUST come first! */ + . = ALIGN(4096); + .eh_frame : + { + *(.eh_frame) + } + . = ALIGN(4096); + .text : + { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + } + . = ALIGN(4096); + .reloc : + { + *(.reloc) + } + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.got.plt) + *(.got) + *(.data*) + *(.sdata) + /* the EFI loader doesn't seem to like a .bss section, so we stick + it all into .data: */ + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + *(.rel.local) + } + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rela : + { + *(.rela.data*) + *(.rela.got) + *(.rela.stab) + } + . = ALIGN(4096); + .dynsym : { *(.dynsym) } + . = ALIGN(4096); + .dynstr : { *(.dynstr) } + . = ALIGN(4096); + .ignored.reloc : + { + *(.rela.reloc) + *(.eh_frame) + *(.note.GNU-stack) + } + .comment 0 : { *(.comment) } +} diff --git a/sys/arch/amd64/stand/efi32/ldscript.i386 b/sys/arch/amd64/stand/efi32/ldscript.i386 new file mode 100644 index 00000000000..975e36c14f5 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/ldscript.i386 @@ -0,0 +1,75 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + . = 0; + ImageBase = .; + .hash : { *(.hash) } /* this MUST come first! */ + . = ALIGN(4096); + .text : + { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + } + . = ALIGN(4096); + .sdata : + { + *(.got.plt) + *(.got) + *(.srodata) + *(.sdata) + *(.sbss) + *(.scommon) + } + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.data) + *(.data1) + *(.data.*) + *(.sdata) + *(.got.plt) + *(.got) + /* the EFI loader doesn't seem to like a .bss section, so we stick + it all into .data: */ + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rel : + { + *(.rel.data) + *(.rel.data.*) + *(.rel.got) + *(.rel.stab) + *(.data.rel.ro.local) + *(.data.rel.local) + *(.data.rel.ro) + *(.data.rel*) + } + . = ALIGN(4096); + .reloc : /* This is the PECOFF .reloc section! */ + { + *(.reloc) + } + . = ALIGN(4096); + .dynsym : { *(.dynsym) } + . = ALIGN(4096); + .dynstr : { *(.dynstr) } + . = ALIGN(4096); + /DISCARD/ : + { + *(.rel.reloc) + *(.eh_frame) + *(.note.GNU-stack) + } + .comment 0 : { *(.comment) } +} diff --git a/sys/arch/amd64/stand/efi32/machdep.c b/sys/arch/amd64/stand/efi32/machdep.c new file mode 100644 index 00000000000..5e50922f3c8 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/machdep.c @@ -0,0 +1,95 @@ +/* $OpenBSD: machdep.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2004 Tom Cosgrove + * Copyright (c) 1997-1999 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 AUTHOR OR HIS RELATIVES 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 MIND, 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 "libsa.h" +#include "biosdev.h" +#include <machine/apmvar.h> +#include <machine/biosvar.h> +#include <machine/specialreg.h> +#include <machine/vmmvar.h> + +#include "efiboot.h" + +volatile struct BIOS_regs BIOS_regs; + +#if defined(DEBUG) +#define CKPT(c) (*(u_int16_t*)0xb8148 = 0x4700 + (c)) +#else +#define CKPT(c) /* c */ +#endif + +const char *vmm_hv_signature = VMM_HV_SIGNATURE; + +void +machdep(void) +{ + int i, j, vmm = 0; + struct i386_boot_probes *pr; + uint32_t dummy, ebx, ecx, edx; + dev_t dev; + + /* + * The list of probe routines is now in conf.c. + */ + for (i = 0; i < nibprobes; i++) { + pr = &probe_list[i]; + if (pr != NULL) { + printf("%s:", pr->name); + + for (j = 0; j < pr->count; j++) { + (*(pr->probes)[j])(); + } + + printf("\n"); + } + } + + CPUID(0x1, dummy, dummy, ecx, dummy); + if (ecx & CPUIDECX_HV) { + CPUID(0x40000000, dummy, ebx, ecx, edx); + if (memcmp(&ebx, &vmm_hv_signature[0], sizeof(uint32_t)) == 0 && + memcmp(&ecx, &vmm_hv_signature[4], sizeof(uint32_t)) == 0 && + memcmp(&edx, &vmm_hv_signature[8], sizeof(uint32_t)) == 0) + vmm = 1; + } + + /* Set console to com0/115200 by default in vmm */ + if (vmm) { + dev = ttydev("com0"); + cnspeed(dev, 115200); + cnset(dev); + } +} + +int +check_skip_conf(void) +{ + /* Return non-zero (skip boot.conf) if Control "shift" key down */ + return (efi_cons_getshifts(0) & 0x04); +} diff --git a/sys/arch/amd64/stand/efi32/memprobe.c b/sys/arch/amd64/stand/efi32/memprobe.c new file mode 100644 index 00000000000..e119a990737 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/memprobe.c @@ -0,0 +1,173 @@ +/* $OpenBSD: memprobe.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 1997-1999 Michael Shalayeff + * Copyright (c) 1997-1999 Tobias Weingartner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/param.h> +#include <machine/biosvar.h> +#include <dev/isa/isareg.h> +#include <stand/boot/bootarg.h> +#include "libsa.h" + +u_int cnvmem, extmem; /* XXX - compatibility */ + +bios_memmap_t bios_memmap[64]; /* This is easier */ + +void +dump_biosmem(bios_memmap_t *tm) +{ + register bios_memmap_t *p; + register u_int total = 0; + + if (tm == NULL) + tm = bios_memmap; + + for (p = tm; p->type != BIOS_MAP_END; p++) { + printf("Region %ld: type %u at 0x%llx for %uKB\n", + (long)(p - tm), p->type, p->addr, + (u_int)(p->size / 1024)); + + if (p->type == BIOS_MAP_FREE) + total += p->size / 1024; + } + + printf("Low ram: %dKB High ram: %dKB\n", cnvmem, extmem); + printf("Total free memory: %uKB\n", total); +} + +int +mem_limit(long long ml) +{ + register bios_memmap_t *p; + + for (p = bios_memmap; p->type != BIOS_MAP_END; p++) { + register int64_t sp = p->addr, ep = p->addr + p->size; + + if (p->type != BIOS_MAP_FREE) + continue; + + /* Wholly above limit, nuke it */ + if ((sp >= ml) && (ep >= ml)) { + bcopy (p + 1, p, (char *)bios_memmap + + sizeof(bios_memmap) - (char *)p); + } else if ((sp < ml) && (ep >= ml)) { + p->size -= (ep - ml); + } + } + return 0; +} + +int +mem_delete(long long sa, long long ea) +{ + register bios_memmap_t *p; + + for (p = bios_memmap; p->type != BIOS_MAP_END; p++) { + if (p->type == BIOS_MAP_FREE) { + register int64_t sp = p->addr, ep = p->addr + p->size; + + /* can we eat it as a whole? */ + if ((sa - sp) <= PAGE_SIZE && (ep - ea) <= PAGE_SIZE) { + bcopy(p + 1, p, (char *)bios_memmap + + sizeof(bios_memmap) - (char *)p); + break; + /* eat head or legs */ + } else if (sa <= sp && sp < ea) { + p->addr = ea; + p->size = ep - ea; + break; + } else if (sa < ep && ep <= ea) { + p->size = sa - sp; + break; + } else if (sp < sa && ea < ep) { + /* bite in half */ + bcopy(p, p + 1, (char *)bios_memmap + + sizeof(bios_memmap) - (char *)p - + sizeof(bios_memmap[0])); + p[1].addr = ea; + p[1].size = ep - ea; + p->size = sa - sp; + break; + } + } + } + return 0; +} + +int +mem_add(long long sa, long long ea) +{ + register bios_memmap_t *p; + + for (p = bios_memmap; p->type != BIOS_MAP_END; p++) { + if (p->type == BIOS_MAP_FREE) { + register int64_t sp = p->addr, ep = p->addr + p->size; + + /* is it already there? */ + if (sp <= sa && ea <= ep) { + break; + /* join head or legs */ + } else if (sa < sp && sp <= ea) { + p->addr = sa; + p->size = ep - sa; + break; + } else if (sa <= ep && ep < ea) { + p->size = ea - sp; + break; + } else if (ea < sp) { + /* insert before */ + bcopy(p, p + 1, (char *)bios_memmap + + sizeof(bios_memmap) - (char *)(p - 1)); + p->addr = sa; + p->size = ea - sa; + break; + } + } + } + + /* meaning add new item at the end of the list */ + if (p->type == BIOS_MAP_END) { + p[1] = p[0]; + p->type = BIOS_MAP_FREE; + p->addr = sa; + p->size = ea - sa; + } + + return 0; +} + +void +mem_pass(void) +{ + bios_memmap_t *p; + + for (p = bios_memmap; p->type != BIOS_MAP_END; p++) + ; + addbootarg(BOOTARG_MEMMAP, (p - bios_memmap + 1) * sizeof *bios_memmap, + bios_memmap); +} diff --git a/sys/arch/amd64/stand/efi32/run_i386.S b/sys/arch/amd64/stand/efi32/run_i386.S new file mode 100644 index 00000000000..ccb423d0079 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/run_i386.S @@ -0,0 +1,115 @@ +/* $OpenBSD: run_i386.S,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <machine/asm.h> +#include <machine/specialreg.h> + +#define CODE_SEGMENT 0x10 +#define DATA_SEGMENT 0x18 + + .globl _C_LABEL(run_i386_size) +_C_LABEL(run_i386_size): + .long run_i386_end - _C_LABEL(run_i386_start) + + .align 4 + .text + .globl _C_LABEL(run_i386_start) +_C_LABEL(run_i386_start): +start: + /* + * run_i386(_start) is to call the loaded kernel's start() with + * 32bit segment mode from x64 mode. + * %rdi == loaded start address, %rsi == kernel start address + */ + + /* re-arrange the parameters for the x86 calling convension */ + mov %edx, (run_i386_end - start - 0x20)(%rdi) + mov %ecx, (run_i386_end - start - 0x1c)(%rdi) + mov %r8d, (run_i386_end - start - 0x18)(%rdi) + mov %r9d, (run_i386_end - start - 0x14)(%rdi) + mov 0x8(%rsp), %edx + mov %edx, (run_i386_end - start - 0x10)(%rdi) + mov 0x10(%rsp), %edx + mov %edx, (run_i386_end - start - 0xc)(%rdi) + mov 0x18(%rsp), %edx + mov %edx, (run_i386_end - start - 0x8)(%rdi) + mov 0x20(%rsp), %edx + mov %edx, (run_i386_end - start - 0x4)(%rdi) + + /* Prepare jump address */ + lea (start32a - start)(%rdi), %rax + movl %eax, (start32r - start)(%rdi) + + cli + + /* Setup GDT */ + lea (gdt - start)(%rdi), %rax + mov %rax, (gdtrr - start)(%rdi) + lgdt (gdtr - start)(%rdi) + + /* Jump to set %cs */ + ljmp *(start32r - start)(%rdi) + + .align 4 +start32a: + .code32 + movl $DATA_SEGMENT, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + + lea (run_i386_end - start - 0x20)(%edi), %eax + mov %eax, %esp + + /* Disable Paging in CR0 */ + movl %cr0, %eax + andl $(~CR0_PG), %eax + movl %eax, %cr0 + + /* Disable PAE in CR4 */ + movl %cr4, %eax + andl $(~CR4_PAE), %eax + movl %eax, %cr4 + + jmp start32b +start32b: + .code32 + + call *%esi + + .align 4 +start32r: + .long 0 + .long CODE_SEGMENT + .align 4 +gdt: + .long 0, 0 + .long 0, 0 + .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9f, 0xcf, 0x00 + .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x93, 0xcf, 0x00 +gdtr: + .word gdtr - gdt +gdtrr: + .quad +start32end: + /* Space for the stack */ + .align 4 + .space 8192 +run_i386_end: diff --git a/sys/arch/amd64/stand/efi32/run_i386.h b/sys/arch/amd64/stand/efi32/run_i386.h new file mode 100644 index 00000000000..f20d2d4f19a --- /dev/null +++ b/sys/arch/amd64/stand/efi32/run_i386.h @@ -0,0 +1,21 @@ +/* $OpenBSD: run_i386.h,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ + +/* + * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +extern void run_i386_start(u_long, u_long, int, int, int, int, int, int, + int, int) __attribute__ ((noreturn)); +extern u_int run_i386_size; diff --git a/sys/arch/amd64/stand/efi32/self_reloc.c b/sys/arch/amd64/stand/efi32/self_reloc.c new file mode 100644 index 00000000000..75ccf65bad6 --- /dev/null +++ b/sys/arch/amd64/stand/efi32/self_reloc.c @@ -0,0 +1,124 @@ +/* $OpenBSD: self_reloc.c,v 1.1 2019/05/11 02:33:34 mlarkin Exp $ */ +/*- + * Copyright (c) 2008-2010 Rui Paulo <rpaulo@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <machine/reloc.h> + +#if defined(__aarch64__) || defined(__amd64__) +#define ELFSIZE 64 +#define ElfW_Rel Elf64_Rela +#define ElfW_Dyn Elf64_Dyn +#define ELFW_R_TYPE ELF64_R_TYPE +#define ELF_RELA +#elif defined(__arm__) || defined(__i386__) +#define ELFSIZE 32 +#define ElfW_Rel Elf32_Rel +#define ElfW_Dyn Elf32_Dyn +#define ELFW_R_TYPE ELF32_R_TYPE +#else +#error architecture not supported +#endif + +#include <sys/exec_elf.h> + +#if defined(__aarch64__) +#define RELOC_TYPE_NONE R_AARCH64_NONE +#define RELOC_TYPE_RELATIVE R_AARCH64_RELATIVE +#elif defined(__amd64__) +#define RELOC_TYPE_NONE R_X86_64_NONE +#define RELOC_TYPE_RELATIVE R_X86_64_RELATIVE +#elif defined(__arm__) +#define RELOC_TYPE_NONE R_ARM_NONE +#define RELOC_TYPE_RELATIVE R_ARM_RELATIVE +#elif defined(__i386__) +#define RELOC_TYPE_NONE R_386_NONE +#define RELOC_TYPE_RELATIVE R_386_RELATIVE +#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/amd64/stand/efi32/start_amd64.S b/sys/arch/amd64/stand/efi32/start_amd64.S new file mode 100644 index 00000000000..171dbff3d0a --- /dev/null +++ b/sys/arch/amd64/stand/efi32/start_amd64.S @@ -0,0 +1,76 @@ +/*- + * Copyright (C) 1999 Hewlett-Packard Co. + * Contributed by David Mosberger <davidm@hpl.hp.com>. + * Copyright (C) 2005 Intel Co. + * Contributed by Fenghua Yu <fenghua.yu@intel.com>. + * 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. + * 3. Neither the name of Hewlett-Packard Co. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + */ + +/* + * crt0-efi-x86_64.S - x86_64 EFI startup code. + * $FreeBSD: head/sys/boot/efi/loader/arch/amd64/start.S 282727 2015-05-10 13:24:26Z ian $ + */ + + .text + .align 4 + + .globl _start +_start: + subq $8, %rsp + pushq %rcx + pushq %rdx + +0: + lea ImageBase(%rip), %rdi + lea _DYNAMIC(%rip), %rsi + + popq %rcx + popq %rdx + pushq %rcx + pushq %rdx + call self_reloc + + popq %rdi + popq %rsi + + call efi_main + addq $8, %rsp + +.exit: + ret + + /* + * hand-craft a dummy .reloc section so EFI knows it's a relocatable + * executable: + */ + + .data + .section .reloc, "a" + .long 0 + .long 10 + .word 0 diff --git a/sys/arch/amd64/stand/efi32/start_i386.S b/sys/arch/amd64/stand/efi32/start_i386.S new file mode 100644 index 00000000000..016ab93d15a --- /dev/null +++ b/sys/arch/amd64/stand/efi32/start_i386.S @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2008-2010 Rui Paulo <rpaulo@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/sys/boot/efi/loader/arch/i386/start.S 282728 2015-05-10 13:30:21Z ian $ + */ + + .text + +#include <machine/asm.h> + +#define EFI_SUCCESS 0 + +/* + * EFI entry point. + * _start(EFI_IMAGE image_handle, EFI_SYSTEM_TABLE *system_table); + * + * We calculate the base address along with _DYNAMIC, relocate us and finally + * pass control to efi_main. + */ + +ENTRY(_start) + pushl %ebp + movl %esp, %ebp + + pushl 12(%ebp) /* image_handle */ + pushl 8(%ebp) /* system_table */ + call 0f +0: popl %eax + movl %eax, %ebx + addl $ImageBase-0b, %eax + addl $_DYNAMIC-0b, %ebx + pushl %ebx /* dynamic */ + pushl %eax /* ImageBase */ + call self_reloc + popl %ebx /* remove ImageBase from the stack */ + popl %ebx /* remove dynamic from the stack */ + call efi_main +1: leave + ret +END(_start) + + .data + .section .reloc, "a" + .long 0 + .long 10 + .word 0 |