> From: Dave Voutila <d...@sisu.io> > Date: Mon, 10 Oct 2022 14:08:57 -0400 > > Mark Kettenis <mark.kette...@xs4all.nl> writes: > > > 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? > > Tests fine on my Go3 and X13gen1. Code looks ok to me but 2 little nits > below: > > > > > > > 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. > > + */ > > Comment above is wrong as we're not accessing ConfigurationTable > afaict.
Ah right. But on arm64 we do, and we may do the same on amd64 in the future, and this improves the diffability with the arm64 implementation. > > + 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); > > + > > One small nit...no need to efi_leave() just to efi_enter(). Similar issue here. On arm64 we have some additional code here. This improves the diffability. > > + 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; >