> Date: Fri, 9 Sep 2022 15:17:55 +0300 > From: Sergii Dmytruk <sergii.dmyt...@3mdeb.com> > > Hi Mark, > > Any news? I since setup gdb debugging of OVMF and figured out my EFI RT > issues and it returns data fine now (was wrong calling ABI), but I'm not > making /dev/efi as it sounds like you've done it already. Where are you > at with this? Can I help to move this forward?
Hi Sergii, Sorry, not much. I've been a bit busy; some evil person put a lenovo x13s on my desk ;). I dug out the patch I was working on. The goal of that patch was to support rebooting machines using EFI runtime services, which makes the Microsoft Surface Go 3 reboot correctly. Unfortunately this breaks other machines, which is why I parked the diff. The diff is basically a port of the arm64 code, and I want to make an effort to keep things in sync. My plan is to get this bit in soon after OpenBSD 7.2 is released. That'll be a few weeks from now. I may use some of the time in between to make this a bit more robust such that if we crash during an EFI runtime services call, the kernel can recover. This diff doesn't create dev/efi yet, but that will follow soon after. I did discuss the approach with Theo and some other OpenBSD developers really. I think this is very useful functionality, but there are some concerns about making it too easy to set EFI variables from within OpenBSD as this can have serious security implications. Traditionally we only allow this kind of thing when securelevel is -1 or 0. See https://man.openbsd.org/securelevel.7 for more information about securelevel. That would mean you can only run fwupd from single-user mode. Cheers, and thank you for your patience, Mark Index: arch/amd64/amd64/acpi_machdep.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/acpi_machdep.c,v retrieving revision 1.104 diff -u -p -r1.104 acpi_machdep.c --- arch/amd64/amd64/acpi_machdep.c 7 Aug 2022 23:56:06 -0000 1.104 +++ arch/amd64/amd64/acpi_machdep.c 14 Sep 2022 07:58:01 -0000 @@ -330,10 +330,14 @@ void acpi_attach_machdep(struct acpi_softc *sc) { extern void (*cpuresetfn)(void); + extern void (*powerdownfn)(void); sc->sc_interrupt = isa_intr_establish(NULL, sc->sc_fadt->sci_int, IST_LEVEL, IPL_BIO, acpi_interrupt, sc, sc->sc_dev.dv_xname); - cpuresetfn = acpi_reset; + if (!cpuresetfn) + cpuresetfn = acpi_reset; + if (!powerdownfn) + powerdownfn = acpi_powerdown; #ifndef SMALL_KERNEL /* 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 14 Sep 2022 07:58:01 -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.c =================================================================== RCS file: arch/amd64/amd64/efi.c diff -N arch/amd64/amd64/efi.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ arch/amd64/amd64/efi.c 14 Sep 2022 07:58:01 -0000 @@ -0,0 +1,315 @@ +/* $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; +extern void (*cpuresetfn)(void); +extern void (*powerdownfn)(void); + +#include <dev/acpi/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_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 *); +void efi_reset(void); +void efi_powerdown(void); + +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; + uint64_t system_table; + bus_space_handle_t memh; + EFI_SYSTEM_TABLE *st; + EFI_RUNTIME_SERVICES *rs; + uint32_t mmap_size = bios_efiinfo->mmap_size; + uint32_t mmap_desc_size = bios_efiinfo->mmap_desc_size; + uint32_t mmap_desc_ver = bios_efiinfo->mmap_desc_ver; + EFI_MEMORY_DESCRIPTOR *desc; + 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_PREFETCHABLE, &memh)) { + printf(": can't map system table\n"); + return; + } + + st = bus_space_vaddr(ba->ba_memt, memh); + 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; + + /* + * 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->PhysicalStart; + paddr_t pa = desc->PhysicalStart; + int npages = desc->NumberOfPages; + vm_prot_t prot = PROT_READ | PROT_WRITE; + +#define EFI_DEBUG +#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 + + /* + * 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); + } + + /* + * The FirmwareVendor and ConfigurationTable fields have been + * converted from a physical pointer to a virtual pointer, so + * we have to activate our pmap to access them. + */ + 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); + + if (rs == NULL) + return; + sc->sc_rs = rs; + + cpuresetfn = efi_reset; + powerdownfn = efi_powerdown; + + efi_enter(sc); + status = 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_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; +} + +void +efi_reset(void) +{ + struct efi_softc *sc = efi_cd.cd_devs[0]; + + printf("%s\n", __func__); + efi_enter(sc); + sc->sc_rs->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); + efi_leave(sc); +} + +void +efi_powerdown(void) +{ + struct efi_softc *sc = efi_cd.cd_devs[0]; + + printf("%s\n", __func__); + efi_enter(sc); + sc->sc_rs->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); + efi_leave(sc); +} 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 14 Sep 2022 07:58:01 -0000 @@ -126,6 +126,11 @@ extern int db_console; #include <dev/ic/comreg.h> #endif +#include "efi.h" +#if NEFI > 0 +#include <dev/acpi/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. */ @@ -258,6 +267,7 @@ void cpu_init_extents(void); void map_tramps(void); void init_x86_64(paddr_t); void (*cpuresetfn)(void); +void (*powerdownfn)(void); void enter_shared_special_pages(void); #ifdef APERTURE @@ -850,15 +860,13 @@ haltsys: #endif if ((howto & RB_HALT) != 0) { -#if NACPI > 0 && !defined(SMALL_KERNEL) - extern int acpi_enabled; - - if (acpi_enabled) { + if ((howto & RB_POWERDOWN) != 0) { + printf("\nAttempting to power down...\n"); delay(500000); - if ((howto & RB_POWERDOWN) != 0) - acpi_powerdown(); + if (powerdownfn) + (*powerdownfn)(); } -#endif + printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); @@ -1537,6 +1545,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 14 Sep 2022 07:58:01 -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 14 Sep 2022 07:58:01 -0000 @@ -86,6 +86,8 @@ 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 14 Sep 2022 07:58:01 -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.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 14 Sep 2022 07:58:01 -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/acpi/efi.h =================================================================== RCS file: /cvs/src/sys/dev/acpi/efi.h,v retrieving revision 1.4 diff -u -p -r1.4 efi.h --- dev/acpi/efi.h 4 Aug 2019 09:27:09 -0000 1.4 +++ dev/acpi/efi.h 14 Sep 2022 07:58:01 -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; @@ -41,6 +47,10 @@ typedef struct { { 0xf2fd1544, 0x9794, 0x4a2c, \ { 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94 } } +#define EFI_GLOBAL_VARIABLE \ + { 0x8be4df61, 0x93ca, 0x11d2, \ + { 0xaa,0x0d,0x00,0xe0,0x98,0x03,0x2b,0x8c } } + typedef enum { EfiReservedMemoryType, EfiLoaderCode, @@ -87,6 +97,13 @@ typedef struct { #define NextMemoryDescriptor(Ptr, Size) \ ((EFI_MEMORY_DESCRIPTOR *)(((UINT8 *)Ptr) + Size)) +typedef enum { + EfiResetCold, + EfiResetWarm, + EfiResetShutdown, + EfiResetPlatformSpecific +} EFI_RESET_TYPE; + typedef struct { UINT64 Signature; UINT32 Revision; @@ -111,9 +128,13 @@ 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 EFI_STATUS (EFIAPI *EFI_GET_VARIABLE)(CHAR16 *, EFI_GUID *, UINT32 *, UINTN *, VOID *); +typedef EFI_STATUS (EFIAPI *EFI_GET_NEXT_VARIABLE_NAME)(UINTN *, CHAR16 *, EFI_GUID *); +typedef EFI_STATUS (EFIAPI *EFI_SET_VARIABLE)(CHAR16 *, EFI_GUID *, UINT32, UINTN, VOID *); +typedef VOID (EFIAPI *EFI_RESET_SYSTEM)(EFI_RESET_TYPE, EFI_STATUS, UINTN, VOID *); typedef struct { EFI_TABLE_HEADER Hdr; @@ -123,6 +144,14 @@ typedef struct { VOID *SetWakeupTime; EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap; + VOID *ConvertPointer; + + EFI_GET_VARIABLE GetVariable; + EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; + EFI_SET_VARIABLE SetVariable; + + VOID *GetNextHighMonotonicCount; + EFI_RESET_SYSTEM ResetSystem; } EFI_RUNTIME_SERVICES; typedef struct {