Index: share/man/man4/efidev.4 =================================================================== --- share/man/man4/efidev.4 +++ 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 @@ .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 table from the UEFI system described by +.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 found 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" Index: sys/dev/efidev/efidev.c =================================================================== --- sys/dev/efidev/efidev.c +++ sys/dev/efidev/efidev.c @@ -53,6 +53,30 @@ 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; Index: sys/dev/efidev/efirt.c =================================================================== --- sys/dev/efidev/efirt.c +++ sys/dev/efidev/efirt.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include #include @@ -96,6 +98,11 @@ 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 +343,124 @@ 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) { + return (ENODEV); + } + + fw_resource_count = ((struct efi_esrt_table *)buf)->fw_resource_count; + len += sizeof(struct efi_esrt_entry_v1) * fw_resource_count; + *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; + *table_len = prop_len; + + if (taddr != NULL) { + *taddr = prop; + } + 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); + if (*buf == NULL) { + return (ENOMEM); + } + + 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 +693,7 @@ 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, Index: sys/sys/efi.h =================================================================== --- sys/sys/efi.h +++ 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 @@ 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 @@ */ 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,14 @@ 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) { Index: sys/sys/efiio.h =================================================================== --- sys/sys/efiio.h +++ sys/sys/efiio.h @@ -32,6 +32,14 @@ #include #include +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 @@ 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) Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile +++ usr.sbin/Makefile @@ -129,7 +129,7 @@ .endif SUBDIR.${MK_CXGBETOOL}+= cxgbetool SUBDIR.${MK_DIALOG}+= bsdconfig -SUBDIR.${MK_EFI}+= efivar efidp efibootmgr +SUBDIR.${MK_EFI}+= efivar efidp efibootmgr efitable .if ${MK_OPENSSL} != "no" SUBDIR.${MK_EFI}+= uefisign .endif Index: usr.sbin/efitable/Makefile =================================================================== --- /dev/null +++ usr.sbin/efitable/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +PROG= efitable +MAN= efitable.8 +SRCS= efitable.c + +LIBADD= xo + +.include Index: usr.sbin/efitable/Makefile.depend =================================================================== --- /dev/null +++ usr.sbin/efitable/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libutil \ + lib/libxo \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif Index: usr.sbin/efitable/efitable.8 =================================================================== --- /dev/null +++ usr.sbin/efitable/efitable.8 @@ -0,0 +1,63 @@ +.\" +.\" Copyright (c) 2021 3mdeb Embedded Systems Consulting +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd June 10, 2021 +.Dt EFITABLE 8 +.Os +.Sh NAME +.Nm efitable +.Nd UEFI tables dumping +.Sh SYNOPSIS +.Nm +.Op Fl u Ar uuid | Fl t Ar name +.Op Fl -libxo +.Sh DESCRIPTION +This program prints data from +.Dq Unified Extensible Firmware Interface +.Pq UEFI +tables. +.Pp +The following options are available: +.Bl -tag -width 20m +.It Fl -libxo +Generate output via +.Xr libxo 3 +in a selection of different human and machine readable formats. +See +.Xr xo_parse_args 3 +for details on command line arguments. +.It Fl t Ar name Fl -table Ar name +Specify the name of the table to print. +Currently supported tables: esrt (EFI System Resource Table), +prop (EFI Properties Table) +.It Fl u Ar uuid Fl -uuid Ar uuid +Specify the UUID of the table to print. +.El +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 14.0 . Index: usr.sbin/efitable/efitable.c =================================================================== --- /dev/null +++ usr.sbin/efitable/efitable.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2021 3mdeb Embedded Systems Consulting + * + * 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 AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TABLE_MAX_LEN 30 +#define EFITABLE_XO_VERSION "1" + +static void efi_table_print_esrt(const void *data); +static void efi_table_print_prop(const void *data); +static void usage(void); + +struct efi_table_op { + char name[TABLE_MAX_LEN]; + void (*parse) (const void *); + struct uuid uuid; +}; + +static const struct efi_table_op efi_table_ops[] = { + { .name = "esrt", .parse = efi_table_print_esrt, .uuid = EFI_TABLE_ESRT }, + { .name = "prop", .parse = efi_table_print_prop, .uuid = EFI_PROPERTIES_TABLE } +}; + +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 } + }; + + argc = xo_parse_args(argc, argv); + if (argc < 0) { + exit(EXIT_FAILURE); + } + + 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; + + uuid_set = 1; + + uuid_from_string(uuid_str, &uuid, &status); + if (status != uuid_s_ok) { + xo_errx(EX_DATAERR, "invalid UUID"); + } + for (size_t 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; + + table_set = true; + + for (size_t 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) { + xo_errx(EX_DATAERR, "unsupported efi table"); + } + + break; + } + default: + usage(); + } + } + + if (!table_set && !uuid_set) { + xo_errx(EX_USAGE, "table is not set"); + } + + if (!got_table) { + xo_errx(EX_DATAERR, "unsupported table"); + } + + efi_fd = open("/dev/efi", O_RDWR); + if (efi_fd < 0) { + xo_err(EX_OSFILE, "/dev/efi"); + } + + table.uuid = efi_table_ops[efi_idx].uuid; + if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) { + xo_err(EX_OSERR, NULL); + } + + table.buf = malloc(table.table_len); + table.buf_len = table.table_len; + + if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) { + xo_err(EX_OSERR, NULL); + } + + efi_table_ops[efi_idx].parse(table.buf); + close(efi_fd); + + return (rc); +} + +static void +efi_table_print_esrt(const void *data) +{ + const struct efi_esrt_entry_v1 *entries_v1; + const struct efi_esrt_table *esrt; + + esrt = (const struct efi_esrt_table *)data; + + xo_set_version(EFITABLE_XO_VERSION); + xo_open_container("esrt"); + xo_emit("{Lwc:FwResourceCount}{:fw_resource_count/%u}\n", + esrt->fw_resource_count); + xo_emit("{Lwc:FwResourceCountMax}{:fw_resource_count_max/%u}\n", + esrt->fw_resource_count_max); + xo_emit("{Lwc:FwResourceVersion}{:fw_resource_version/%u}\n", + esrt->fw_resource_version); + xo_open_list("entries"); + xo_emit("\nEntries:\n"); + + entries_v1 = (const void *) esrt->entries; + for (uint32_t i = 0; i < esrt->fw_resource_count; i++) { + const struct efi_esrt_entry_v1 *e = &entries_v1[i]; + uint32_t status; + char *uuid; + + uuid_to_string(&e->fw_class, &uuid, &status); + if (status != uuid_s_ok) { + xo_errx(EX_DATAERR, "uuid_to_string error"); + } + + xo_open_instance("entries"); + xo_emit("\n"); + xo_emit("{P: }{Lwc:FwClass}{:fw_class/%s}\n", uuid); + xo_emit("{P: }{Lwc:FwType}{:fw_type/%u}\n", e->fw_type); + xo_emit("{P: }{Lwc:FwVersion}{:fw_version/%u}\n", e->fw_version); + xo_emit("{P: }{Lwc:LowestSupportedFwVersion}" + "{:lowest_supported_fw_version/%u}\n", + e->lowest_supported_fw_version); + xo_emit("{P: }{Lwc:CapsuleFlags}{:capsule_flags/%#x}\n", + e->capsule_flags); + xo_emit("{P: }{Lwc:LastAttemptVersion" + "}{:last_attempt_version/%u}\n", e->last_attempt_version); + xo_emit("{P: }{Lwc:LastAttemptStatus" + "}{:last_attempt_status/%u}\n", e->last_attempt_status); + + xo_close_instance("entries"); + } + + xo_close_list("entries"); + xo_close_container("esrt"); + xo_finish(); + + return; +} + +static void +efi_table_print_prop(const void *data) +{ + const struct efi_prop_table *prop; + + prop = (const struct efi_prop_table *)data; + + xo_set_version(EFITABLE_XO_VERSION); + xo_open_container("prop"); + xo_emit("{Lwc:Version}{:version/%#x}\n", prop->version); + xo_emit("{Lwc:Length}{:length/%u}\n", prop->length); + xo_emit("{Lwc:MemoryProtectionAttribute}" + "{:memory_protection_attribute/%#lx}\n", + prop->memory_protection_attribute); + xo_close_container("prop"); + xo_finish(); + + return; +} + +static void usage(void) +{ + xo_error("usage: efitable [-d uuid | -t name] [--libxo]\n"); + exit(EX_USAGE); +}