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:
Jakub Czapiga 2025-04-25 13:58:49 +00:00 committed by Matt DeVillier
commit 7095c99a87
5 changed files with 500 additions and 19 deletions

View file

@ -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)

View file

@ -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
View 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);
}
}

View file

@ -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
View 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);
}