> 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;
> 

Reply via email to