util/cbmem: Add support for CBMEM in sysfs
This commit adds support for CBMEM in sysfs. Useful for systems without access to /dev/mem e.g. Android. Linux kernel driver: drivers/firmware/google/cbmem.c Linux driver Kconfig: CONFIG_GOOGLE_CBMEM BUG=b:391874512 TEST=(devmem) cbmem -l; cbmem -x; cbmem -r 434f4e53; cbmem -t; cbmem -a 1200 TEST=modprobe cbmem; cbmem -l; cbmem -x; cbmem -r 434f4e53; cbmem -t; cbmem -a 1200 Change-Id: I527889509ffc84203be42d0160e5363c60eafd02 Signed-off-by: Jakub Czapiga <czapiga@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/86606 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
parent
eeb15e83cb
commit
7095c99a87
5 changed files with 500 additions and 19 deletions
|
|
@ -14,7 +14,7 @@ CFLAGS += -Wall -Wextra -Wmissing-prototypes -Wshadow $(WERROR)
|
|||
CPPFLAGS += -I . -I $(ROOT)/commonlib/include -I $(ROOT)/commonlib/bsd/include
|
||||
CPPFLAGS += -include $(ROOT)/commonlib/bsd/include/commonlib/bsd/compiler.h
|
||||
|
||||
OBJS = $(PROGRAM).o devmem_drv.o $(COMMONLIB)/bsd/ipchksum.o
|
||||
OBJS = $(PROGRAM).o cbmem_drv.o devmem_drv.o sysfs_drv.o $(COMMONLIB)/bsd/ipchksum.o
|
||||
|
||||
all: $(PROGRAM)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -29,7 +30,7 @@
|
|||
|
||||
#include "cbmem_util.h"
|
||||
|
||||
#define CBMEM_VERSION "1.1"
|
||||
#define CBMEM_VERSION "1.2"
|
||||
|
||||
/* Global verbosity level for debug() macro. */
|
||||
int cbmem_util_verbose;
|
||||
|
|
@ -140,8 +141,7 @@ static void cbmem_get_lb_table_entry(uint32_t tag, uint8_t **buf_out, size_t *si
|
|||
const uint8_t *lbtable_raw;
|
||||
bool tag_found = false;
|
||||
|
||||
if (!cbmem_devmem_get_cbmem_entry(CBMEM_ID_CBTABLE, (uint8_t **)&lbtable_raw, NULL,
|
||||
NULL))
|
||||
if (!cbmem_drv_get_cbmem_entry(CBMEM_ID_CBTABLE, (uint8_t **)&lbtable_raw, NULL, NULL))
|
||||
die("coreboot table not found.\n");
|
||||
|
||||
const struct lb_header *lbh = (const struct lb_header *)lbtable_raw;
|
||||
|
|
@ -337,7 +337,7 @@ static void dump_timestamps(enum timestamps_print_type output_type)
|
|||
uint64_t prev_stamp = 0;
|
||||
uint64_t total_time = 0;
|
||||
|
||||
if (!cbmem_devmem_get_cbmem_entry(CBMEM_ID_TIMESTAMP, (uint8_t **)&tst_p, &size, NULL))
|
||||
if (!cbmem_drv_get_cbmem_entry(CBMEM_ID_TIMESTAMP, (uint8_t **)&tst_p, &size, NULL))
|
||||
die("Timestamps not found.\n");
|
||||
|
||||
timestamp_set_tick_freq(tst_p->tick_freq_mhz);
|
||||
|
|
@ -437,7 +437,7 @@ static void timestamp_add_now(uint32_t timestamp_id)
|
|||
struct timestamp_table *tst_p;
|
||||
size_t tst_size;
|
||||
|
||||
if (!cbmem_devmem_get_cbmem_entry(CBMEM_ID_TIMESTAMP, (uint8_t **)&tst_p, &tst_size, NULL))
|
||||
if (!cbmem_drv_get_cbmem_entry(CBMEM_ID_TIMESTAMP, (uint8_t **)&tst_p, &tst_size, NULL))
|
||||
die("Unable to find timestamps.\n");
|
||||
|
||||
/*
|
||||
|
|
@ -454,7 +454,7 @@ static void timestamp_add_now(uint32_t timestamp_id)
|
|||
tst_p->num_entries += 1;
|
||||
}
|
||||
|
||||
if (!cbmem_devmem_write_cbmem_entry(CBMEM_ID_TIMESTAMP, (uint8_t *)tst_p, tst_size))
|
||||
if (!cbmem_drv_write_cbmem_entry(CBMEM_ID_TIMESTAMP, (uint8_t *)tst_p, tst_size))
|
||||
die("Unable to write timestamps.\n");
|
||||
free((uint8_t *)tst_p);
|
||||
}
|
||||
|
|
@ -674,7 +674,7 @@ static void dump_tpm_cb_log(void)
|
|||
{
|
||||
const struct tpm_cb_log_table *tclt_p;
|
||||
|
||||
if (!cbmem_devmem_get_cbmem_entry(CBMEM_ID_TPM_CB_LOG, (uint8_t **)&tclt_p, NULL, NULL))
|
||||
if (!cbmem_drv_get_cbmem_entry(CBMEM_ID_TPM_CB_LOG, (uint8_t **)&tclt_p, NULL, NULL))
|
||||
die("coreboot TPM log not found.\n");
|
||||
|
||||
printf("coreboot TPM log:\n\n");
|
||||
|
|
@ -694,8 +694,8 @@ static void dump_tpm_log(void)
|
|||
{
|
||||
uint8_t *buf;
|
||||
|
||||
if (cbmem_devmem_get_cbmem_entry(CBMEM_ID_TCPA_TCG_LOG, &buf, NULL, NULL) ||
|
||||
cbmem_devmem_get_cbmem_entry(CBMEM_ID_TPM2_TCG_LOG, &buf, NULL, NULL)) {
|
||||
if (cbmem_drv_get_cbmem_entry(CBMEM_ID_TCPA_TCG_LOG, &buf, NULL, NULL) ||
|
||||
cbmem_drv_get_cbmem_entry(CBMEM_ID_TPM2_TCG_LOG, &buf, NULL, NULL)) {
|
||||
dump_tpm_std_log(buf);
|
||||
free(buf);
|
||||
} else
|
||||
|
|
@ -738,7 +738,7 @@ static void dump_console(enum console_print_type type, int max_loglevel, int pri
|
|||
char *console_c;
|
||||
size_t size, cursor, previous;
|
||||
|
||||
if (!cbmem_devmem_get_cbmem_entry(CBMEM_ID_CONSOLE, (uint8_t **)&console_p, NULL, NULL))
|
||||
if (!cbmem_drv_get_cbmem_entry(CBMEM_ID_CONSOLE, (uint8_t **)&console_p, NULL, NULL))
|
||||
die("CBMEM console not found.\n");
|
||||
|
||||
cursor = console_p->cursor & CBMC_CURSOR_MASK;
|
||||
|
|
@ -882,7 +882,7 @@ static bool hexdump_handler(const uint32_t id, const uint64_t physical_address,
|
|||
|
||||
static void dump_cbmem_hex(void)
|
||||
{
|
||||
cbmem_devmem_foreach_cbmem_entry(hexdump_handler, NULL, true);
|
||||
cbmem_drv_foreach_cbmem_entry(hexdump_handler, NULL, true);
|
||||
}
|
||||
|
||||
static void dump_cbmem_raw(unsigned int id)
|
||||
|
|
@ -890,7 +890,7 @@ static void dump_cbmem_raw(unsigned int id)
|
|||
uint8_t *buf;
|
||||
size_t size;
|
||||
|
||||
if (!cbmem_devmem_get_cbmem_entry(id, &buf, &size, NULL))
|
||||
if (!cbmem_drv_get_cbmem_entry(id, &buf, &size, NULL))
|
||||
die("cbmem entry id: %#x not found.\n", id);
|
||||
|
||||
fwrite(buf, 1, size, stdout);
|
||||
|
|
@ -958,7 +958,7 @@ static void dump_cbmem_toc(void)
|
|||
printf("CBMEM table of contents:\n");
|
||||
printf(" %-20s %-8s %-8s %-8s\n", "NAME", "ID", "START", "LENGTH");
|
||||
|
||||
cbmem_devmem_foreach_cbmem_entry(toc_handler, &i, false);
|
||||
cbmem_drv_foreach_cbmem_entry(toc_handler, &i, false);
|
||||
}
|
||||
|
||||
#define COVERAGE_MAGIC 0x584d4153
|
||||
|
|
@ -995,7 +995,7 @@ static void dump_coverage(void)
|
|||
unsigned long phys_offset;
|
||||
#define phys_to_virt(x) ((void *)(unsigned long)(x) + phys_offset)
|
||||
|
||||
if (!cbmem_devmem_get_cbmem_entry(CBMEM_ID_COVERAGE, &coverage, NULL, &start))
|
||||
if (!cbmem_drv_get_cbmem_entry(CBMEM_ID_COVERAGE, &coverage, NULL, &start))
|
||||
die("No coverage information found\n");
|
||||
|
||||
/* Map coverage area */
|
||||
|
|
@ -1059,6 +1059,7 @@ static void print_usage(const char *name, int exit_code)
|
|||
" -c | --console: print cbmem console\n"
|
||||
" -1 | --oneboot: print cbmem console for last boot only\n"
|
||||
" -2 | --2ndtolast: print cbmem console for the boot that came before the last one only\n"
|
||||
" -b | --backend [devmem|sysfs]: select specific CBMEM backend\n"
|
||||
" -B | --loglevel: maximum loglevel to print; prefix `+` (e.g. -B +INFO) to also print lines that have no level\n"
|
||||
" -C | --coverage: dump coverage information\n"
|
||||
" -l | --list: print cbmem table of contents\n"
|
||||
|
|
@ -1091,10 +1092,12 @@ int main(int argc, char** argv)
|
|||
int max_loglevel = BIOS_NEVER;
|
||||
int print_unknown_logs = 1;
|
||||
uint32_t timestamp_id = 0;
|
||||
enum cbmem_drv_backend_type backend_type = CBMEM_DRV_BACKEND_ANY;
|
||||
|
||||
int opt, option_index = 0;
|
||||
static struct option long_options[] = {
|
||||
{"console", 0, 0, 'c'},
|
||||
{"backend", required_argument, 0, 'b'},
|
||||
{"oneboot", 0, 0, '1'},
|
||||
{"2ndtolast", 0, 0, '2'},
|
||||
{"loglevel", required_argument, 0, 'B'},
|
||||
|
|
@ -1112,13 +1115,24 @@ int main(int argc, char** argv)
|
|||
{"help", 0, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
while ((opt = getopt_long(argc, argv, "c12B:CltTSa:LxVvh?r:",
|
||||
while ((opt = getopt_long(argc, argv, "cb:12B:CltTSa:LxVvh?r:",
|
||||
long_options, &option_index)) != EOF) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
print_console = 1;
|
||||
print_defaults = 0;
|
||||
break;
|
||||
case 'b':
|
||||
if (!strcasecmp(optarg, "devmem"))
|
||||
backend_type = CBMEM_DRV_BACKEND_DEVMEM;
|
||||
else if (!strcasecmp(optarg, "sysfs"))
|
||||
backend_type = CBMEM_DRV_BACKEND_SYSFS;
|
||||
else if (!strcasecmp(optarg, "any"))
|
||||
backend_type = CBMEM_DRV_BACKEND_ANY;
|
||||
else
|
||||
die("Unrecognized backend type: '%s'\n", optarg);
|
||||
break;
|
||||
|
||||
case '1':
|
||||
print_console = 1;
|
||||
console_type = CONSOLE_PRINT_LAST;
|
||||
|
|
@ -1194,8 +1208,16 @@ int main(int argc, char** argv)
|
|||
print_usage(argv[0], 1);
|
||||
}
|
||||
|
||||
if (!cbmem_devmem_init(timestamp_id != 0))
|
||||
die("Unable to initialize /dev/mem access to coreboot tables and CBMEM.\n");
|
||||
if (print_hexdump) {
|
||||
debug("Hexdump requested. CBMEM backend force-set to DEVMEM.\n");
|
||||
backend_type = CBMEM_DRV_BACKEND_DEVMEM;
|
||||
}
|
||||
|
||||
if (!cbmem_drv_init(backend_type, timestamp_id != 0)) {
|
||||
if (print_hexdump)
|
||||
fprintf(stderr, "Hexdump is only available on systems with /dev/mem.\n");
|
||||
die("Unable to initialize CBMEM access. Check if you have either /dev/mem access or sysfs CBMEM entries.\n");
|
||||
}
|
||||
|
||||
if (print_console)
|
||||
dump_console(console_type, max_loglevel, print_unknown_logs);
|
||||
|
|
@ -1224,7 +1246,7 @@ int main(int argc, char** argv)
|
|||
if (print_tcpa_log)
|
||||
dump_tpm_log();
|
||||
|
||||
cbmem_devmem_terminate();
|
||||
cbmem_drv_terminate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
95
util/cbmem/cbmem_drv.c
Normal file
95
util/cbmem/cbmem_drv.c
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cbmem_util.h"
|
||||
|
||||
/* "ANY" (0) means no backend is active. */
|
||||
static enum cbmem_drv_backend_type active_backend = CBMEM_DRV_BACKEND_ANY;
|
||||
|
||||
static const char no_backend_error_message[] =
|
||||
"No active/initialized CBMEM backend detected. Terminating.\n";
|
||||
|
||||
static bool init_specific_backend(enum cbmem_drv_backend_type backend, bool writeable)
|
||||
{
|
||||
switch (backend) {
|
||||
case CBMEM_DRV_BACKEND_DEVMEM:
|
||||
if (!cbmem_devmem_init(writeable))
|
||||
return false;
|
||||
debug("Initialized CBMEM backend: devmem\n");
|
||||
break;
|
||||
case CBMEM_DRV_BACKEND_SYSFS:
|
||||
if (!cbmem_sysfs_init())
|
||||
return false;
|
||||
debug("Initialized CBMEM backend: sysfs\n");
|
||||
break;
|
||||
default:
|
||||
die("Unsupported backend selected. Terminating.\n");
|
||||
}
|
||||
|
||||
active_backend = backend;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cbmem_drv_init(enum cbmem_drv_backend_type backend, bool writeable)
|
||||
{
|
||||
if (backend != CBMEM_DRV_BACKEND_ANY)
|
||||
return init_specific_backend(backend, writeable);
|
||||
|
||||
/* First try SYSFS backend as it's more secure. */
|
||||
if (!init_specific_backend(CBMEM_DRV_BACKEND_SYSFS, writeable))
|
||||
return init_specific_backend(CBMEM_DRV_BACKEND_DEVMEM, writeable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cbmem_drv_terminate(void)
|
||||
{
|
||||
/* Only /dev/mem driver requires termination. */
|
||||
if (active_backend == CBMEM_DRV_BACKEND_DEVMEM)
|
||||
cbmem_devmem_terminate();
|
||||
|
||||
active_backend = CBMEM_DRV_BACKEND_ANY;
|
||||
}
|
||||
|
||||
bool cbmem_drv_get_cbmem_entry(uint32_t id, uint8_t **buf_out, size_t *size_out, uint64_t *addr_out)
|
||||
{
|
||||
switch (active_backend) {
|
||||
case CBMEM_DRV_BACKEND_DEVMEM:
|
||||
return cbmem_devmem_get_cbmem_entry(id, buf_out, size_out, addr_out);
|
||||
case CBMEM_DRV_BACKEND_SYSFS:
|
||||
return cbmem_sysfs_get_cbmem_entry(id, buf_out, size_out, addr_out);
|
||||
default:
|
||||
die(no_backend_error_message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cbmem_drv_write_cbmem_entry(uint32_t id, uint8_t *buf, size_t buf_size)
|
||||
{
|
||||
switch (active_backend) {
|
||||
case CBMEM_DRV_BACKEND_DEVMEM:
|
||||
return cbmem_devmem_write_cbmem_entry(id, buf, buf_size);
|
||||
case CBMEM_DRV_BACKEND_SYSFS:
|
||||
return cbmem_sysfs_write_cbmem_entry(id, buf, buf_size);
|
||||
default:
|
||||
die(no_backend_error_message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cbmem_drv_foreach_cbmem_entry(cbmem_iterator_callback cb, void *data, bool with_contents)
|
||||
{
|
||||
switch (active_backend) {
|
||||
case CBMEM_DRV_BACKEND_DEVMEM:
|
||||
cbmem_devmem_foreach_cbmem_entry(cb, data, with_contents);
|
||||
break;
|
||||
case CBMEM_DRV_BACKEND_SYSFS:
|
||||
cbmem_sysfs_foreach_cbmem_entry(cb, data, with_contents);
|
||||
break;
|
||||
default:
|
||||
die(no_backend_error_message);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,6 +46,62 @@ struct cbmem_console {
|
|||
typedef bool (*cbmem_iterator_callback)(const uint32_t id, const uint64_t physical_address,
|
||||
const uint8_t *buf, const size_t size, void *data);
|
||||
|
||||
/* Common CBMEM access API */
|
||||
|
||||
enum cbmem_drv_backend_type {
|
||||
CBMEM_DRV_BACKEND_ANY,
|
||||
CBMEM_DRV_BACKEND_DEVMEM,
|
||||
CBMEM_DRV_BACKEND_SYSFS,
|
||||
};
|
||||
|
||||
/**
|
||||
* Pick and initialize CBMEM driver. Function can either probe for available drivers or initialize only the selected one.
|
||||
*
|
||||
* @param backend preferred CBMEM backend to initialize.
|
||||
* @param writeable initialize CBMEM driver in writeable mode. Required by devmem driver to modify CBMEM.
|
||||
*
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool cbmem_drv_init(enum cbmem_drv_backend_type backend, bool writeable);
|
||||
|
||||
/**
|
||||
* Cleanup and terminate previously initialized CBMEM driver.
|
||||
* **MUST** be called if cbmem_drv_init() succeeded.
|
||||
*/
|
||||
void cbmem_drv_terminate(void);
|
||||
|
||||
/**
|
||||
* Get CBMEM entry as an allocated buffer.
|
||||
*
|
||||
* @param id CBMEM_ID_* value.
|
||||
* @param buf_out return pointer for the allocated buffer containing entry contents.
|
||||
* @param size_out size of returned buffer.
|
||||
* @param addr_out pointer to the output buffer for entry address in physical memory. Optional.
|
||||
*
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool cbmem_drv_get_cbmem_entry(uint32_t id, uint8_t **buf_out, size_t *size_out, uint64_t *addr_out);
|
||||
|
||||
/**
|
||||
* Write provided buffer contents to the CBMEM entry.
|
||||
*
|
||||
* @param id CBMEM_ID_* value.
|
||||
* @param buf pointer to the source buffer.
|
||||
* @param buf_size size of the source buffer.
|
||||
*
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool cbmem_drv_write_cbmem_entry(uint32_t id, uint8_t *buf, size_t buf_size);
|
||||
|
||||
/**
|
||||
* Backend-specific function iterating over CBMEM entries.
|
||||
*
|
||||
* @param cb user callback function to call during iteration.
|
||||
* @param data pointer to the context data for the callback.
|
||||
* @param with_contents tells whether the callback should get NULL (false) or copy of the entry (true).
|
||||
*/
|
||||
void cbmem_drv_foreach_cbmem_entry(cbmem_iterator_callback cb, void *data, bool with_contents);
|
||||
|
||||
/* API for accessing CBMEM via /dev/mem */
|
||||
|
||||
/**
|
||||
|
|
@ -94,3 +150,45 @@ bool cbmem_devmem_write_cbmem_entry(uint32_t id, uint8_t *buf, size_t buf_size);
|
|||
*/
|
||||
void cbmem_devmem_foreach_cbmem_entry(cbmem_iterator_callback cb, void *data,
|
||||
bool with_contents);
|
||||
|
||||
/* API for accessing CBMEM via sysfs entries */
|
||||
|
||||
/**
|
||||
* Initialize the sysfs driver.
|
||||
*
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool cbmem_sysfs_init(void);
|
||||
|
||||
/**
|
||||
* Get CBMEM entry as an allocated buffer.
|
||||
*
|
||||
* @param id CBMEM_ID_* value.
|
||||
* @param buf_out return pointer for the allocated buffer containing entry contents.
|
||||
* @param size_out size of returned buffer.
|
||||
* @param addr_out pointer to the output buffer for entry address in physical memory. Optional.
|
||||
*
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool cbmem_sysfs_get_cbmem_entry(uint32_t id, uint8_t **buf_out, size_t *size_out, uint64_t *addr_out);
|
||||
|
||||
/**
|
||||
* Write provided buffer contents to the CBMEM entry.
|
||||
*
|
||||
* @param id CBMEM_ID_* value.
|
||||
* @param buf pointer to the source buffer.
|
||||
* @param buf_size size of the source buffer.
|
||||
*
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool cbmem_sysfs_write_cbmem_entry(uint32_t id, uint8_t *buf, size_t buf_size);
|
||||
|
||||
/**
|
||||
* Backend-specific function iterating over CBMEM entries.
|
||||
*
|
||||
* @param cb user callback function to call during iteration.
|
||||
* @param data pointer to the context data for the callback.
|
||||
* @param with_contents tells whether the callback should get NULL (false) or copy of the entry (true).
|
||||
*/
|
||||
void cbmem_sysfs_foreach_cbmem_entry(cbmem_iterator_callback cb, void *data,
|
||||
bool with_contents);
|
||||
|
|
|
|||
266
util/cbmem/sysfs_drv.c
Normal file
266
util/cbmem/sysfs_drv.c
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <commonlib/bsd/cbmem_id.h>
|
||||
#include <commonlib/bsd/ipchksum.h>
|
||||
#include <commonlib/coreboot_tables.h>
|
||||
#include <commonlib/helpers.h>
|
||||
#include <commonlib/timestamp_serialized.h>
|
||||
#include <commonlib/tpm_log_serialized.h>
|
||||
|
||||
#include "cbmem_util.h"
|
||||
|
||||
#define CBMEM_SYSFS_BASE_DIR "/sys/bus/coreboot/devices/"
|
||||
#define CBMEM_SYSFS_ENTRY_DIR_NAME_PREFIX "cbmem-"
|
||||
#define CBMEM_SYSFS_ENTRY_DIR_NAME_SCN_FMT CBMEM_SYSFS_ENTRY_DIR_NAME_PREFIX "%8" SCNx32
|
||||
#define CBMEM_SYSFS_ENTRY_DIR_NAME_PRI_FMT CBMEM_SYSFS_ENTRY_DIR_NAME_PREFIX "%08" PRIx32
|
||||
#define CBMEM_SYSFS_ENTRY_DIR_PATH_PRI_FMT \
|
||||
CBMEM_SYSFS_BASE_DIR CBMEM_SYSFS_ENTRY_DIR_NAME_PRI_FMT
|
||||
|
||||
enum cbmem_sysfs_path_type {
|
||||
CBMEM_SYSFS_PATH_BASE,
|
||||
CBMEM_SYSFS_PATH_ADDRESS,
|
||||
CBMEM_SYSFS_PATH_MEM,
|
||||
CBMEM_SYSFS_PATH_SIZE,
|
||||
};
|
||||
|
||||
/* Maximum filesystem path length. */
|
||||
#define PATH_MAX_LEN 1024
|
||||
|
||||
/**
|
||||
* Create new path for CBMEM entry of a requested type. free() path_out after use.
|
||||
*
|
||||
* @param id CBMEM_ID_* value.
|
||||
* @param t path type.
|
||||
* @param path_out output pointer to created path.
|
||||
*/
|
||||
static void new_sysfs_path(uint32_t id, enum cbmem_sysfs_path_type t, char *path_out, size_t path_out_max_size)
|
||||
{
|
||||
const char *const type2str[] = {
|
||||
[CBMEM_SYSFS_PATH_BASE] = "",
|
||||
[CBMEM_SYSFS_PATH_ADDRESS] = "/address",
|
||||
[CBMEM_SYSFS_PATH_MEM] = "/mem",
|
||||
[CBMEM_SYSFS_PATH_SIZE] = "/size"
|
||||
};
|
||||
|
||||
if (t >= ARRAY_SIZE(type2str))
|
||||
die("Incorrect path type requested: %d\n", t);
|
||||
|
||||
if (snprintf(path_out, path_out_max_size, CBMEM_SYSFS_ENTRY_DIR_PATH_PRI_FMT "%s", id,
|
||||
type2str[t]) <= 0)
|
||||
die("Unable to create sysfs path string for coreboot table: %#" PRIx32
|
||||
". Path type: %d. Error: %s\n",
|
||||
id, t, strerror(errno));
|
||||
}
|
||||
|
||||
bool cbmem_sysfs_init(void)
|
||||
{
|
||||
char path[PATH_MAX_LEN];
|
||||
|
||||
for (enum cbmem_sysfs_path_type t = CBMEM_SYSFS_PATH_ADDRESS; t <= CBMEM_SYSFS_PATH_SIZE; ++t) {
|
||||
new_sysfs_path(CBMEM_ID_CBTABLE, t, path, sizeof(path));
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
debug("Unable to open path %s for reading. Error: %s\n", path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t test_byte = 0;
|
||||
if (fread(&test_byte, 1, 1, f) != 1) {
|
||||
debug("Unable to read data from %s. Error: %s\n", path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cbmem_sysfs_probe_cbmem_entry(uint32_t id, uint64_t *addr_out, size_t *size_out)
|
||||
{
|
||||
char path[PATH_MAX_LEN];
|
||||
|
||||
new_sysfs_path(id, CBMEM_SYSFS_PATH_ADDRESS, path, sizeof(path));
|
||||
|
||||
FILE *address_file = fopen(path, "rb");
|
||||
if (!address_file) {
|
||||
debug("Unable to access CBMEM entry id: %#" PRIx32
|
||||
" address file at %s. Error: %s\n",
|
||||
id, path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fscanf(address_file, "%" SCNx64, addr_out) != 1) {
|
||||
debug("Read from %s failed.\n", path);
|
||||
fclose(address_file);
|
||||
return false;
|
||||
}
|
||||
fclose(address_file);
|
||||
|
||||
new_sysfs_path(id, CBMEM_SYSFS_PATH_SIZE, path, sizeof(path));
|
||||
|
||||
FILE *size_file = fopen(path, "rb");
|
||||
if (!size_file) {
|
||||
debug("Unable to access CBMEM entry id: %#" PRIx32
|
||||
" size file at %s. Error: %s\n",
|
||||
id, path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fscanf(size_file, "%zx", size_out) != 1) {
|
||||
debug("Read from %s failed.\n", path);
|
||||
fclose(size_file);
|
||||
*addr_out = 0;
|
||||
return false;
|
||||
}
|
||||
fclose(size_file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void fetch_cbmem_entry(const uint32_t id, const size_t size, uint8_t **buf_out)
|
||||
{
|
||||
char path[PATH_MAX_LEN];
|
||||
|
||||
new_sysfs_path(id, CBMEM_SYSFS_PATH_MEM, path, sizeof(path));
|
||||
|
||||
FILE *mem_file = fopen(path, "rb");
|
||||
if (!mem_file)
|
||||
die("Unable to open mem file for CBMEM entry id: %#" PRIx32
|
||||
" at %s. Error: %s\n",
|
||||
id, path, strerror(errno));
|
||||
|
||||
*buf_out = malloc(size);
|
||||
if (!buf_out)
|
||||
die("Unable to allocate memory for CBMEM entry id: %#" PRIx32
|
||||
" of size: %zuB.\n",
|
||||
id, size);
|
||||
|
||||
if (fread(*buf_out, 1, size, mem_file) != size)
|
||||
die("Unable to correctly read memory of CBMEM entry id: %#" PRIx32
|
||||
" at %s. Error: %s\n",
|
||||
id, path, strerror(errno));
|
||||
|
||||
fclose(mem_file);
|
||||
}
|
||||
|
||||
bool cbmem_sysfs_get_cbmem_entry(uint32_t id, uint8_t **buf_out, size_t *size_out, uint64_t *addr_out)
|
||||
{
|
||||
uint64_t addr;
|
||||
size_t size;
|
||||
|
||||
if (!cbmem_sysfs_probe_cbmem_entry(id, &addr, &size)) {
|
||||
debug("CBMEM entry id: %#" PRIx32 " not found.\n", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
fetch_cbmem_entry(id, size, buf_out);
|
||||
if (size_out)
|
||||
*size_out = size;
|
||||
if (addr_out)
|
||||
*addr_out = addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cbmem_sysfs_write_cbmem_entry(uint32_t id, uint8_t *buf, size_t buf_size)
|
||||
{
|
||||
char path[PATH_MAX_LEN];
|
||||
uint64_t addr;
|
||||
size_t size;
|
||||
|
||||
if (!cbmem_sysfs_probe_cbmem_entry(id, &addr, &size)) {
|
||||
debug("Unable to find CBMEM entry id: %#" PRIx32 "\n", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buf_size > size)
|
||||
die("Attempting to write %zu bytes to CBMEM entry id: %#" PRIx32
|
||||
" of %zu bytes. Operation not possible.\n",
|
||||
buf_size, id, size);
|
||||
|
||||
new_sysfs_path(id, CBMEM_SYSFS_PATH_MEM, path, sizeof(path));
|
||||
|
||||
FILE *mem_file = fopen(path, "rb+");
|
||||
if (!mem_file)
|
||||
die("Unable to open mem file for CBMEM entry id: %#" PRIx32
|
||||
" at %s. Error: %s\n",
|
||||
id, path, strerror(errno));
|
||||
|
||||
if (fwrite(buf, 1, buf_size, mem_file) != buf_size)
|
||||
die("Unable to correctly write memory of CBMEM entry id: %#" PRIx32
|
||||
" at %s. Error: %s\n",
|
||||
id, path, strerror(errno));
|
||||
|
||||
fclose(mem_file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cbmem_sysfs_foreach_cbmem_entry(cbmem_iterator_callback cb, void *data, bool with_contents)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
if ((dir = opendir(CBMEM_SYSFS_BASE_DIR)) == NULL)
|
||||
die("Unable to open directory containing CBMEM entries. Path: %s. Error: %s\n",
|
||||
CBMEM_SYSFS_BASE_DIR, strerror(errno));
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
debug("Checking path: %s%s\n", CBMEM_SYSFS_BASE_DIR, entry->d_name);
|
||||
/* Check directory name prefix */
|
||||
if (strncmp(entry->d_name, CBMEM_SYSFS_ENTRY_DIR_NAME_PREFIX,
|
||||
strlen(CBMEM_SYSFS_ENTRY_DIR_NAME_PREFIX)) != 0)
|
||||
continue;
|
||||
|
||||
uint32_t id = 0;
|
||||
/* Extract CBMEM entry id from the directory name.
|
||||
If it fails, then directory is not a correct entry. */
|
||||
if (sscanf(entry->d_name, CBMEM_SYSFS_ENTRY_DIR_NAME_SCN_FMT, &id) != 1)
|
||||
continue;
|
||||
|
||||
uint64_t addr = 0;
|
||||
uint64_t size = 0;
|
||||
uint8_t *buf;
|
||||
|
||||
/* If entry was not found or previously matched directory
|
||||
does not contain necessary files, then omit the entry. */
|
||||
if (!cbmem_sysfs_probe_cbmem_entry(id, &addr, &size))
|
||||
continue;
|
||||
|
||||
if (with_contents)
|
||||
fetch_cbmem_entry(id, size, &buf);
|
||||
|
||||
debug("Invoking callback on %s%s\n", CBMEM_SYSFS_BASE_DIR, entry->d_name);
|
||||
|
||||
const bool res = cb(id, addr, buf, size, data);
|
||||
|
||||
if (with_contents)
|
||||
free(buf);
|
||||
|
||||
/* Finish iteration if callback requested it. */
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue