I'm not worried until it gets exposed to userland.
Mark Kettenis <[email protected]> wrote:
> 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;
>