pkgsrc-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: pkg/57076: Add efivar port to NetBSD
The following reply was made to PR pkg/57076; it has been noted by GNATS.
From: Sergii Dmytruk <sergii.dmytruk%3mdeb.com@localhost>
To: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Cc: gnats-bugs%NetBSD.org@localhost
Subject: Re: pkg/57076: Add efivar port to NetBSD
Date: Sun, 6 Nov 2022 19:42:24 +0200
--e94Sh53BmyHhQjjd
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Apart from swapped arguments, there was incorrect duplicated definition of
EFI_SUCCESS:
#define EFI_SUCCESS EFIERR(0)
It's just 0, since this isn't an error.
With these fixes, everything works. I've attached modified patch and a
client for the API which I used for testing.
--e94Sh53BmyHhQjjd
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="efitab-fixed.patch"
sys/arch/x86/x86/efi_machdep.c | 29 +++---
sys/dev/efi.c | 216 ++++++++++++++++++++++++++++++++++++++---
sys/dev/efi/efi.h | 16 +++
sys/dev/efivar.h | 8 +-
sys/sys/efiio.h | 8 ++
5 files changed, 247 insertions(+), 30 deletions(-)
diff --git a/sys/arch/x86/x86/efi_machdep.c b/sys/arch/x86/x86/efi_machdep.c
index 3a40a8eee04..57710c799dd 100644
--- a/sys/arch/x86/x86/efi_machdep.c
+++ b/sys/arch/x86/x86/efi_machdep.c
@@ -581,18 +581,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()
*
@@ -985,12 +973,29 @@ efi_runtime_setvar(efi_char *name, struct uuid *vendor, uint32_t attrib,
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 */
diff --git a/sys/dev/efi.c b/sys/dev/efi.c
index 91f0e1365d7..cba8c0f3707 100644
--- a/sys/dev/efi.c
+++ b/sys/dev/efi.c
@@ -40,23 +40,10 @@ __KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.4 2022/09/24 11:06:03 riastradh Exp $");
#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,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)
{
@@ -289,6 +471,8 @@ efi_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
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 --git a/sys/dev/efi/efi.h b/sys/dev/efi/efi.h
index 1f7fe910d06..544c81c0ca5 100644
--- a/sys/dev/efi/efi.h
+++ b/sys/dev/efi/efi.h
@@ -44,6 +44,22 @@
#define EFIAPI /* empty */
#endif
+#ifdef _LP64
+#define EFIERR(x) (0x8000000000000000ul | (x))
+#else
+#define EFIERR(x) (0x80000000ul | (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,
diff --git a/sys/dev/efivar.h b/sys/dev/efivar.h
index 21d6d61fd26..72aeb8c6fba 100644
--- a/sys/dev/efivar.h
+++ b/sys/dev/efivar.h
@@ -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 --git a/sys/sys/efiio.h b/sys/sys/efiio.h
index 8f3a9a2d54e..c50c2c416fa 100644
--- a/sys/sys/efiio.h
+++ b/sys/sys/efiio.h
@@ -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)
--e94Sh53BmyHhQjjd
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="efitable.c"
/*-
* Copyright (c) 2022 3mdeb <contact%3mdeb.com@localhost>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/efiio.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <err.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <uuid.h>
#include <errno.h>
#define TABLE_MAX_LEN 30
#ifdef EFI_TABLE_ESRT
#undef EFI_TABLE_ESRT
#endif
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#define EFI_TABLE_ESRT \
{0xb122a263,0x3661,0x4f68,0x99,0x29,{0x78,0xf8,0xb0,0xd6,0x21,0x80}}
static void efi_table_print_esrt(void *data);
static void usage(void);
static void fail(const char msg[]);
struct efi_table_op {
char name[TABLE_MAX_LEN];
void (*parse) (void *);
struct uuid uuid;
};
static const struct efi_table_op efi_table_ops[] = {
{ .name = "esrt",
.parse = efi_table_print_esrt,
.uuid = EFI_TABLE_ESRT },
};
struct efi_esrt_table {
uint32_t fw_resource_count;
uint32_t fw_resource_count_max;
uint64_t fw_resource_version;
uint8_t entries[];
};
struct efi_esrt_entry_v1 {
struct uuid fw_class;
uint32_t fw_type;
uint32_t fw_version;
uint32_t lowest_supported_fw_version;
uint32_t capsule_flags;
uint32_t last_attempt_version;
uint32_t last_attempt_status;
};
int
main(int argc, char **argv)
{
struct efi_get_table_ioc table = {
.buf = NULL,
.buf_len = 0,
.table_len = 0
};
int efi_fd, ch, rc = 1, efi_idx = -1;
bool got_table = false;
bool table_set = false;
bool uuid_set = false;
struct option longopts[] = {
{ "uuid", required_argument, NULL, 'u' },
{ "table", required_argument, NULL, 't' },
{ NULL, 0, NULL, 0 }
};
while ((ch = getopt_long(argc, argv, "u:t:", longopts, NULL)) != -1) {
switch (ch) {
case 'u': {
char *uuid_str = optarg;
struct uuid uuid;
uint32_t status;
size_t n;
uuid_set = 1;
uuid_from_string(uuid_str, &uuid, &status);
if (status != uuid_s_ok)
fail("invalid UUID");
for (n = 0; n < nitems(efi_table_ops); n++) {
if (!memcmp(&uuid,
&efi_table_ops[n].uuid,
sizeof(uuid))) {
efi_idx = n;
got_table = true;
break;
}
}
break;
}
case 't': {
char *table_name = optarg;
size_t n;
table_set = true;
for (n = 0; n < nitems(efi_table_ops); n++) {
if (!strcmp(table_name,
efi_table_ops[n].name)) {
efi_idx = n;
got_table = true;
break;
}
}
if (!got_table)
fail("unsupported efi table");
break;
}
default:
usage();
}
}
if (!table_set && !uuid_set)
fail("table is not set");
if (!got_table)
fail("unsupported table");
efi_fd = open("/dev/efi", O_RDWR);
if (efi_fd < 0)
fail("/dev/efi");
table.uuid = efi_table_ops[efi_idx].uuid;
if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1)
fail(NULL);
table.buf = malloc(table.table_len);
table.buf_len = table.table_len;
if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1)
fail(NULL);
close(efi_fd);
efi_table_ops[efi_idx].parse(table.buf);
return (rc);
}
static void
efi_table_print_esrt(void *data)
{
struct efi_esrt_table *esrt = data;
struct efi_esrt_entry_v1 *esre = (void *)esrt->entries;
printf("ESRT FwResourceCount = %d\n", esrt->fw_resource_count);
uint32_t i;
for (i = 0; i < esrt->fw_resource_count; i++) {
char *uuid = NULL;
uuid_to_string(&esre[i].fw_class, &uuid, NULL);
printf("ESRT[%d]:\n", i);
printf(" FwClass: %s\n", uuid);
printf(" FwType: 0x%08x\n", esre[i].fw_type);
printf(" FwVersion: 0x%08x\n", esre[i].fw_version);
printf(" LowestSupportedFwVersion: 0x%08x\n",
esre[i].lowest_supported_fw_version);
printf(" CapsuleFlags: 0x%08x\n",
esre[i].capsule_flags);
printf(" LastAttemptVersion: 0x%08x\n",
esre[i].last_attempt_version);
printf(" LastAttemptStatus: 0x%08x\n",
esre[i].last_attempt_status);
free(uuid);
}
}
static void usage(void)
{
printf("usage: efitable [-u uuid | -t name]\n");
exit(1);
}
static void fail(const char msg[])
{
if (msg != NULL)
fprintf(stderr, "%s\n", msg);
else
fprintf(stderr, "errno: %s\n", strerror(errno));
exit(1);
}
--e94Sh53BmyHhQjjd--
Home |
Main Index |
Thread Index |
Old Index