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?


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.
+        */
+       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);
+
+       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;

Reply via email to