Module Name: src Committed By: martin Date: Tue Aug 1 16:05:12 UTC 2023
Modified Files: src/sys/arch/arm/arm [netbsd-10]: efi_runtime.c src/sys/arch/x86/x86 [netbsd-10]: efi_machdep.c src/sys/dev [netbsd-10]: efi.c efivar.h src/sys/dev/efi [netbsd-10]: efi.h src/sys/sys [netbsd-10]: efiio.h Log Message: Pull up following revision(s) (requested by riastradh in ticket #292): sys/arch/arm/arm/efi_runtime.c: revision 1.11 sys/dev/efi/efi.h: revision 1.3 sys/arch/x86/x86/efi_machdep.c: revision 1.5 sys/arch/x86/x86/efi_machdep.c: revision 1.6 sys/dev/efi.c: revision 1.5 sys/dev/efi.c: revision 1.6 sys/dev/efi.c: revision 1.7 sys/dev/efi.c: revision 1.8 sys/dev/efi.c: revision 1.9 sys/dev/efivar.h: revision 1.2 sys/sys/efiio.h: revision 1.3 efi(4): Parenthesize EFIERR argument out of paranoia. PR kern/57076 efi(4): Move error macros to efi.h. PR kern/57076 efi(4): Implement MI parts of EFIIOC_GET_TABLE. Intended to be compatible with FreeBSD. Not yet supported on any architectures. PR kern/57076 efi(4): Implement EFIIOC_GET_TABLE on x86. PR kern/57076 efi(4): Translate between size_t and unsigned long. Fixes i386 build. PR kern/57076 efi(4): Fix logic to handle buffer sizing. Can't KASSERT(datasize <= databufsize) because the caller is allowed to pass in a too-small size and get ERR_BUFFER_TOO_SMALL back, with the actual size returned so it can resize its buffer. So just clamp the size to the smaller of what the caller provided and what the firwmare provided, instead of asserting anything. PR kern/57076 To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.10.2.1 src/sys/arch/arm/arm/efi_runtime.c cvs rdiff -u -r1.3 -r1.3.4.1 src/sys/arch/x86/x86/efi_machdep.c cvs rdiff -u -r1.4 -r1.4.4.1 src/sys/dev/efi.c cvs rdiff -u -r1.1 -r1.1.4.1 src/sys/dev/efivar.h cvs rdiff -u -r1.2 -r1.2.4.1 src/sys/dev/efi/efi.h cvs rdiff -u -r1.2 -r1.2.4.1 src/sys/sys/efiio.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/arm/efi_runtime.c diff -u src/sys/arch/arm/arm/efi_runtime.c:1.10 src/sys/arch/arm/arm/efi_runtime.c:1.10.2.1 --- src/sys/arch/arm/arm/efi_runtime.c:1.10 Sat Oct 15 11:19:23 2022 +++ src/sys/arch/arm/arm/efi_runtime.c Tue Aug 1 16:05:11 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efi_runtime.c,v 1.10 2022/10/15 11:19:23 jmcneill Exp $ */ +/* $NetBSD: efi_runtime.c,v 1.10.2.1 2023/08/01 16:05:11 martin Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #include "efi.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: efi_runtime.c,v 1.10 2022/10/15 11:19:23 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: efi_runtime.c,v 1.10.2.1 2023/08/01 16:05:11 martin Exp $"); #include <sys/param.h> #include <sys/mutex.h> @@ -45,15 +45,6 @@ __KERNEL_RCSID(0, "$NetBSD: efi_runtime. #include <arm/arm/efi_runtime.h> #include <arm/bootconfig.h> -#ifdef _LP64 -#define EFIERR(x) (0x8000000000000000 | x) -#else -#define EFIERR(x) (0x80000000 | x) -#endif - -#define EFI_UNSUPPORTED EFIERR(3) -#define EFI_DEVICE_ERROR EFIERR(7) - static kmutex_t efi_lock; static struct efi_rt *RT; #if BYTE_ORDER == LITTLE_ENDIAN Index: src/sys/arch/x86/x86/efi_machdep.c diff -u src/sys/arch/x86/x86/efi_machdep.c:1.3 src/sys/arch/x86/x86/efi_machdep.c:1.3.4.1 --- src/sys/arch/x86/x86/efi_machdep.c:1.3 Sat Sep 24 15:01:54 2022 +++ src/sys/arch/x86/x86/efi_machdep.c Tue Aug 1 16:05:12 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efi_machdep.c,v 1.3 2022/09/24 15:01:54 riastradh Exp $ */ +/* $NetBSD: efi_machdep.c,v 1.3.4.1 2023/08/01 16:05:12 martin Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: efi_machdep.c,v 1.3 2022/09/24 15:01:54 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: efi_machdep.c,v 1.3.4.1 2023/08/01 16:05:12 martin Exp $"); #include "efi.h" #include "opt_efi.h" @@ -582,18 +582,6 @@ efi_get_e820memmap(void) #ifdef EFI_RUNTIME /* - * XXX move to sys/dev/efi/efi.h - */ -#ifdef _LP64 -#define EFIERR(x) (0x8000000000000000ul | (x)) -#else -#define EFIERR(x) (0x80000000ul | (x)) -#endif - -#define EFI_UNSUPPORTED EFIERR(3) -#define EFI_DEVICE_ERROR EFIERR(7) - -/* * efi_runtime_init() * * Set up kernel access to EFI runtime services: @@ -985,12 +973,29 @@ efi_runtime_setvar(efi_char *name, struc return status; } +static efi_status +efi_runtime_gettab(const struct uuid *vendor, uint64_t *addrp) +{ + struct efi_cfgtbl *cfgtbl = efi_getcfgtblhead(); + paddr_t pa; + + if (cfgtbl == NULL) + return EFI_UNSUPPORTED; + + pa = efi_getcfgtblpa(vendor); + if (pa == 0) + return EFI_NOT_FOUND; + *addrp = pa; + return EFI_SUCCESS; +} + static struct efi_ops efi_runtime_ops = { .efi_gettime = efi_runtime_gettime, .efi_settime = efi_runtime_settime, .efi_getvar = efi_runtime_getvar, .efi_setvar = efi_runtime_setvar, .efi_nextvar = efi_runtime_nextvar, + .efi_gettab = efi_runtime_gettab, }; #endif /* EFI_RUNTIME */ Index: src/sys/dev/efi.c diff -u src/sys/dev/efi.c:1.4 src/sys/dev/efi.c:1.4.4.1 --- src/sys/dev/efi.c:1.4 Sat Sep 24 11:06:03 2022 +++ src/sys/dev/efi.c Tue Aug 1 16:05:12 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efi.c,v 1.4 2022/09/24 11:06:03 riastradh Exp $ */ +/* $NetBSD: efi.c,v 1.4.4.1 2023/08/01 16:05:12 martin Exp $ */ /*- * Copyright (c) 2021 Jared McNeill <jmcne...@invisible.ca> @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.4 2022/09/24 11:06:03 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.4.4.1 2023/08/01 16:05:12 martin Exp $"); #include <sys/param.h> #include <sys/conf.h> @@ -40,23 +40,10 @@ __KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.4 #include <sys/atomic.h> #include <sys/efiio.h> -#include <dev/efivar.h> - -#ifdef _LP64 -#define EFIERR(x) (0x8000000000000000 | x) -#else -#define EFIERR(x) (0x80000000 | x) -#endif +#include <uvm/uvm_extern.h> -#define EFI_SUCCESS 0 -#define EFI_INVALID_PARAMETER EFIERR(2) -#define EFI_UNSUPPORTED EFIERR(3) -#define EFI_BUFFER_TOO_SMALL EFIERR(5) -#define EFI_DEVICE_ERROR EFIERR(7) -#define EFI_WRITE_PROTECTED EFIERR(8) -#define EFI_OUT_OF_RESOURCES EFIERR(9) -#define EFI_NOT_FOUND EFIERR(14) -#define EFI_SECURITY_VIOLATION EFIERR(26) +#include <dev/efivar.h> +#include <dev/mm.h> #include "ioconf.h" @@ -149,12 +136,208 @@ 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) { uint16_t *namebuf; void *databuf = NULL; - size_t datasize; + size_t databufsize; + unsigned long datasize; efi_status status; int error; @@ -165,6 +348,9 @@ efi_ioctl_var_get(struct efi_var_ioc *va if (var->namesize > EFI_VARNAME_MAXLENGTH) { return ENOMEM; } + if (var->datasize > ULONG_MAX) { /* XXX stricter limit */ + return ENOMEM; + } namebuf = kmem_alloc(var->namesize, KM_SLEEP); error = copyin(var->name, namebuf, var->namesize); @@ -175,23 +361,26 @@ efi_ioctl_var_get(struct efi_var_ioc *va error = EINVAL; goto done; } - datasize = var->datasize; - if (datasize != 0) { - databuf = kmem_alloc(datasize, KM_SLEEP); - error = copyin(var->data, databuf, datasize); + databufsize = var->datasize; + if (databufsize != 0) { + databuf = kmem_alloc(databufsize, KM_SLEEP); + error = copyin(var->data, databuf, databufsize); if (error != 0) { goto done; } } + datasize = databufsize; status = efi_ops->efi_getvar(namebuf, &var->vendor, &var->attrib, - &var->datasize, databuf); + &datasize, databuf); if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) { error = efi_status_to_error(status); goto done; } - if (status == EFI_SUCCESS && databuf != NULL) { - error = copyout(databuf, var->data, var->datasize); + var->datasize = datasize; + if (status == EFI_SUCCESS && databufsize != 0) { + error = copyout(databuf, var->data, + MIN(datasize, databufsize)); } else { var->data = NULL; } @@ -199,7 +388,7 @@ efi_ioctl_var_get(struct efi_var_ioc *va done: kmem_free(namebuf, var->namesize); if (databuf != NULL) { - kmem_free(databuf, datasize); + kmem_free(databuf, databufsize); } return error; } @@ -209,7 +398,8 @@ efi_ioctl_var_next(struct efi_var_ioc *v { efi_status status; uint16_t *namebuf; - size_t namesize; + size_t namebufsize; + unsigned long namesize; int error; if (var->name == NULL || var->namesize == 0) { @@ -219,26 +409,30 @@ efi_ioctl_var_next(struct efi_var_ioc *v return ENOMEM; } - namesize = var->namesize; - namebuf = kmem_alloc(namesize, KM_SLEEP); - error = copyin(var->name, namebuf, namesize); + namebufsize = var->namesize; + namebuf = kmem_alloc(namebufsize, KM_SLEEP); + error = copyin(var->name, namebuf, namebufsize); if (error != 0) { goto done; } - status = efi_ops->efi_nextvar(&var->namesize, namebuf, &var->vendor); + CTASSERT(EFI_VARNAME_MAXLENGTH <= ULONG_MAX); + namesize = namebufsize; + status = efi_ops->efi_nextvar(&namesize, namebuf, &var->vendor); if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) { error = efi_status_to_error(status); goto done; } + var->namesize = namesize; if (status == EFI_SUCCESS) { - error = copyout(namebuf, var->name, var->namesize); + error = copyout(namebuf, var->name, + MIN(namesize, namebufsize)); } else { var->name = NULL; } done: - kmem_free(namebuf, namesize); + kmem_free(namebuf, namebufsize); return error; } @@ -289,6 +483,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: Index: src/sys/dev/efivar.h diff -u src/sys/dev/efivar.h:1.1 src/sys/dev/efivar.h:1.1.4.1 --- src/sys/dev/efivar.h:1.1 Sun Oct 10 13:03:09 2021 +++ src/sys/dev/efivar.h Tue Aug 1 16:05:12 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efivar.h,v 1.1 2021/10/10 13:03:09 jmcneill Exp $ */ +/* $NetBSD: efivar.h,v 1.1.4.1 2023/08/01 16:05:12 martin Exp $ */ /*- * Copyright (c) 2021 Jared McNeill <jmcne...@invisible.ca> @@ -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 *); Index: src/sys/dev/efi/efi.h diff -u src/sys/dev/efi/efi.h:1.2 src/sys/dev/efi/efi.h:1.2.4.1 --- src/sys/dev/efi/efi.h:1.2 Thu Sep 22 14:46:37 2022 +++ src/sys/dev/efi/efi.h Tue Aug 1 16:05:12 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efi.h,v 1.2 2022/09/22 14:46:37 riastradh Exp $ */ +/* $NetBSD: efi.h,v 1.2.4.1 2023/08/01 16:05:12 martin Exp $ */ /*- * Copyright (c) 2004 Marcel Moolenaar @@ -44,6 +44,22 @@ #define EFIAPI /* empty */ #endif +#ifdef _LP64 +#define EFIERR(x) (0x8000000000000000 | (x)) +#else +#define EFIERR(x) (0x80000000 | (x)) +#endif + +#define EFI_SUCCESS 0 +#define EFI_INVALID_PARAMETER EFIERR(2) +#define EFI_UNSUPPORTED EFIERR(3) +#define EFI_BUFFER_TOO_SMALL EFIERR(5) +#define EFI_DEVICE_ERROR EFIERR(7) +#define EFI_WRITE_PROTECTED EFIERR(8) +#define EFI_OUT_OF_RESOURCES EFIERR(9) +#define EFI_NOT_FOUND EFIERR(14) +#define EFI_SECURITY_VIOLATION EFIERR(26) + enum efi_reset { EFI_RESET_COLD, EFI_RESET_WARM, Index: src/sys/sys/efiio.h diff -u src/sys/sys/efiio.h:1.2 src/sys/sys/efiio.h:1.2.4.1 --- src/sys/sys/efiio.h:1.2 Mon Oct 11 10:23:02 2021 +++ src/sys/sys/efiio.h Tue Aug 1 16:05:12 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: efiio.h,v 1.2 2021/10/11 10:23:02 jmcneill Exp $ */ +/* $NetBSD: efiio.h,v 1.2.4.1 2023/08/01 16:05:12 martin 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) #define EFIIOC_VAR_GET _IOWR('e', 4, struct efi_var_ioc) #define EFIIOC_VAR_NEXT _IOWR('e', 5, struct efi_var_ioc) #define EFIIOC_VAR_SET _IOWR('e', 7, struct efi_var_ioc)