summaryrefslogtreecommitdiff
path: root/sys/arch/amd64
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2022-10-16 15:03:40 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2022-10-16 15:03:40 +0000
commitfe8731fb4a777bf1ea69767e96091545a8c93c4d (patch)
tree96d551099a22099ad5a12a3d8975df607153ca10 /sys/arch/amd64
parent908b2cbfaf3689db0a0ad08cacd6eea6f7f3bb30 (diff)
Add the guts for EFI runtime services support on amd64. This will be used
in the future to implement support for things like EFI variables. ok krw@ (a few others ok'ed earlier incarnations of this diff)
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r--sys/arch/amd64/amd64/bios.c15
-rw-r--r--sys/arch/amd64/amd64/efi_machdep.c301
-rw-r--r--sys/arch/amd64/amd64/machdep.c21
-rw-r--r--sys/arch/amd64/amd64/pmap.c4
-rw-r--r--sys/arch/amd64/conf/GENERIC3
-rw-r--r--sys/arch/amd64/conf/files.amd649
-rw-r--r--sys/arch/amd64/include/pmap.h5
7 files changed, 351 insertions, 7 deletions
diff --git a/sys/arch/amd64/amd64/bios.c b/sys/arch/amd64/amd64/bios.c
index 5061380f006..038bc89c71b 100644
--- a/sys/arch/amd64/amd64/bios.c
+++ b/sys/arch/amd64/amd64/bios.c
@@ -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
index 00000000000..f4e9835caf1
--- /dev/null
+++ b/sys/arch/amd64/amd64/efi_machdep.c
@@ -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;
+}
diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c
index e701b9b8173..03d11d98d20 100644
--- a/sys/arch/amd64/amd64/machdep.c
+++ b/sys/arch/amd64/amd64/machdep.c
@@ -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);
diff --git a/sys/arch/amd64/amd64/pmap.c b/sys/arch/amd64/amd64/pmap.c
index 0078b47b6e4..a932a2c9af9 100644
--- a/sys/arch/amd64/amd64/pmap.c
+++ b/sys/arch/amd64/amd64/pmap.c
@@ -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())
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index a12efcee3b1..f16e570a692 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -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
diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64
index dbf462083ad..91c5592e340 100644
--- a/sys/arch/amd64/conf/files.amd64
+++ b/sys/arch/amd64/conf/files.amd64
@@ -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
@@ -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 {}
diff --git a/sys/arch/amd64/include/pmap.h b/sys/arch/amd64/include/pmap.h
index d436258604f..e36c1f6f1c6 100644
--- a/sys/arch/amd64/include/pmap.h
+++ b/sys/arch/amd64/include/pmap.h
@@ -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 $ */
/*
@@ -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 */
@@ -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):
*/