Add the guts for EFI runtime services support on amd64. This will be used
authorkettenis <kettenis@openbsd.org>
Sun, 16 Oct 2022 15:03:39 +0000 (15:03 +0000)
committerkettenis <kettenis@openbsd.org>
Sun, 16 Oct 2022 15:03:39 +0000 (15:03 +0000)
in the future to implement support for things like EFI variables.

ok krw@ (a few others ok'ed earlier incarnations of this diff)

sys/arch/amd64/amd64/bios.c
sys/arch/amd64/amd64/efi_machdep.c [new file with mode: 0644]
sys/arch/amd64/amd64/machdep.c
sys/arch/amd64/amd64/pmap.c
sys/arch/amd64/conf/GENERIC
sys/arch/amd64/conf/files.amd64
sys/arch/amd64/include/pmap.h
sys/dev/efi/efi.h

index 5061380..038bc89 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bios.c,v 1.45 2022/02/21 11:03:39 mpi Exp $   */
+/*     $OpenBSD: bios.c,v 1.46 2022/10/16 15:03:39 kettenis Exp $      */
 /*
  * Copyright (c) 2006 Gordon Willem Klok <gklok@cogeco.ca>
  *
@@ -30,6 +30,7 @@
 #include <amd64/include/isa_machdep.h>
 
 #include "acpi.h"
+#include "efi.h"
 #include "mpbios.h"
 #include "pci.h"
 
@@ -190,6 +191,18 @@ out:
                        }
        }
 
+#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
        {
                struct bios_attach_args ba;
diff --git a/sys/arch/amd64/amd64/efi_machdep.c b/sys/arch/amd64/amd64/efi_machdep.c
new file mode 100644 (file)
index 0000000..f4e9835
--- /dev/null
@@ -0,0 +1,301 @@
+/*     $OpenBSD: efi_machdep.c,v 1.1 2022/10/16 15:03:39 kettenis Exp $        */
+
+/*
+ * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/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 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;
+
+       /*
+        * EDK II implementations provide an implementation of
+        * GetTime() that returns a fixed compiled-in time on hardware
+        * without a (supported) RTC.  So only use this interface as a
+        * last resort.
+        */
+       sc->sc_todr.cookie = sc;
+       sc->sc_todr.todr_gettime = efi_gettime;
+       sc->sc_todr.todr_settime = efi_settime;
+       sc->sc_todr.todr_quality = -1000;
+       todr_attach(&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 e701b9b..03d11d9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: machdep.c,v 1.280 2022/08/25 17:25:25 cheloha Exp $   */
+/*     $OpenBSD: machdep.c,v 1.281 2022/10/16 15:03:39 kettenis Exp $  */
 /*     $NetBSD: machdep.c,v 1.3 2003/05/07 22:58:18 fvdl Exp $ */
 
 /*-
@@ -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.
  */
@@ -1538,6 +1547,16 @@ init_x86_64(paddr_t first_avail)
         */
        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)
                avail_start = pmap_prealloc_lowmem_ptps(avail_start);
index 0078b47..a932a2c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pmap.c,v 1.154 2022/09/10 20:35:28 miod Exp $ */
+/*     $OpenBSD: pmap.c,v 1.155 2022/10/16 15:03:39 kettenis Exp $     */
 /*     $NetBSD: pmap.c,v 1.3 2003/05/08 18:13:13 thorpej Exp $ */
 
 /*
@@ -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 a12efce..f16e570 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.512 2022/03/08 15:08:01 hastings Exp $
+#      $OpenBSD: GENERIC,v 1.513 2022/10/16 15:03:39 kettenis Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -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 dbf4620..91c5592 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.amd64,v 1.105 2022/02/09 23:54:34 deraadt Exp $
+#      $OpenBSD: files.amd64,v 1.106 2022/10/16 15:03:39 kettenis Exp $
 
 maxpartitions 16
 maxusers 2 16 128
@@ -242,6 +242,13 @@ device     acpipci
 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
 #
index d436258..e36c1f6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pmap.h,v 1.81 2022/08/29 02:58:13 jsg Exp $   */
+/*     $OpenBSD: pmap.h,v 1.82 2022/10/16 15:03:39 kettenis Exp $      */
 /*     $NetBSD: pmap.h,v 1.1 2003/04/26 18:39:46 fvdl Exp $    */
 
 /*
 #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 */
 
@@ -317,6 +318,8 @@ struct pmap {
        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 3fa5b10..a471333 100644 (file)
@@ -1,10 +1,16 @@
-/* $OpenBSD: efi.h,v 1.1 2022/10/03 19:32:22 kettenis Exp $ */
+/* $OpenBSD: efi.h,v 1.2 2022/10/16 15:03:39 kettenis Exp $ */
 
 /* Public Domain */
 
 #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;