Jakub "Kuba" Czapiga has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/86606?usp=email )
Change subject: util/cbmem: Add support for CBMEM in sysfs ......................................................................
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.
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 --- M util/cbmem/cbmem.c A util/cbmem/cbmem_drv.c M util/cbmem/cbmem_util.h M util/cbmem/common.c M util/cbmem/devmem_drv.c A util/cbmem/sysfs_drv.c 6 files changed, 591 insertions(+), 30 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/06/86606/1
diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c index 1d8c9ac..6a4bb44 100644 --- a/util/cbmem/cbmem.c +++ b/util/cbmem/cbmem.c @@ -144,13 +144,13 @@ struct lb_tsc_info *tsc_info; size_t size;
- if (cbmem_devmem_get_lb_table_entry(LB_TAG_TSC_INFO, (uint8_t **)&tsc_info, &size)) { + if (cbmem_drv_get_lb_table_entry(LB_TAG_TSC_INFO, (uint8_t **)&tsc_info, &size)) { fprintf(stderr, "Failed to get coreboot table TSC INFO entry.\n"); goto end; }
tsc_freq_khz = tsc_info->freq_khz; - cbmem_devmem_free_entry((uint8_t *)tsc_info); + cbmem_drv_free_entry((uint8_t *)tsc_info); end: already_checked = true; return tsc_freq_khz; @@ -316,7 +316,7 @@ uint8_t *tst_buf; size_t tst_buf_size;
- if (cbmem_devmem_get_lb_table_entry(LB_TAG_TIMESTAMPS, &tst_buf, &tst_buf_size)) + if (cbmem_drv_get_lb_table_entry(LB_TAG_TIMESTAMPS, &tst_buf, &tst_buf_size)) return;
tst_p = (const struct timestamp_table *)tst_buf; @@ -329,7 +329,7 @@
sorted_tst_p = malloc(size + sizeof(struct timestamp_entry)); if (!sorted_tst_p) { - cbmem_devmem_free_entry(tst_buf); + cbmem_drv_free_entry(tst_buf); die("Failed to allocate memory"); } memcpy(sorted_tst_p, tst_p, size); @@ -410,18 +410,18 @@ }
free(sorted_tst_p); - cbmem_devmem_free_entry(tst_buf); + cbmem_drv_free_entry(tst_buf); }
/* add a timestamp entry */ static void timestamp_add_now(uint32_t timestamp_id) { struct timestamp_table *tst_p; - struct mapping timestamp_mapping; + struct cbmem_drv_data drv_data; uint8_t *tst_buf; size_t tst_buf_size;
- if (cbmem_devmem_map_lb_table_entry_rw(LB_TAG_TIMESTAMPS, ×tamp_mapping, &tst_buf, + if (cbmem_drv_map_cbmem_entry_rw(CBMEM_ID_TIMESTAMP, &drv_data, &tst_buf, &tst_buf_size)) return;
@@ -441,7 +441,7 @@ tst_p->num_entries += 1; }
- cbmem_devmem_unmap_entry(×tamp_mapping); + cbmem_drv_unmap_entry(&drv_data, tst_buf, tst_buf_size); }
static bool can_print(const uint8_t *data, size_t len) @@ -662,7 +662,7 @@ uint8_t *tclt_buf; size_t tclt_buf_size;
- if (cbmem_devmem_get_lb_table_entry(LB_TAG_TPM_CB_LOG, &tclt_buf, &tclt_buf_size)) + if (cbmem_drv_get_lb_table_entry(LB_TAG_TPM_CB_LOG, &tclt_buf, &tclt_buf_size)) return;
tclt_p = (const struct tpm_cb_log_table *)tclt_buf; @@ -677,7 +677,7 @@ printf(" %s [%s]\n", tce->digest_type, tce->name); }
- cbmem_devmem_free_entry(tclt_buf); + cbmem_drv_free_entry(tclt_buf); }
static void dump_tpm_log(void) @@ -685,10 +685,10 @@ uint8_t *buf; size_t size;
- if (!cbmem_devmem_get_cbmem_entry(CBMEM_ID_TCPA_TCG_LOG, &buf, &size) || - !cbmem_devmem_get_cbmem_entry(CBMEM_ID_TPM2_TCG_LOG, &buf, &size)) { + if (!cbmem_drv_get_cbmem_entry(CBMEM_ID_TCPA_TCG_LOG, &buf, &size) || + !cbmem_drv_get_cbmem_entry(CBMEM_ID_TPM2_TCG_LOG, &buf, &size)) { dump_tpm_std_log(buf, size); - cbmem_devmem_free_entry(buf); + cbmem_drv_free_entry(buf); } else dump_tpm_cb_log(); } @@ -731,7 +731,7 @@ uint8_t *console_buf; size_t console_buf_size;
- if (cbmem_devmem_get_lb_table_entry(LB_TAG_CBMEM_CONSOLE, &console_buf, + if (cbmem_drv_get_lb_table_entry(LB_TAG_CBMEM_CONSOLE, &console_buf, &console_buf_size)) return;
@@ -746,7 +746,7 @@ console_c = malloc(size + 1); if (!console_c) { fprintf(stderr, "Not enough memory for console.\n"); - cbmem_devmem_free_entry(console_buf); + cbmem_drv_free_entry(console_buf); exit(1); } console_c[size] = '\0'; @@ -835,12 +835,12 @@ printf(BIOS_LOG_ESCAPE_RESET);
free(console_c); - cbmem_devmem_free_entry(console_buf); + cbmem_drv_free_entry(console_buf); }
static void dump_cbmem_hex(void) { - cbmem_devmem_hexdump_cbmem(); + cbmem_drv_hexdump_cbmem(); }
static void rawdump(uint8_t *buf, size_t size) @@ -854,19 +854,19 @@ uint8_t *buf; size_t size;
- if (cbmem_devmem_get_cbmem_entry(id, &buf, &size)) { + if (cbmem_drv_get_cbmem_entry(id, &buf, &size)) { fprintf(stderr, "cbmem entry id: %#x not found.\n", id); return; }
rawdump(buf, size);
- cbmem_devmem_free_entry(buf); + cbmem_drv_free_entry(buf); }
static void dump_cbmem_toc(void) { - cbmem_devmem_toc(); + cbmem_drv_toc(); }
#define COVERAGE_MAGIC 0x584d4153 @@ -905,7 +905,7 @@ ptrdiff_t phys_offset; #define phys_to_virt(x) ((void *)((uintptr_t)(x) + phys_offset))
- if (cbmem_devmem_get_cbmem_entry(CBMEM_ID_COVERAGE, &coverage, &size)) { + if (cbmem_drv_get_cbmem_entry(CBMEM_ID_COVERAGE, &coverage, &size)) { fprintf(stderr, "No coverage information found\n"); return; } @@ -916,7 +916,7 @@ fprintf(stderr, "Incorrect starting coverage magic header. Expected: %#x, Got: %#x.", COVERAGE_MAGIC, file->magic); - cbmem_devmem_free_entry(coverage); + cbmem_drv_free_entry(coverage); return; }
@@ -956,7 +956,7 @@ else file = NULL; } - cbmem_devmem_free_entry(coverage); + cbmem_drv_free_entry(coverage); #undef phys_to_virt }
@@ -1116,7 +1116,7 @@ print_usage(argv[0], 1); }
- if (cbmem_devmem_init(!!timestamp_id)) + if (cbmem_drv_init(!!timestamp_id)) return -1;
if (print_console) @@ -1146,7 +1146,7 @@ if (print_tcpa_log) dump_tpm_log();
- cbmem_devmem_terminate(); + cbmem_drv_terminate();
return 0; } diff --git a/util/cbmem/cbmem_drv.c b/util/cbmem/cbmem_drv.c new file mode 100644 index 0000000..8e882b9 --- /dev/null +++ b/util/cbmem/cbmem_drv.c @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <inttypes.h> +#include <stdbool.h> + +#include "cbmem_util.h" +#include "commonlib/bsd/cbmem_id.h" +#include "commonlib/coreboot_tables.h" + +enum cbmem_drv_type { + CBMEM_DRV_UNKNOWN = 0, + CBMEM_DRV_DEVMEM = 1, + CBMEM_DRV_SYSFS = 2, +} drv_type; + +int cbmem_drv_init(bool writeable) +{ + if (cbmem_sysfs_probe(cbmem_util_verbose) == 0) { + debug("Selected driver: sysfs\n"); + drv_type = CBMEM_DRV_SYSFS; + return 0; + } + + const int ret = cbmem_devmem_init(writeable); + if (ret == 0) { + debug("Selected driver: devmem\n"); + drv_type = CBMEM_DRV_DEVMEM; + } else { + fprintf(stderr, + "No suitable access methods found. /dev/mem and /systs entries missing or inaccessible.\n"); + drv_type = CBMEM_DRV_UNKNOWN; + } + return ret; +} + +void cbmem_drv_terminate(void) +{ + if (drv_type == CBMEM_DRV_DEVMEM) + cbmem_devmem_terminate(); + drv_type = CBMEM_DRV_UNKNOWN; +} + +int cbmem_drv_get_cbmem_entry(uint32_t id,uint8_t **buf_out, size_t *size_out) +{ + switch (drv_type) { + case CBMEM_DRV_UNKNOWN: + debug("%s() called but no suitable driver found.\n", __func__); + return -1; + case CBMEM_DRV_DEVMEM: + return cbmem_devmem_get_cbmem_entry(id, buf_out, size_out); + case CBMEM_DRV_SYSFS: + return cbmem_sysfs_get_cbmem_entry(id, buf_out, size_out); + } + + return -1; +} + +int cbmem_drv_get_lb_table_entry(uint32_t tag, uint8_t **buf_out, size_t *size_out) +{ + switch (drv_type) { + case CBMEM_DRV_UNKNOWN: + debug("%s() called but no suitable driver found.\n", __func__); + return -1; + case CBMEM_DRV_DEVMEM: + return cbmem_devmem_get_lb_table_entry(tag, buf_out, size_out); + case CBMEM_DRV_SYSFS: + return cbmem_sysfs_get_lb_table_entry(tag, buf_out, size_out); + } + + return -1; +} + +void cbmem_drv_free_entry(uint8_t *buf) +{ + switch (drv_type) { + case CBMEM_DRV_UNKNOWN: + debug("%s() called but no suitable driver found.\n", __func__); + break; + case CBMEM_DRV_DEVMEM: + cbmem_devmem_free_entry(buf); + break; + case CBMEM_DRV_SYSFS: + cbmem_sysfs_free_entry(buf); + break; + } +} + +int cbmem_drv_map_cbmem_entry_rw(uint32_t id, struct cbmem_drv_data *data, uint8_t **buf_out, size_t *size_out) +{ + switch (drv_type) { + case CBMEM_DRV_UNKNOWN: + debug("%s() called but no suitable driver found.\n", __func__); + return -1; + case CBMEM_DRV_DEVMEM: + if (id == CBMEM_ID_TIMESTAMP) + return cbmem_devmem_map_lb_table_entry_rw(LB_TAG_TIMESTAMPS, &data->mapping, buf_out, size_out); + fprintf(stderr, "/dev/mem driver does not support mapping anything except CBMEM_ID_TIMESTAMP.\n"); + return -1; + case CBMEM_DRV_SYSFS: + return cbmem_sysfs_map_cbmem_entry_rw(id, &data->file, buf_out, size_out); + } +} + +void cbmem_drv_unmap_entry(struct cbmem_drv_data *data, uint8_t *buf, size_t size) +{ + switch (drv_type) { + case CBMEM_DRV_UNKNOWN: + debug("%s() called but no suitable driver found.\n", __func__); + break; + case CBMEM_DRV_DEVMEM: + cbmem_devmem_unmap_entry(&data->mapping); + break; + case CBMEM_DRV_SYSFS: + cbmem_sysfs_unmap_entry(data->file, buf, size); + break; + } +} + +void cbmem_drv_hexdump_cbmem(void) +{ + switch (drv_type) { + case CBMEM_DRV_UNKNOWN: + debug("%s() called but no suitable driver found.\n", __func__); + break; + case CBMEM_DRV_DEVMEM: + cbmem_devmem_hexdump_cbmem(); + break; + case CBMEM_DRV_SYSFS: + cbmem_sysfs_hexdump_cbmem(); + break; + } +} + +void cbmem_drv_toc(void) +{ + switch (drv_type) { + case CBMEM_DRV_UNKNOWN: + debug("%s() called but no suitable driver found.\n", __func__); + break; + case CBMEM_DRV_DEVMEM: + cbmem_devmem_toc(); + break; + case CBMEM_DRV_SYSFS: + cbmem_sysfs_toc(); + break; + } +} diff --git a/util/cbmem/cbmem_util.h b/util/cbmem/cbmem_util.h index f82b10b..b3c9124e 100644 --- a/util/cbmem/cbmem_util.h +++ b/util/cbmem/cbmem_util.h @@ -26,7 +26,14 @@ /* Returns 0 on success, < 0 on error. mapping is cleared if successful. */ int unmap_memory(struct mapping *mapping);
-void hexdump(const struct mapping *mapping, const uint8_t *buf, const int length); +/** + * Hexdump buffer. + * + * @param start_addr starting address for hexdump during printf. Does not affect buffer. + * @param buf input buffer. + * @param length buffer size. + */ +void hexdump(const unsigned long long start_addr, const uint8_t *buf, const int length);
struct cbmem_id_to_name { uint32_t id; @@ -57,6 +64,84 @@ */ uint32_t lb_tag_to_cbmem_id(uint32_t tag, bool *ok_out);
+/* Common driver API. Use this instead of using specific drivers manually. */ + +struct cbmem_drv_data { + union { + struct mapping mapping; /// cbmem_devmem driver + FILE *file; /// cbmem_sysfs driver + }; +}; + +/** + * Find and initialize CBMEM driver. First sysfs driver is tested. devmem driver is used as a fallback. + * + * @param writeable request a write access to CBMEM sections. + * + * @returns zero on success. + */ +int cbmem_drv_init(bool writeable); + +/** + * Terminate and cleanup the driver. **MUST** be used 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 pointer to obtain allocated entry buffer. + * @param size_out size of returned buffer. + * + * @returns zero on success. + */ +int cbmem_drv_get_cbmem_entry(uint32_t id,uint8_t **buf_out, size_t *size_out); + +/** + * Get lb_table/cb_table entry as an allocated buffer. + * + * @param tag LB_TAG_* value. + * @param buf_out pointer to obtain allocated entry buffer. + * @param size_out size of returned buffer. + * + * @returns zero on success. + */ +int cbmem_drv_get_lb_table_entry(uint32_t tag, uint8_t **buf_out, size_t *size_out); + +/** + * Free buffer allocated by cbmem_drv_get*_entry(). + */ +void cbmem_drv_free_entry(uint8_t *buf); + +/** + * Map CBMEM entry in RW mode. + * + * @param id CBMEM_ID_* value. + * @param data the driver call context data. A separate structure is needed for every new call if not unmapped first. + * @param buf_out output pointer to the mapped cbmem entry. + * @param size_out output buffer size. + * + * @returns zero on success. + */ +int cbmem_drv_map_cbmem_entry_rw(uint32_t id, struct cbmem_drv_data *data, uint8_t **buf_out, size_t *size_out); + +/** + * Unmap entry mapped by cbmem_drv_map_cbmem_entry_rw(). + * **MUST** be called if cbmem_drv_map_cbmem_entry_rw() succeeded. + * + * @param data the driver call context data. + * @param buf the mapped buffer. + * @param size size of the mapped buffer. + */ +void cbmem_drv_unmap_entry(struct cbmem_drv_data *data, uint8_t *buf, size_t size); + +/* Hexdump all cbmem entries. */ +void cbmem_drv_hexdump_cbmem(void); + +/* Print CBMEM Table of Contents */ +void cbmem_drv_toc(void); + /* API for accessing CBMEM via /dev/mem */
/** @@ -125,3 +210,34 @@
/* Print CBMEM Table of Contents */ void cbmem_devmem_toc(void); + +/* API for accessing CBMEM via sysfs entries */ + +/** + * Check if cbmem in sysfs is supported on the device. + * No driver initialization is required. + * + * @returns zero on success. + */ +int cbmem_sysfs_probe(bool verbose); + +/* @see cbmem_devmem_get_cbmem_entry() */ +int cbmem_sysfs_get_cbmem_entry(uint32_t id, uint8_t **buf_out, size_t* size_out); + +/* @see cbmem_devmem_get_lb_table_entry() */ +int cbmem_sysfs_get_lb_table_entry(uint32_t tag, uint8_t **buf_out, size_t *size_out); + +/* Free buffer returned by cbmem_sysfs_get_*_entry() */ +void cbmem_sysfs_free_entry(uint8_t *buf); + +/* @see cbmem_devmem_map_cbmem_entry_rw() */ +int cbmem_sysfs_map_cbmem_entry_rw(uint32_t id, FILE **file_out, uint8_t **buf_out, + size_t *size_out); + +void cbmem_sysfs_unmap_entry(FILE *file, uint8_t *buf, size_t size); + +/* Hexdump all cbmem entries. */ +void cbmem_sysfs_hexdump_cbmem(void); + +/* Print CBMEM Table of Contents */ +void cbmem_sysfs_toc(void); diff --git a/util/cbmem/common.c b/util/cbmem/common.c index 46d96b5..55bc156 100644 --- a/util/cbmem/common.c +++ b/util/cbmem/common.c @@ -12,7 +12,7 @@
int cbmem_util_verbose;
-void hexdump(const struct mapping *mapping, const uint8_t *buf, const int length) +void hexdump(const unsigned long long start_addr, const uint8_t *buf, const int length) { int i; int all_zero = 0; @@ -29,7 +29,7 @@ }
if (all_zero < 2) { - printf("%08llx:", mapping->phys + i); + printf("%08llx:", start_addr + i); for (j = 0; j < 16; j++) printf(" %02x", buf[i + j]); printf(" "); diff --git a/util/cbmem/devmem_drv.c b/util/cbmem/devmem_drv.c index 25b9154..0e2734a 100644 --- a/util/cbmem/devmem_drv.c +++ b/util/cbmem/devmem_drv.c @@ -564,7 +564,7 @@ const uint32_t id = lb_tag_to_cbmem_id(tag, &id_found);
if (id_found) { - debug("lbtable/cbtable tag: %d. Found CBMEM ID equivalent: %#x\n.", tag, id); + debug("lbtable/cbtable tag: %d. Found CBMEM ID equivalent: %#x.\n", tag, id); return cbmem_devmem_get_cbmem_entry(id, buf_out, size_out); }
@@ -699,7 +699,7 @@ fprintf(stderr, "Unable to map cbmem memory for hexdump.\n"); return -1; } - hexdump(&cbmem_mapping, buf, size); + hexdump(cbmem_mapping.phys, buf, size); unmap_memory(&cbmem_mapping); return 0; } diff --git a/util/cbmem/sysfs_drv.c b/util/cbmem/sysfs_drv.c new file mode 100644 index 0000000..4a025d9 --- /dev/null +++ b/util/cbmem/sysfs_drv.c @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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_ENTRY_DIR_PATH_FMT "/sys/bus/coreboot/devices/cbmem-%x" +#define CBMEM_SYSFS_ENTRY_MEM_PATH_FMT CBMEM_SYSFS_ENTRY_DIR_PATH_FMT "/mem" +#define CBMEM_SYSFS_ENTRY_SIZE_PATH_FMT CBMEM_SYSFS_ENTRY_DIR_PATH_FMT "/size" + +static int new_cbmem_paths(uint32_t id, char **mem_path_out, char **size_path_out) +{ + if (asprintf(mem_path_out, CBMEM_SYSFS_ENTRY_MEM_PATH_FMT, id) <= 0) { + *mem_path_out = NULL; + return -1; + } + if (asprintf(size_path_out, CBMEM_SYSFS_ENTRY_SIZE_PATH_FMT, id) <= 0) { + if (*mem_path_out) { + free(*mem_path_out); + *mem_path_out = NULL; + } + *size_path_out = NULL; + return -1; + } + return 0; +} + +static int check_path(const char *path, bool verbose) +{ + struct stat st; + + if (stat(path, &st)) { + if (verbose) + fprintf(stderr, "Failed to stat file: %s. Error: %s\n", path, + strerror(errno)); + return -1; + } + + if (S_ISDIR(st.st_mode)) { + if (verbose) + fprintf(stderr, + "CBMEM sysfs structure is incorrect. Path %s expected to be a readable file.\n", + path); + return -1; + } + + if (access(path, R_OK)) { + if (verbose) + fprintf(stderr, "No read permissions for file: %s. Error: %s\n", path, + strerror(errno)); + return -1; + } + + return 0; +} + +static int check_paths(const char *mem_path, const char *size_path, bool verbose) +{ + const int ret = check_path(mem_path, verbose); + if (check_path(size_path, verbose)) + return -1; + return ret; +} + +int cbmem_sysfs_probe(bool verbose) +{ + int ret = 0; + char *cbtable_mem_path = NULL; + char *cbtable_size_path = NULL; + + /* cbtable/lbtable should always exist */ + if (new_cbmem_paths(CBMEM_ID_CBTABLE, &cbtable_mem_path, &cbtable_size_path)) { + if (verbose) + fprintf(stderr, "Failed to allocate cbmem paths.\n"); + return -1; + } + + if (check_paths(cbtable_mem_path, cbtable_size_path, verbose)) + ret = -1; + + free(cbtable_size_path); + free(cbtable_mem_path); + return ret; +} + +static int read_cbmem_entry(uint32_t id, FILE **file_out, uint8_t **buf_out, size_t *size_out) +{ + int ret = -1; + char *mem_path = NULL; + char *size_path = NULL; + + if (new_cbmem_paths(id, &mem_path, &size_path)) { + fprintf(stderr, "Failed to allocate cbmem paths.\n"); + return -1; + } + + if (check_paths(mem_path, size_path, true)) { + goto end; + } + + FILE *size_file = fopen(size_path, "r"); + if (!size_file) { + fprintf(stderr, "Unable to open CBMEM entry size file: %s\n", size_path); + goto end; + } + + uint32_t entry_size = 0; + if (fscanf(size_file, "%i", &entry_size) != 1) { + fprintf(stderr, "Failed to parse cbmem entry size from path: %s\n", size_path); + fclose(size_file); + goto end; + } + fclose(size_file); + + *file_out = fopen(mem_path, "rb+"); + if (!file_out) { + fprintf(stderr, "Unable to open CBMEM entry mem file: %s\n", mem_path); + goto end; + } + + *buf_out = calloc(1, entry_size); + if (!*buf_out) { + fprintf(stderr, "Failed to allocate buffer for CBMEM entry %#x.\n", id); + goto end; + } + + fread(*buf_out, entry_size, 1, *file_out); + + if (ferror(*file_out)) { + fprintf(stderr, "Failed to read CBMEM entry %#x contents from %s.\n", id, + mem_path); + fclose(*file_out); + free(*buf_out); + *buf_out = NULL; + goto end; + } + + *size_out = entry_size; + ret = 0; +end: + free(mem_path); + free(size_path); + return ret; +} + +int cbmem_sysfs_get_cbmem_entry(uint32_t id, uint8_t **buf_out, size_t *size_out) +{ + FILE *f = NULL; + + if (read_cbmem_entry(id, &f, buf_out, size_out)) + return -1; + + fclose(f); + return 0; +} + +int cbmem_sysfs_get_lb_table_entry(uint32_t tag, uint8_t **buf_out, size_t *size_out) +{ + uint8_t *cbtable; + size_t size; + bool id_found = false; + const uint32_t id = lb_tag_to_cbmem_id(tag, &id_found); + + if (id_found) { + debug("lbtable/cbtable tag: %d. Found CBMEM ID equivalent: %#x.\n", tag, id); + return cbmem_sysfs_get_cbmem_entry(id, buf_out, size_out); + } + + if (cbmem_sysfs_get_cbmem_entry(CBMEM_ID_CBTABLE, &cbtable, &size)) { + fprintf(stderr, "Unable to find coreboot table CBMEM entry.\n"); + return -1; + } + + const struct lb_record *lbr_p; + bool tag_found = false; + for (size_t i = 0; i < size; i += lbr_p->size) { + lbr_p = (const struct lb_record *)(&cbtable[i]); + if (lbr_p->tag == tag) { + tag_found = true; + break; + } + } + + if (!tag_found) { + fprintf(stderr, "coreboot table entry %#x not found.\n", tag); + return -1; + } + + debug("coreboot table entry %#x found.\n", tag); + + *buf_out = malloc(lbr_p->size); + if (!*buf_out) { + fprintf(stderr, "Unable to allocate memory for coreboot table entry %#x", tag); + return -1; + } + *size_out = lbr_p->size; + memcpy(*buf_out, lbr_p, lbr_p->size); + cbmem_sysfs_free_entry(cbtable); + + return 0; +} + +void cbmem_sysfs_free_entry(uint8_t *buf) +{ + free(buf); +} + +int cbmem_sysfs_map_cbmem_entry_rw(uint32_t id, FILE **file_out, uint8_t **buf_out, size_t *size_out) +{ + return read_cbmem_entry(id, file_out, buf_out, size_out); +} + +void cbmem_sysfs_unmap_entry(FILE *file, uint8_t *buf, size_t size) +{ + fseek(file, 0, SEEK_SET); + fwrite(buf, size, 1, file); + if (ferror(file)) + fprintf(stderr, "Failed to write back. Error: %s\n", strerror(errno)); + fclose(file); +} + +static void foreach_cbmem_entry(void (*fn)(const struct lb_cbmem_entry *lbe, int idx)) +{ + uint8_t *cbtable; + size_t size; + if (cbmem_sysfs_get_cbmem_entry(CBMEM_ID_CBTABLE, &cbtable, &size)) { + fprintf(stderr, "Unable to find coreboot table CBMEM entry.\n"); + return; + } + + const struct lb_record *lbr_p; + for (size_t i = 0; i < size; i += lbr_p->size) { + lbr_p = (const struct lb_record *)(&cbtable[i]); + if (lbr_p->tag != LB_TAG_CBMEM_ENTRY) + continue; + + const struct lb_cbmem_entry *lbe = (const struct lb_cbmem_entry *)lbr_p; + fn(lbe, i); + } + + cbmem_sysfs_free_entry(cbtable); +} + +static void hexdump_fn(const struct lb_cbmem_entry *lbe, int idx) +{ + (void)idx; + printf("# CBMEM entry id: %#x\n", lbe->id); + + uint8_t *buf = NULL; + size_t size = 0; + + if (cbmem_sysfs_get_cbmem_entry(lbe->id, &buf, &size)) + return; + + hexdump(0, buf, size); + + cbmem_sysfs_free_entry(buf); +} + +void cbmem_sysfs_hexdump_cbmem(void) +{ + foreach_cbmem_entry(hexdump_fn); +} + +static void toc_fn(const struct lb_cbmem_entry *lbe, int idx) +{ + cbmem_print_entry(idx, lbe->id, lbe->address, lbe->entry_size); +} + +void cbmem_sysfs_toc(void) +{ + printf("CBMEM table of contents:\n"); + printf(" %-20s %-8s %-8s %-8s\n", "NAME", "ID", "START", "LENGTH"); + foreach_cbmem_entry(toc_fn); +}