Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys efi(4): Implement MI parts of EFIIOC_GET_TABLE.



details:   https://anonhg.NetBSD.org/src/rev/a23727f1fb95
branches:  trunk
changeset: 375943:a23727f1fb95
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Mon May 22 16:27:58 2023 +0000

description:
efi(4): Implement MI parts of EFIIOC_GET_TABLE.

Intended to be compatible with FreeBSD.

Not yet supported on any architectures.

PR kern/57076

XXX pullup-10

diffstat:

 sys/dev/efi.c    |  204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 sys/dev/efivar.h |   10 +-
 sys/sys/efiio.h  |   10 ++-
 3 files changed, 218 insertions(+), 6 deletions(-)

diffs (truncated from 303 to 300 lines):

diff -r 43fdfa005d42 -r a23727f1fb95 sys/dev/efi.c
--- a/sys/dev/efi.c     Mon May 22 16:27:48 2023 +0000
+++ b/sys/dev/efi.c     Mon May 22 16:27:58 2023 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: efi.c,v 1.6 2023/05/22 16:27:49 riastradh Exp $ */
+/* $NetBSD: efi.c,v 1.7 2023/05/22 16:27:58 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2021 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.6 2023/05/22 16:27:49 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.7 2023/05/22 16:27:58 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -40,7 +40,10 @@
 #include <sys/atomic.h>
 #include <sys/efiio.h>
 
+#include <uvm/uvm_extern.h>
+
 #include <dev/efivar.h>
+#include <dev/mm.h>
 
 #include "ioconf.h"
 
@@ -133,6 +136,201 @@ efi_status_to_error(efi_status status)
        }
 }
 
+/* XXX move to efi.h */
+#define        EFI_SYSTEM_RESOURCE_TABLE_GUID                                        \
+       {0xb122a263,0x3661,0x4f68,0x99,0x29,{0x78,0xf8,0xb0,0xd6,0x21,0x80}}
+#define        EFI_PROPERTIES_TABLE                                                  \
+       {0x880aaca3,0x4adc,0x4a04,0x90,0x79,{0xb7,0x47,0x34,0x08,0x25,0xe5}}
+
+#define        EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION     1
+
+struct EFI_SYSTEM_RESOURCE_ENTRY {
+       struct uuid     FwClass;
+       uint32_t        FwType;
+       uint32_t        FwVersion;
+       uint32_t        LowestSupportedFwVersion;
+       uint32_t        CapsuleFlags;
+       uint32_t        LastAttemptVersion;
+       uint32_t        LastAttemptStatus;
+};
+
+struct EFI_SYSTEM_RESOURCE_TABLE {
+       uint32_t        FwResourceCount;
+       uint32_t        FwResourceCountMax;
+       uint64_t        FwResourceVersion;
+       struct EFI_SYSTEM_RESOURCE_ENTRY        Entries[];
+};
+
+static void *
+efi_map_pa(uint64_t addr, bool *directp)
+{
+       paddr_t pa = addr;
+       vaddr_t va;
+
+       /*
+        * Verify the address is not truncated by conversion to
+        * paddr_t.  This might happen with a 64-bit EFI booting a
+        * 32-bit OS.
+        */
+       if (pa != addr)
+               return NULL;
+
+       /*
+        * Try direct-map if we have it.  If it works, note that it was
+        * direct-mapped for efi_unmap.
+        */
+#ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS
+       if (mm_md_direct_mapped_phys(pa, &va)) {
+               *directp = true;
+               return (void *)va;
+       }
+#endif
+
+       /*
+        * No direct map.  Reserve a page of kernel virtual address
+        * space, with no backing, to map to the physical address.
+        */
+       va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
+           UVM_KMF_VAONLY|UVM_KMF_WAITVA);
+       KASSERT(va != 0);
+
+       /*
+        * Map the kva page to the physical address and update the
+        * kernel pmap so we can use it.
+        */
+       pmap_kenter_pa(va, pa, VM_PROT_READ, 0);
+       pmap_update(pmap_kernel());
+
+       /*
+        * Success!  Return the VA and note that it was not
+        * direct-mapped for efi_unmap.
+        */
+       *directp = false;
+       return (void *)va;
+}
+
+static void
+efi_unmap(void *ptr, bool direct)
+{
+       vaddr_t va = (vaddr_t)ptr;
+
+       /*
+        * If it was direct-mapped, nothing to do here.
+        */
+       if (direct)
+               return;
+
+       /*
+        * First remove the mapping from the kernel pmap so that it can
+        * be reused, before we free the kva and let anyone else reuse
+        * it.
+        */
+       pmap_kremove(va, PAGE_SIZE);
+       pmap_update(pmap_kernel());
+
+       /*
+        * Next free the kva so it can be reused by someone else.
+        */
+       uvm_km_free(kernel_map, va, PAGE_SIZE, UVM_KMF_VAONLY);
+}
+
+static int
+efi_ioctl_got_table(struct efi_get_table_ioc *ioc, void *ptr, size_t len)
+{
+
+       /*
+        * Return the actual table length.
+        */
+       ioc->table_len = len;
+
+       /*
+        * Copy out as much as we can into the user's allocated buffer.
+        */
+       return copyout(ptr, ioc->buf, MIN(ioc->buf_len, len));
+}
+
+static int
+efi_ioctl_get_esrt(struct efi_get_table_ioc *ioc,
+    struct EFI_SYSTEM_RESOURCE_TABLE *tab)
+{
+
+       /*
+        * Verify the firmware resource version is one we understand.
+        */
+       if (tab->FwResourceVersion !=
+           EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION)
+               return ENOENT;
+
+       /*
+        * Verify the resource count fits within the single page we
+        * have mapped.
+        *
+        * XXX What happens if it doesn't?  Are we expected to map more
+        * than one page, according to the table header?  The UEFI spec
+        * is unclear on this.
+        */
+       const size_t entry_space = PAGE_SIZE -
+           offsetof(struct EFI_SYSTEM_RESOURCE_TABLE, Entries);
+       if (tab->FwResourceCount > entry_space/sizeof(tab->Entries[0]))
+               return ENOENT;
+
+       /*
+        * Success!  Return everything through the last table entry.
+        */
+       const size_t len = offsetof(struct EFI_SYSTEM_RESOURCE_TABLE,
+           Entries[tab->FwResourceCount]);
+       return efi_ioctl_got_table(ioc, tab, len);
+}
+
+static int
+efi_ioctl_get_table(struct efi_get_table_ioc *ioc)
+{
+       uint64_t addr;
+       bool direct;
+       efi_status status;
+       int error;
+
+       /*
+        * If the platform doesn't support it yet, fail now.
+        */
+       if (efi_ops->efi_gettab == NULL)
+               return ENODEV;
+
+       /*
+        * Get the address of the requested table out of the EFI
+        * configuration table.
+        */
+       status = efi_ops->efi_gettab(&ioc->uuid, &addr);
+       if (status != EFI_SUCCESS)
+               return efi_status_to_error(status);
+
+       /*
+        * UEFI provides no generic way to identify the size of the
+        * table, so we have to bake knowledge of every vendor GUID
+        * into this code to safely expose the right amount of data to
+        * userland.
+        *
+        * We even have to bake knowledge of which ones are physically
+        * addressed and which ones might be virtually addressed
+        * according to the vendor GUID into this code, although for
+        * the moment we never use RT->SetVirtualAddressMap so we only
+        * ever have to deal with physical addressing.
+        */
+       if (memcmp(&ioc->uuid, &(struct uuid)EFI_SYSTEM_RESOURCE_TABLE_GUID,
+               sizeof(ioc->uuid)) == 0) {
+               struct EFI_SYSTEM_RESOURCE_TABLE *tab;
+
+               if ((tab = efi_map_pa(addr, &direct)) == NULL)
+                       return ENOENT;
+               error = efi_ioctl_get_esrt(ioc, tab);
+               efi_unmap(tab, direct);
+       } else {
+               error = ENOENT;
+       }
+
+       return error;
+}
+
 static int
 efi_ioctl_var_get(struct efi_var_ioc *var)
 {
@@ -273,6 +471,8 @@ efi_ioctl(dev_t dev, u_long cmd, void *d
        KASSERT(efi_ops != NULL);
 
        switch (cmd) {
+       case EFIIOC_GET_TABLE:
+               return efi_ioctl_get_table(data);
        case EFIIOC_VAR_GET:
                return efi_ioctl_var_get(data);
        case EFIIOC_VAR_NEXT:
diff -r 43fdfa005d42 -r a23727f1fb95 sys/dev/efivar.h
--- a/sys/dev/efivar.h  Mon May 22 16:27:48 2023 +0000
+++ b/sys/dev/efivar.h  Mon May 22 16:27:58 2023 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: efivar.h,v 1.1 2021/10/10 13:03:09 jmcneill Exp $ */
+/* $NetBSD: efivar.h,v 1.2 2023/05/22 16:27:58 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2021 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -29,16 +29,20 @@
 #ifndef _DEV_EFIVAR_H
 #define _DEV_EFIVAR_H
 
+#include <sys/uuid.h>
+#include <sys/types.h>
+
 #include <machine/efi.h>
 
 struct efi_ops {
        efi_status      (*efi_gettime)(struct efi_tm *, struct efi_tmcap *);
        efi_status      (*efi_settime)(struct efi_tm *);
        efi_status      (*efi_getvar)(uint16_t *, struct uuid *, uint32_t *,
-                                     u_long *, void *);
+                           u_long *, void *);
        efi_status      (*efi_setvar)(uint16_t *, struct uuid *, uint32_t,
-                                     u_long, void *);
+                           u_long, void *);
        efi_status      (*efi_nextvar)(u_long *, uint16_t *, struct uuid *);
+       efi_status      (*efi_gettab)(const struct uuid *, uint64_t *);
 };
 
 void   efi_register_ops(const struct efi_ops *);
diff -r 43fdfa005d42 -r a23727f1fb95 sys/sys/efiio.h
--- a/sys/sys/efiio.h   Mon May 22 16:27:48 2023 +0000
+++ b/sys/sys/efiio.h   Mon May 22 16:27:58 2023 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: efiio.h,v 1.2 2021/10/11 10:23:02 jmcneill Exp $ */
+/* $NetBSD: efiio.h,v 1.3 2023/05/22 16:27:58 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -48,6 +48,13 @@
 #define        EFI_VARIABLE_APPEND_WRITE                               0x00000040
 #define        EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS              0x00000080
 
+struct efi_get_table_ioc {
+       void *          buf;
+       struct uuid     uuid;
+       size_t          table_len;
+       size_t          buf_len;
+};
+
 struct efi_var_ioc {
        uint16_t *      name;           /* vendor's variable name */
        size_t          namesize;       /* size in bytes of the name buffer */
@@ -57,6 +64,7 @@ struct efi_var_ioc {
        size_t          datasize;       /* size in bytes of the data buffer */
 };
 
+#define        EFIIOC_GET_TABLE        _IOWR('e', 1, struct efi_get_table_ioc)



Home | Main Index | Thread Index | Old Index