> From: Dave Voutila <[email protected]>
> Date: Mon, 10 Oct 2022 14:08:57 -0400
>
> Mark Kettenis <[email protected]> writes:
>
> > Here is a diff that implements EFI runtime services support on amd64
> > in much the same way as we already do on arm64. This will be used in
> > the future to implement support for EFI variables.
> >
> > Some initial testing among OpenBSD developers did not uncover any
> > issues. So I'd like to move ahead with this. If this ends up
> > breaking machines we can always disable the
> >
> > ok?
>
> Tests fine on my Go3 and X13gen1. Code looks ok to me but 2 little nits
> below:
>
> >
> >
> > Index: arch/amd64/amd64/bios.c
> > ===================================================================
> > RCS file: /cvs/src/sys/arch/amd64/amd64/bios.c,v
> > retrieving revision 1.45
> > diff -u -p -r1.45 bios.c
> > --- arch/amd64/amd64/bios.c 21 Feb 2022 11:03:39 -0000 1.45
> > +++ arch/amd64/amd64/bios.c 8 Oct 2022 16:07:02 -0000
> > @@ -30,6 +30,7 @@
> > #include <amd64/include/isa_machdep.h>
> >
> > #include "acpi.h"
> > +#include "efi.h"
> > #include "mpbios.h"
> > #include "pci.h"
> >
> > @@ -189,6 +190,18 @@ out:
> > break;
> > }
> > }
> > +
> > +#if NEFI > 0
> > + if (bios_efiinfo != NULL) {
> > + struct bios_attach_args ba;
> > +
> > + memset(&ba, 0, sizeof(ba));
> > + ba.ba_name = "efi";
> > + ba.ba_memt = X86_BUS_SPACE_MEM;
> > +
> > + config_found(self, &ba, bios_print);
> > + }
> > +#endif
> >
> > #if NACPI > 0
> > {
> > Index: arch/amd64/amd64/efi_machdep.c
> > ===================================================================
> > RCS file: arch/amd64/amd64/efi_machdep.c
> > diff -N arch/amd64/amd64/efi_machdep.c
> > --- /dev/null 1 Jan 1970 00:00:00 -0000
> > +++ arch/amd64/amd64/efi_machdep.c 8 Oct 2022 16:07:02 -0000
> > @@ -0,0 +1,296 @@
> > +/* $OpenBSD$ */
> > +
> > +/*
> > + * Copyright (c) 2022 Mark Kettenis <[email protected]>
> > + *
> > + * Permission to use, copy, modify, and distribute this software for any
> > + * purpose with or without fee is hereby granted, provided that the above
> > + * copyright notice and this permission notice appear in all copies.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> > + */
> > +
> > +#include <sys/param.h>
> > +#include <sys/device.h>
> > +#include <sys/proc.h>
> > +#include <sys/systm.h>
> > +#include <sys/user.h>
> > +
> > +#include <uvm/uvm_extern.h>
> > +
> > +#include <machine/biosvar.h>
> > +extern paddr_t cr3_reuse_pcid;
> > +
> > +#include <dev/efi/efi.h>
> > +
> > +#include <dev/clock_subr.h>
> > +
> > +extern todr_chip_handle_t todr_handle;
> > +
> > +extern EFI_MEMORY_DESCRIPTOR *mmap;
> > +
> > +struct efi_softc {
> > + struct device sc_dev;
> > + struct pmap *sc_pm;
> > + EFI_RUNTIME_SERVICES *sc_rs;
> > + u_long sc_psw;
> > + uint64_t sc_cr3;
> > +
> > + struct todr_chip_handle sc_todr;
> > +};
> > +
> > +int efi_match(struct device *, void *, void *);
> > +void efi_attach(struct device *, struct device *, void *);
> > +
> > +const struct cfattach efi_ca = {
> > + sizeof(struct efi_softc), efi_match, efi_attach
> > +};
> > +
> > +struct cfdriver efi_cd = {
> > + NULL, "efi", DV_DULL
> > +};
> > +
> > +void efi_map_runtime(struct efi_softc *);
> > +void efi_enter(struct efi_softc *);
> > +void efi_leave(struct efi_softc *);
> > +int efi_gettime(struct todr_chip_handle *, struct timeval *);
> > +int efi_settime(struct todr_chip_handle *, struct timeval *);
> > +
> > +int
> > +efi_match(struct device *parent, void *match, void *aux)
> > +{
> > + struct bios_attach_args *ba = aux;
> > + struct cfdata *cf = match;
> > +
> > + if (strcmp(ba->ba_name, cf->cf_driver->cd_name) == 0 &&
> > + bios_efiinfo->system_table != 0)
> > + return 1;
> > +
> > + return 0;
> > +}
> > +
> > +void
> > +efi_attach(struct device *parent, struct device *self, void *aux)
> > +{
> > + struct efi_softc *sc = (struct efi_softc *)self;
> > + struct bios_attach_args *ba = aux;
> > + uint32_t mmap_desc_ver = bios_efiinfo->mmap_desc_ver;
> > + uint64_t system_table;
> > + bus_space_handle_t memh;
> > + EFI_SYSTEM_TABLE *st;
> > + EFI_TIME time;
> > + EFI_STATUS status;
> > + uint16_t major, minor;
> > + int i;
> > +
> > + if (mmap_desc_ver != EFI_MEMORY_DESCRIPTOR_VERSION) {
> > + printf(": unsupported memory descriptor version %d\n",
> > + mmap_desc_ver);
> > + return;
> > + }
> > +
> > + system_table = bios_efiinfo->system_table;
> > + KASSERT(system_table);
> > +
> > + if (bus_space_map(ba->ba_memt, system_table, sizeof(EFI_SYSTEM_TABLE),
> > + BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_CACHEABLE, &memh)) {
> > + printf(": can't map system table\n");
> > + return;
> > + }
> > +
> > + st = bus_space_vaddr(ba->ba_memt, memh);
> > + sc->sc_rs = st->RuntimeServices;
> > +
> > + major = st->Hdr.Revision >> 16;
> > + minor = st->Hdr.Revision & 0xffff;
> > + printf(": UEFI %d.%d", major, minor / 10);
> > + if (minor % 10)
> > + printf(".%d", minor % 10);
> > + printf("\n");
> > +
> > + if ((bios_efiinfo->flags & BEI_64BIT) == 0)
> > + return;
> > +
> > + efi_map_runtime(sc);
> > +
> > + /*
> > + * Activate our pmap such that we can access the
> > + * FirmwareVendor and ConfigurationTable fields.
> > + */
>
> Comment above is wrong as we're not accessing ConfigurationTable
> afaict.
Ah right. But on arm64 we do, and we may do the same on amd64 in the
future, and this improves the diffability with the arm64 implementation.
> > + efi_enter(sc);
> > + if (st->FirmwareVendor) {
> > + printf("%s: ", sc->sc_dev.dv_xname);
> > + for (i = 0; st->FirmwareVendor[i]; i++)
> > + printf("%c", st->FirmwareVendor[i]);
> > + printf(" rev 0x%x\n", st->FirmwareRevision);
> > + }
> > + efi_leave(sc);
> > +
>
> One small nit...no need to efi_leave() just to efi_enter().
Similar issue here. On arm64 we have some additional code here. This
improves the diffability.
> > + efi_enter(sc);
> > + status = sc->sc_rs->GetTime(&time, NULL);
> > + efi_leave(sc);
> > + if (status != EFI_SUCCESS)
> > + return;
> > +
> > + sc->sc_todr.cookie = sc;
> > + sc->sc_todr.todr_gettime = efi_gettime;
> > + sc->sc_todr.todr_settime = efi_settime;
> > + todr_handle = &sc->sc_todr;
> > +}
> > +
> > +void
> > +efi_map_runtime(struct efi_softc *sc)
> > +{
> > + uint32_t mmap_size = bios_efiinfo->mmap_size;
> > + uint32_t mmap_desc_size = bios_efiinfo->mmap_desc_size;
> > + EFI_MEMORY_DESCRIPTOR *desc;
> > + int i;
> > +
> > + /*
> > + * We don't really want some random executable non-OpenBSD
> > + * code lying around in kernel space. So create a separate
> > + * pmap and only activate it when we call runtime services.
> > + */
> > + sc->sc_pm = pmap_create();
> > +
> > + desc = mmap;
> > + for (i = 0; i < mmap_size / mmap_desc_size; i++) {
> > + if (desc->Attribute & EFI_MEMORY_RUNTIME) {
> > + vaddr_t va = desc->VirtualStart;
> > + paddr_t pa = desc->PhysicalStart;
> > + int npages = desc->NumberOfPages;
> > + vm_prot_t prot = PROT_READ | PROT_WRITE;
> > +
> > +#ifdef EFI_DEBUG
> > + printf("type 0x%x pa 0x%llx va 0x%llx pages 0x%llx attr
> > 0x%llx\n",
> > + desc->Type, desc->PhysicalStart,
> > + desc->VirtualStart, desc->NumberOfPages,
> > + desc->Attribute);
> > +#endif
> > +
> > + /*
> > + * If the virtual address is still zero, use
> > + * an identity mapping.
> > + */
> > + if (va == 0)
> > + va = pa;
> > +
> > + /*
> > + * Normal memory is expected to be "write
> > + * back" cacheable. Everything else is mapped
> > + * as device memory.
> > + */
> > + if ((desc->Attribute & EFI_MEMORY_WB) == 0)
> > + pa |= PMAP_NOCACHE;
> > +
> > + /*
> > + * Only make pages marked as runtime service code
> > + * executable. This violates the standard but it
> > + * seems we can get away with it.
> > + */
> > + if (desc->Type == EfiRuntimeServicesCode)
> > + prot |= PROT_EXEC;
> > +
> > + if (desc->Attribute & EFI_MEMORY_RP)
> > + prot &= ~PROT_READ;
> > + if (desc->Attribute & EFI_MEMORY_XP)
> > + prot &= ~PROT_EXEC;
> > + if (desc->Attribute & EFI_MEMORY_RO)
> > + prot &= ~PROT_WRITE;
> > +
> > + while (npages--) {
> > + pmap_enter(sc->sc_pm, va, pa, prot,
> > + prot | PMAP_WIRED | PMAP_EFI);
> > + va += PAGE_SIZE;
> > + pa += PAGE_SIZE;
> > + }
> > + }
> > +
> > + desc = NextMemoryDescriptor(desc, mmap_desc_size);
> > + }
> > +}
> > +
> > +void
> > +efi_enter(struct efi_softc *sc)
> > +{
> > + sc->sc_psw = intr_disable();
> > + sc->sc_cr3 = rcr3() | cr3_reuse_pcid;
> > + lcr3(sc->sc_pm->pm_pdirpa | (pmap_use_pcid ? PCID_EFI : 0));
> > +
> > + fpu_kernel_enter();
> > +}
> > +
> > +void
> > +efi_leave(struct efi_softc *sc)
> > +{
> > + fpu_kernel_exit();
> > +
> > + lcr3(sc->sc_cr3);
> > + intr_restore(sc->sc_psw);
> > +}
> > +
> > +int
> > +efi_gettime(struct todr_chip_handle *handle, struct timeval *tv)
> > +{
> > + struct efi_softc *sc = handle->cookie;
> > + struct clock_ymdhms dt;
> > + EFI_TIME time;
> > + EFI_STATUS status;
> > +
> > + efi_enter(sc);
> > + status = sc->sc_rs->GetTime(&time, NULL);
> > + efi_leave(sc);
> > + if (status != EFI_SUCCESS)
> > + return EIO;
> > +
> > + dt.dt_year = time.Year;
> > + dt.dt_mon = time.Month;
> > + dt.dt_day = time.Day;
> > + dt.dt_hour = time.Hour;
> > + dt.dt_min = time.Minute;
> > + dt.dt_sec = time.Second;
> > +
> > + if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
> > + dt.dt_day > 31 || dt.dt_day == 0 ||
> > + dt.dt_mon > 12 || dt.dt_mon == 0 ||
> > + dt.dt_year < POSIX_BASE_YEAR)
> > + return EINVAL;
> > +
> > + tv->tv_sec = clock_ymdhms_to_secs(&dt);
> > + tv->tv_usec = 0;
> > + return 0;
> > +}
> > +
> > +int
> > +efi_settime(struct todr_chip_handle *handle, struct timeval *tv)
> > +{
> > + struct efi_softc *sc = handle->cookie;
> > + struct clock_ymdhms dt;
> > + EFI_TIME time;
> > + EFI_STATUS status;
> > +
> > + clock_secs_to_ymdhms(tv->tv_sec, &dt);
> > +
> > + time.Year = dt.dt_year;
> > + time.Month = dt.dt_mon;
> > + time.Day = dt.dt_day;
> > + time.Hour = dt.dt_hour;
> > + time.Minute = dt.dt_min;
> > + time.Second = dt.dt_sec;
> > + time.Nanosecond = 0;
> > + time.TimeZone = 0;
> > + time.Daylight = 0;
> > +
> > + efi_enter(sc);
> > + status = sc->sc_rs->SetTime(&time);
> > + efi_leave(sc);
> > + if (status != EFI_SUCCESS)
> > + return EIO;
> > + return 0;
> > +}
> > Index: arch/amd64/amd64/machdep.c
> > ===================================================================
> > RCS file: /cvs/src/sys/arch/amd64/amd64/machdep.c,v
> > retrieving revision 1.280
> > diff -u -p -r1.280 machdep.c
> > --- arch/amd64/amd64/machdep.c 25 Aug 2022 17:25:25 -0000 1.280
> > +++ arch/amd64/amd64/machdep.c 8 Oct 2022 16:07:02 -0000
> > @@ -126,6 +126,11 @@ extern int db_console;
> > #include <dev/ic/comreg.h>
> > #endif
> >
> > +#include "efi.h"
> > +#if NEFI > 0
> > +#include <dev/efi/efi.h>
> > +#endif
> > +
> > #include "softraid.h"
> > #if NSOFTRAID > 0
> > #include <dev/softraidvar.h>
> > @@ -244,6 +249,10 @@ u_int32_t bios_cksumlen;
> > bios_efiinfo_t *bios_efiinfo;
> > bios_ucode_t *bios_ucode;
> >
> > +#if NEFI > 0
> > +EFI_MEMORY_DESCRIPTOR *mmap;
> > +#endif
> > +
> > /*
> > * Size of memory segments, before any memory is stolen.
> > */
> > @@ -1537,6 +1546,16 @@ init_x86_64(paddr_t first_avail)
> > * We must do this before loading pages into the VM system.
> > */
> > first_avail = pmap_bootstrap(first_avail, trunc_page(avail_end));
> > +
> > +#if NEFI > 0
> > + /* Relocate the EFI memory map. */
> > + if (bios_efiinfo && bios_efiinfo->mmap_start) {
> > + mmap = (EFI_MEMORY_DESCRIPTOR *)PMAP_DIRECT_MAP(first_avail);
> > + memcpy(mmap, (void *)PMAP_DIRECT_MAP(bios_efiinfo->mmap_start),
> > + bios_efiinfo->mmap_size);
> > + first_avail += round_page(bios_efiinfo->mmap_size);
> > + }
> > +#endif
> >
> > /* Allocate these out of the 640KB base memory */
> > if (avail_start != PAGE_SIZE)
> > Index: arch/amd64/amd64/pmap.c
> > ===================================================================
> > RCS file: /cvs/src/sys/arch/amd64/amd64/pmap.c,v
> > retrieving revision 1.154
> > diff -u -p -r1.154 pmap.c
> > --- arch/amd64/amd64/pmap.c 10 Sep 2022 20:35:28 -0000 1.154
> > +++ arch/amd64/amd64/pmap.c 8 Oct 2022 16:07:02 -0000
> > @@ -2834,7 +2834,7 @@ enter_now:
> > if (nocache)
> > npte |= PG_N;
> > if (va < VM_MAXUSER_ADDRESS)
> > - npte |= PG_u;
> > + npte |= ((flags & PMAP_EFI) ? 0 : PG_u);
> > else if (va < VM_MAX_ADDRESS)
> > npte |= (PG_u | PG_RW); /* XXXCDC: no longer needed? */
> > if (pmap == pmap_kernel())
> > Index: arch/amd64/conf/GENERIC
> > ===================================================================
> > RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v
> > retrieving revision 1.512
> > diff -u -p -r1.512 GENERIC
> > --- arch/amd64/conf/GENERIC 8 Mar 2022 15:08:01 -0000 1.512
> > +++ arch/amd64/conf/GENERIC 8 Oct 2022 16:07:02 -0000
> > @@ -86,6 +86,7 @@ ipmi0 at acpi? disable
> > ccpmic* at iic?
> > tipmic* at iic?
> >
> > +efi0 at bios0
> > mpbios0 at bios0
> >
> > ipmi0 at mainbus? disable # IPMI
> > Index: arch/amd64/conf/files.amd64
> > ===================================================================
> > RCS file: /cvs/src/sys/arch/amd64/conf/files.amd64,v
> > retrieving revision 1.105
> > diff -u -p -r1.105 files.amd64
> > --- arch/amd64/conf/files.amd64 9 Feb 2022 23:54:34 -0000 1.105
> > +++ arch/amd64/conf/files.amd64 8 Oct 2022 16:07:02 -0000
> > @@ -243,6 +243,13 @@ attach acpipci at acpi
> > file arch/amd64/pci/acpipci.c acpipci
> >
> > #
> > +# EFI
> > +#
> > +device efi
> > +attach efi at bios
> > +file arch/amd64/amd64/efi_machdep.c efi needs-flag
> > +
> > +#
> > # VMM
> > #
> > device vmm {}
> > Index: arch/amd64/include/pmap.h
> > ===================================================================
> > RCS file: /cvs/src/sys/arch/amd64/include/pmap.h,v
> > retrieving revision 1.81
> > diff -u -p -r1.81 pmap.h
> > --- arch/amd64/include/pmap.h 29 Aug 2022 02:58:13 -0000 1.81
> > +++ arch/amd64/include/pmap.h 8 Oct 2022 16:07:02 -0000
> > @@ -256,6 +256,7 @@
> > #define PCID_PROC 1 /* non-pmap_kernel(), U+K */
> > #define PCID_PROC_INTEL 2 /* non-pmap_kernel(), U-K (meltdown) */
> > #define PCID_TEMP 3 /* temp mapping of another non-pmap_kernel() */
> > +#define PCID_EFI 4 /* EFI runtime services */
> >
> > extern int pmap_use_pcid; /* non-zero if PCID support is enabled */
> >
> > @@ -316,6 +317,8 @@ struct pmap {
> > int pm_type; /* Type of pmap this is (PMAP_TYPE_x) */
> > uint64_t eptp; /* cached EPTP (used by vmm) */
> > };
> > +
> > +#define PMAP_EFI PMAP_MD0
> >
> > /*
> > * MD flags that we use for pmap_enter (in the pa):
> > Index: dev/efi/efi.h
> > ===================================================================
> > RCS file: /cvs/src/sys/dev/efi/efi.h,v
> > retrieving revision 1.1
> > diff -u -p -r1.1 efi.h
> > --- dev/efi/efi.h 3 Oct 2022 19:32:22 -0000 1.1
> > +++ dev/efi/efi.h 8 Oct 2022 16:07:03 -0000
> > @@ -5,6 +5,12 @@
> > #ifndef _MACHINE_EFI_H_
> > #define _MACHINE_EFI_H_
> >
> > +#ifdef __amd64__
> > +#define EFIAPI __attribute__((ms_abi))
> > +#else
> > +#define EFIAPI
> > +#endif
> > +
> > typedef uint8_t UINT8;
> > typedef int16_t INT16;
> > typedef uint16_t UINT16;
> > @@ -111,9 +117,9 @@ typedef struct {
> >
> > typedef VOID *EFI_TIME_CAPABILITIES;
> >
> > -typedef EFI_STATUS (*EFI_GET_TIME)(EFI_TIME *, EFI_TIME_CAPABILITIES *);
> > -typedef EFI_STATUS (*EFI_SET_TIME)(EFI_TIME *);
> > -typedef EFI_STATUS (*EFI_SET_VIRTUAL_ADDRESS_MAP)(UINTN, UINTN, UINT32,
> > EFI_MEMORY_DESCRIPTOR *);
> > +typedef EFI_STATUS (EFIAPI *EFI_GET_TIME)(EFI_TIME *,
> > EFI_TIME_CAPABILITIES *);
> > +typedef EFI_STATUS (EFIAPI *EFI_SET_TIME)(EFI_TIME *);
> > +typedef EFI_STATUS (EFIAPI *EFI_SET_VIRTUAL_ADDRESS_MAP)(UINTN, UINTN,
> > UINT32, EFI_MEMORY_DESCRIPTOR *);
> >
> > typedef struct {
> > EFI_TABLE_HEADER Hdr;
>