Index: share/man/man4/efidev.4 =================================================================== --- share/man/man4/efidev.4 +++ share/man/man4/efidev.4 @@ -71,11 +71,13 @@ .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 a table by uuid from the UEFI system table. .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 @@ -34,11 +34,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -57,6 +59,9 @@ #include #include #include +#include + +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) static struct efi_systbl *efi_systbl; static eventhandler_tag efi_shutdown_tag; @@ -96,6 +101,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 +346,137 @@ 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; + size_t len = sizeof(*esrt); + uint32_t fw_resource_count = 0; + int error; + void *buf = malloc(len, M_TEMP, M_WAITOK); + + error = efi_get_table(&uuid, (void **)&esrt); + if (error != 0) { + free(buf, M_TEMP); + break; + } +#if 0 + efi_enter(); + fw_resource_count = esrt->fw_resource_count; + efi_leave(); +#endif +#if 1 + printf("%s: esrt:%p, buf=%p, len=%zu\n", __func__, esrt, buf, len); + error = physcopyout((vm_paddr_t)esrt, buf, len); + if (error != 0) { + free(buf, M_TEMP); + break; + } + fw_resource_count = ((struct efi_esrt_table *)buf)->fw_resource_count; + /* TODO check esrt version */ + if (fw_resource_count != 1) { /*XXX debug, will be removed */ + printf("%s fw_resource_count: %u\n", __func__, fw_resource_count); + return (ENXIO); + } +#endif + len += sizeof(struct efi_esrt_entry_v1) * fw_resource_count; + *table_len = len; + printf("%s taddr = %p\n", __func__, esrt); + printf("%s fw_resource_count: %u\n", __func__, fw_resource_count); + printf("%s got esrt table len: %zu\n", __func__, *table_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; + int error; + + error = efi_get_table(&uuid, (void **)&prop); + if (error != 0) { + break; + } + + efi_enter(); + len = prop->length; + efi_leave(); + + *table_len = len; + printf("%s taddr = %p\n", __func__, prop); + printf("%s got prop table len: %zu\n", __func__, *table_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) +{ + struct known_table { + struct uuid uuid; + enum efi_table_type type; + }; + + struct known_table tables[] = { + { EFI_TABLE_ESRT, TYPE_ESRT }, + { EFI_PROPERTIES_TABLE, TYPE_PROP } + }; + bool table_supported = false; + size_t table_idx; + void *taddr; + int rc; + + for (table_idx = 0; table_idx < ARRAY_LENGTH(tables); table_idx++) { + if (!bcmp(&tables[table_idx].uuid, uuid, sizeof(*uuid))) { + table_supported = true; + break; + } + } + + if (!table_supported) { + return (EINVAL); + } + + rc = get_table_length(tables[table_idx].type, table_len, &taddr); + if (rc != 0) { + return rc; + } + printf("%s: buf addr: %p\n", __func__, (buf) ? *buf : NULL); + + /* return table length to userspace */ + if (buf == NULL) { + printf("%s: only get length\n", __func__); + return (0); + } + printf("%s: get table\n", __func__); + + *buf = malloc(*table_len, M_TEMP, M_WAITOK); + if (*buf == NULL) { + return (ENOMEM); + } + printf("%s taddr = %p\n", __func__, taddr); + + /*TODO use physcopyout() */ + efi_enter(); + memcpy(*buf, taddr, *table_len); + efi_leave(); + + return (0); +} + 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 +709,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,29 @@ uint32_t __res; }; +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 +215,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 +244,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,8 @@ +# $FreeBSD$ + +PROG= efitable +MAN= efitable.8 +SRCS= efitable.c + +.include +# DO NOT DELETE Index: usr.sbin/efitable/efitable.8 =================================================================== --- /dev/null +++ usr.sbin/efitable/efitable.8 @@ -0,0 +1,58 @@ +.\" +.\" Copyright (c) 2021 3mdeb. +.\" +.\" 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 interaction +.Sh SYNOPSIS +.Nm +.Op Fl u Ar uuid | Fl t Ar name +.Op Fl j +.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 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. +.It Fl j Fl -json +Print table content in JSON format +.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,284 @@ +/*- + * Copyright (c) 2021 3mdeb. + * + * 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 + +#define TABLE_MAX_LEN 30 +#define ARR_LEN(arr) (sizeof(arr) / sizeof((arr)[0])) + +static void efi_table_print_esrt(const void *data, int json_format); +static void efi_table_print_prop(const void *data, int json_format); + +struct efi_table_op { + char name[TABLE_MAX_LEN]; + void (*parse) (const void *, int); + struct uuid uuid; +}; + +static 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; + int got_table = 0; + int table_set = 0; + int uuid_set = 0; + int use_json = 0; + + struct option longopts[] = { + { "uuid", required_argument, NULL, 'u' }, + { "table", required_argument, NULL, 't' }, + { "json", no_argument, NULL, 'j' }, + { NULL, 0, NULL, 0 } + }; + + while ((ch = getopt_long(argc, argv, "u:t:j", 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) { + fprintf(stderr, "invalid UUID\n"); + exit(EXIT_FAILURE); + } + for (size_t n = 0; n < ARR_LEN(efi_table_ops); n++) { + if (!memcmp(&uuid, &efi_table_ops[n].uuid, sizeof(uuid))) { + efi_idx = n; + got_table = 1; + break; + } + } + break; + } + case 't': + { + char *table_name = optarg; + + table_set = 1; + + for (size_t n = 0; n < ARR_LEN(efi_table_ops); n++) { + if (!strcmp(table_name, efi_table_ops[n].name)) { + efi_idx = n; + got_table = 1; + break; + } + } + + if (!got_table) { + fprintf(stderr, "unsupported efi table\n"); + exit(EXIT_FAILURE); + } + + break; + } + case 'j': + use_json = 1; + break; + default: + fprintf(stderr, "Usage: %s [-d uuid | -t name] [-j]\n", argv[0]); + exit(EXIT_FAILURE); + } + } + + if (!table_set && !uuid_set) { + fprintf(stderr, "table is not set\n"); + exit(EXIT_FAILURE); + } + + if (!got_table) { + fprintf(stderr, "unsupported table\n"); + exit(EXIT_FAILURE); + } + + efi_fd = open("/dev/efi", O_RDWR); + if (efi_fd < 0) { + fprintf(stderr, "Cannot open /dev/efi: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + table.uuid = efi_table_ops[efi_idx].uuid; + if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) { + fprintf(stderr, "0:ioctl error: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + printf("Got table len: %zu\n", table.table_len); + table.buf = malloc(table.table_len); + table.buf_len = table.table_len; + + if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) { + fprintf(stderr, "1:ioctl error: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + efi_table_ops[efi_idx].parse(table.buf, use_json); + close(efi_fd); + + return (rc); +} + +static void efi_table_print_esrt(const void *data, int json_format) +{ + const struct efi_esrt_table *esrt = NULL; + const struct efi_esrt_entry_v1 *entries_v1; + + esrt = (const struct efi_esrt_table *)data; + if (!json_format) { + printf("fw_resource_count: %u\n", esrt->fw_resource_count); + printf("fw_resource_count_max: %u\n", esrt->fw_resource_count_max); + printf("fw_resource_version: %lu\n", esrt->fw_resource_version); + + 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) { + fprintf(stderr, "uuid_to_string error\n"); + exit(EXIT_FAILURE); + } + + printf("%s%d:\n", "entry", i); + printf(" %s: %s\n", "fw_class", uuid); + printf(" %s: %u\n", "fw_type", e->fw_type); + printf(" %s: %u\n", "fw_version", e->fw_version); + printf(" %s: %u\n", "lowest_supported_fw_version", + e->lowest_supported_fw_version); + printf(" %s: %#x\n", "capsule_flags", e->capsule_flags); + printf(" %s: %u\n", "last_attempt_version", e->last_attempt_version); + printf(" %s: %u\n", "last_attempt_status", e->last_attempt_status); + + free(uuid); + + return; + } + } + + printf("{"); + printf("\"fw_resource_count\": \"%u\",", esrt->fw_resource_count); + printf("\"fw_resource_count_max\": \"%u\",", esrt->fw_resource_count_max); + printf("\"fw_resource_version\": \"%lu\",", esrt->fw_resource_version); + printf("\"entries\":["); + + 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) { + fprintf(stderr, "\nuuid_to_string error\n"); + exit(EXIT_FAILURE); + } + + printf("%s", (!i) ? "{" : ",{"); + printf("\"%s\": \"%s\",", "fw_class", uuid); + printf("\"%s\": \"%u\",", "fw_type", e->fw_type); + printf("\"%s\": \"%u\",", "fw_version", e->fw_version); + printf("\"%s\": \"%u\",", "lowest_supported_fw_version", + e->lowest_supported_fw_version); + printf("\"%s\": \"%#x\",", "capsule_flags", e->capsule_flags); + printf("\"%s\": \"%u\",", "last_attempt_version", e->last_attempt_version); + printf("\"%s\": \"%u\"", "last_attempt_status", e->last_attempt_status); + printf("%s", "}"); + + free(uuid); + } + printf("]}"); + + return; +} + +static void efi_table_print_prop(const void *data, int json_format) +{ + const struct efi_prop_table *prop = NULL; + + prop = (const struct efi_prop_table *)data; + + if (!json_format) { + printf("version: %#x\n", prop->version); + printf("length: %u\n", prop->length); + printf("memory_protection_attribute: %#lx\n", + prop->memory_protection_attribute); + return; + } + + printf("{"); + printf("\"version\": \"%#x\",", prop->version); + printf("\"length\": \"%u\",", prop->length); + printf("\"memory_protection_attribute\": \"%#lx\"", + prop->memory_protection_attribute); + printf("}"); + + return; +} + +#if 0 + printf("Got ESRT table phys address: %p\n\n", table.ptr); + mem_fd = open("/dev/mem", O_RDONLY, 0); + if (efi_fd < 0) { + fprintf(stderr, "Cannot open /dev/mem: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + memp = mmap(NULL, 4096, PROT_READ, MAP_SHARED, mem_fd, (uint64_t)table.ptr); + if (memp == MAP_FAILED) { + fprintf(stderr, "Cannot map /dev/mem: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } +#endif