I'm not worried until it gets exposed to userland. Mark Kettenis <mark.kette...@xs4all.nl> 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 <kette...@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 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; >