aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/efidev.423
-rw-r--r--sys/dev/efidev/efidev.c23
-rw-r--r--sys/dev/efidev/efirt.c128
-rw-r--r--sys/sys/efi.h39
-rw-r--r--sys/sys/efiio.h9
5 files changed, 219 insertions, 3 deletions
diff --git a/share/man/man4/efidev.4 b/share/man/man4/efidev.4
index b32a44325f92..2cadada51e9c 100644
--- a/share/man/man4/efidev.4
+++ b/share/man/man4/efidev.4
@@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd August 12, 2018
+.Dd June 18, 2021
.Dt EFIDEV 4
.Os
.Sh NAME
@@ -71,11 +71,28 @@ with supplemental structures and constants defined in
.In sys/efi.h :
.Bl -tag -width indent
.It Dv EFIIOC_GET_TABLE Pq Vt "struct efi_get_table_ioc"
-Get a table by uuid from the UEFI system table.
+Copy the UEFI table specified by the
+.Va uuid
+field of the
+.Vt struct efi_get_table_ioc
+into the
+.Va buf
+field.
+The memory size for the buf field can be queried by passing
+.Dv NULL
+pointer as a buf value.
+The required size will be stored in the
+.Va table_len
+field.
+The size of the allocated memory must be specified in the
+.Va buf_len
+field.
.Bd -literal -offset indent
struct efi_get_table_ioc {
+ void *buf;
struct uuid uuid;
- void *ptr;
+ size_t table_len;
+ size_t buf_len;
};
.Ed
.It Dv EFIIOC_GET_TIME Pq Vt "struct efi_tm"
diff --git a/sys/dev/efidev/efidev.c b/sys/dev/efidev/efidev.c
index 303b10c1d0ba..79d98956ed24 100644
--- a/sys/dev/efidev/efidev.c
+++ b/sys/dev/efidev/efidev.c
@@ -53,6 +53,29 @@ efidev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
int error;
switch (cmd) {
+ case EFIIOC_GET_TABLE:
+ {
+ struct efi_get_table_ioc *egtioc =
+ (struct efi_get_table_ioc *)addr;
+ void *buf = NULL;
+
+ error = efi_copy_table(&egtioc->uuid, egtioc->buf ? &buf : NULL,
+ egtioc->buf_len, &egtioc->table_len);
+
+ if (error != 0 || egtioc->buf == NULL)
+ break;
+
+ if (egtioc->buf_len < egtioc->table_len) {
+ error = EINVAL;
+ free(buf, M_TEMP);
+ break;
+ }
+
+ error = copyout(buf, egtioc->buf, egtioc->buf_len);
+ free(buf, M_TEMP);
+
+ break;
+ }
case EFIIOC_GET_TIME:
{
struct efi_tm *tm = (struct efi_tm *)addr;
diff --git a/sys/dev/efidev/efirt.c b/sys/dev/efidev/efirt.c
index aa7e9afdb69d..9ba0508f1902 100644
--- a/sys/dev/efidev/efirt.c
+++ b/sys/dev/efidev/efirt.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/linker.h>
#include <sys/lock.h>
+#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/clock.h>
@@ -47,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sched.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
+#include <sys/uio.h>
#include <sys/vmmeter.h>
#include <machine/fpu.h>
@@ -58,6 +60,8 @@ __FBSDID("$FreeBSD$");
#include <vm/pmap.h>
#include <vm/vm_map.h>
+#define EFI_TABLE_ALLOC_MAX 0x800000
+
static struct efi_systbl *efi_systbl;
static eventhandler_tag efi_shutdown_tag;
/*
@@ -96,6 +100,11 @@ static int efi_status2err[25] = {
EPROTO /* EFI_PROTOCOL_ERROR */
};
+enum efi_table_type {
+ TYPE_ESRT = 0,
+ TYPE_PROP
+};
+
static int efi_enter(void);
static void efi_leave(void);
@@ -336,6 +345,124 @@ get_table(struct uuid *uuid, void **ptr)
return (ENOENT);
}
+static int
+get_table_length(enum efi_table_type type, size_t *table_len, void **taddr)
+{
+ switch (type) {
+ case TYPE_ESRT:
+ {
+ struct efi_esrt_table *esrt = NULL;
+ struct uuid uuid = EFI_TABLE_ESRT;
+ uint32_t fw_resource_count = 0;
+ size_t len = sizeof(*esrt);
+ int error;
+ void *buf;
+
+ error = efi_get_table(&uuid, (void **)&esrt);
+ if (error != 0)
+ return (error);
+
+ buf = malloc(len, M_TEMP, M_WAITOK);
+ error = physcopyout((vm_paddr_t)esrt, buf, len);
+ if (error != 0) {
+ free(buf, M_TEMP);
+ return (error);
+ }
+
+ /* Check ESRT version */
+ if (((struct efi_esrt_table *)buf)->fw_resource_version !=
+ ESRT_FIRMWARE_RESOURCE_VERSION) {
+ free(buf, M_TEMP);
+ return (ENODEV);
+ }
+
+ fw_resource_count = ((struct efi_esrt_table *)buf)->
+ fw_resource_count;
+ if (fw_resource_count > EFI_TABLE_ALLOC_MAX /
+ sizeof(struct efi_esrt_entry_v1)) {
+ free(buf, M_TEMP);
+ return (ENOMEM);
+ }
+
+ len += fw_resource_count * sizeof(struct efi_esrt_entry_v1);
+ *table_len = len;
+
+ if (taddr != NULL)
+ *taddr = esrt;
+ free(buf, M_TEMP);
+ return (0);
+ }
+ case TYPE_PROP:
+ {
+ struct uuid uuid = EFI_PROPERTIES_TABLE;
+ struct efi_prop_table *prop;
+ size_t len = sizeof(*prop);
+ uint32_t prop_len;
+ int error;
+ void *buf;
+
+ error = efi_get_table(&uuid, (void **)&prop);
+ if (error != 0)
+ return (error);
+
+ buf = malloc(len, M_TEMP, M_WAITOK);
+ error = physcopyout((vm_paddr_t)prop, buf, len);
+ if (error != 0) {
+ free(buf, M_TEMP);
+ return (error);
+ }
+
+ prop_len = ((struct efi_prop_table *)buf)->length;
+ if (prop_len > EFI_TABLE_ALLOC_MAX) {
+ free(buf, M_TEMP);
+ return (ENOMEM);
+ }
+ *table_len = prop_len;
+
+ if (taddr != NULL)
+ *taddr = prop;
+ free(buf, M_TEMP);
+ return (0);
+ }
+ }
+ return (ENOENT);
+}
+
+static int
+copy_table(struct uuid *uuid, void **buf, size_t buf_len, size_t *table_len)
+{
+ static const struct known_table {
+ struct uuid uuid;
+ enum efi_table_type type;
+ } tables[] = {
+ { EFI_TABLE_ESRT, TYPE_ESRT },
+ { EFI_PROPERTIES_TABLE, TYPE_PROP }
+ };
+ size_t table_idx;
+ void *taddr;
+ int rc;
+
+ for (table_idx = 0; table_idx < nitems(tables); table_idx++) {
+ if (!bcmp(&tables[table_idx].uuid, uuid, sizeof(*uuid)))
+ break;
+ }
+
+ if (table_idx == nitems(tables))
+ return (EINVAL);
+
+ rc = get_table_length(tables[table_idx].type, table_len, &taddr);
+ if (rc != 0)
+ return rc;
+
+ /* return table length to userspace */
+ if (buf == NULL)
+ return (0);
+
+ *buf = malloc(*table_len, M_TEMP, M_WAITOK);
+ rc = physcopyout((vm_paddr_t)taddr, *buf, *table_len);
+ return (rc);
+}
+
static int efi_rt_handle_faults = EFI_RT_HANDLE_FAULTS_DEFAULT;
SYSCTL_INT(_machdep, OID_AUTO, efi_rt_handle_faults, CTLFLAG_RWTUN,
&efi_rt_handle_faults, 0,
@@ -568,6 +695,7 @@ var_set(efi_char *name, struct uuid *vendor, uint32_t attrib,
const static struct efi_ops efi_ops = {
.rt_ok = rt_ok,
.get_table = get_table,
+ .copy_table = copy_table,
.get_time = get_time,
.get_time_capabilities = get_time_capabilities,
.reset_system = reset_system,
diff --git a/sys/sys/efi.h b/sys/sys/efi.h
index 7f9408d19b39..6ace7dd6e523 100644
--- a/sys/sys/efi.h
+++ b/sys/sys/efi.h
@@ -40,6 +40,10 @@
{0xeb9d2d31,0x2d88,0x11d3,0x9a,0x16,{0x00,0x90,0x27,0x3f,0xc1,0x4d}}
#define EFI_TABLE_SMBIOS3 \
{0xf2fd1544,0x9794,0x4a2c,0x99,0x2e,{0xe5,0xbb,0xcf,0x20,0xe3,0x94}}
+#define EFI_TABLE_ESRT \
+ {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}}
enum efi_reset {
EFI_RESET_COLD = 0,
@@ -123,6 +127,31 @@ struct efi_tblhdr {
uint32_t __res;
};
+#define ESRT_FIRMWARE_RESOURCE_VERSION 1
+
+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;
+};
+
+struct efi_prop_table {
+ uint32_t version;
+ uint32_t length;
+ uint64_t memory_protection_attribute;
+};
+
#ifdef _KERNEL
#ifdef EFIABI_ATTR
@@ -188,6 +217,7 @@ struct efi_ops {
*/
int (*rt_ok)(void);
int (*get_table)(struct uuid *, void **);
+ int (*copy_table)(struct uuid *, void **, size_t, size_t *);
int (*get_time)(struct efi_tm *);
int (*get_time_capabilities)(struct efi_tmcap *);
int (*reset_system)(enum efi_reset);
@@ -216,6 +246,15 @@ static inline int efi_get_table(struct uuid *uuid, void **ptr)
return (active_efi_ops->get_table(uuid, ptr));
}
+static inline int efi_copy_table(struct uuid *uuid, void **buf,
+ size_t buf_len, size_t *table_len)
+{
+
+ if (active_efi_ops->copy_table == NULL)
+ return (ENXIO);
+ return (active_efi_ops->copy_table(uuid, buf, buf_len, table_len));
+}
+
static inline int efi_get_time(struct efi_tm *tm)
{
diff --git a/sys/sys/efiio.h b/sys/sys/efiio.h
index e5a0763536a3..803aed6a965e 100644
--- a/sys/sys/efiio.h
+++ b/sys/sys/efiio.h
@@ -32,6 +32,14 @@
#include <sys/uuid.h>
#include <sys/efi.h>
+struct efi_get_table_ioc
+{
+ void *buf; /* Pointer to userspace buffer */
+ struct uuid uuid; /* UUID to look up */
+ size_t table_len; /* Table size */
+ size_t buf_len; /* Size of the buffer */
+};
+
struct efi_var_ioc
{
efi_char *name; /* User pointer to name, in wide chars */
@@ -42,6 +50,7 @@ struct efi_var_ioc
size_t datasize; /* Number of *bytes* in the data */
};
+#define EFIIOC_GET_TABLE _IOWR('E', 1, struct efi_get_table_ioc)
#define EFIIOC_GET_TIME _IOR('E', 2, struct efi_tm)
#define EFIIOC_SET_TIME _IOW('E', 3, struct efi_tm)
#define EFIIOC_VAR_GET _IOWR('E', 4, struct efi_var_ioc)