diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index 3787a56578..a568e9aef6 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -1,3 +1,5 @@ +HOSTPKGCONFIG ?= /usr/bin/pkg-config + compressionobj := compressionobj += compress.o # LZ4 @@ -92,6 +94,7 @@ amdcompobj += xdr.o elogobj := elogobj := elogtool.o elogobj += eventlog.o +elogobj += uflashrom.o elogobj += valstr.o elogobj += elog.o elogobj += common.o @@ -144,6 +147,12 @@ else TOOLCFLAGS+=-std=c11 endif +.PHONY: check-flashrom-presence +check-flashrom-presence: + $(HOSTPKGCONFIG) --exists flashrom || \ + (echo "Error: Ensure that pkg-config and flashrom are installed."; exit 1) + +FLASHROM := $$($(HOSTPKGCONFIG) --libs flashrom) VBOOT_HOSTLIB = $(VBOOT_HOST_BUILD)/libvboot_host.a $(VBOOT_HOSTLIB): @@ -224,9 +233,9 @@ $(objutil)/cbfstool/amdcompress: $(addprefix $(objutil)/cbfstool/,$(amdcompobj)) printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(amdcompobj)) -lz -$(objutil)/cbfstool/elogtool: $(addprefix $(objutil)/cbfstool/,$(elogobj)) $(VBOOT_HOSTLIB) +$(objutil)/cbfstool/elogtool: | check-flashrom-presence $(addprefix $(objutil)/cbfstool/,$(elogobj)) printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" - $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(elogobj)) $(VBOOT_HOSTLIB) + $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(elogobj)) $(FLASHROM) $(objutil)/cbfstool/cse_fpt: $(addprefix $(objutil)/cbfstool/,$(cse_fpt_obj)) printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" diff --git a/util/cbfstool/elogtool.c b/util/cbfstool/elogtool.c index 253e2ee95a..44823dd89a 100644 --- a/util/cbfstool/elogtool.c +++ b/util/cbfstool/elogtool.c @@ -11,9 +11,9 @@ #include #include -#include #include "eventlog.h" +#include "uflashrom.h" /* Only refers to the data max size. The "-1" is the checksum byte */ #define ELOG_MAX_EVENT_DATA_SIZE (ELOG_MAX_EVENT_SIZE - sizeof(struct event_header) - 1) @@ -78,16 +78,18 @@ static void usage(char *invoked_as) */ static int elog_read(struct buffer *buffer, const char *filename) { - if (filename == NULL) { - uint8_t *buf; - uint32_t buf_size; + struct firmware_programmer image = { + .programmer = FLASHROM_PROGRAMMER_INTERNAL_AP, + .data = NULL, + .size = 0, + }; - if (flashrom_read(FLASHROM_PROGRAMMER_INTERNAL_AP, ELOG_RW_REGION_NAME, - &buf, &buf_size) != VB2_SUCCESS) { + if (filename == NULL) { + if (flashrom_read(&image, ELOG_RW_REGION_NAME) != 0) { fprintf(stderr, "Could not read RW_ELOG region using flashrom\n"); return ELOGTOOL_EXIT_READ_ERROR; } - buffer_init(buffer, NULL, buf, buf_size); + buffer_init(buffer, NULL, image.data, image.size); } else if (buffer_from_file(buffer, filename) != 0) { fprintf(stderr, "Could not read input file: %s\n", filename); return ELOGTOOL_EXIT_READ_ERROR; @@ -108,9 +110,14 @@ static int elog_read(struct buffer *buffer, const char *filename) */ static int elog_write(struct buffer *buf, const char *filename) { + struct firmware_programmer image = { + .programmer = FLASHROM_PROGRAMMER_INTERNAL_AP, + .data = buffer_get(buf), + .size = buffer_size(buf), + }; + if (filename == NULL) { - if (flashrom_write(FLASHROM_PROGRAMMER_INTERNAL_AP, ELOG_RW_REGION_NAME, - buffer_get(buf), buffer_size(buf)) != VB2_SUCCESS) { + if (flashrom_write(&image, ELOG_RW_REGION_NAME) != 0) { fprintf(stderr, "Failed to write to RW_ELOG region using flashrom\n"); return ELOGTOOL_EXIT_WRITE_ERROR; diff --git a/util/cbfstool/uflashrom.c b/util/cbfstool/uflashrom.c new file mode 100644 index 0000000000..faa45b6b49 --- /dev/null +++ b/util/cbfstool/uflashrom.c @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include +#include +#include +#include + +#include + +#include "uflashrom.h" + +static int flashrom_print_cb(enum flashrom_log_level level, const char *fmt, va_list ap) +{ + int ret = 0; + FILE *output_type = stderr; + + if (level > FLASHROM_MSG_INFO) + return ret; + + ret = vfprintf(output_type, fmt, ap); + /* msg_*spew often happens inside chip accessors + * in possibly time-critical operations. + * If increasing verbosity, don't slow them down by flushing. + */ + fflush(output_type); + + return ret; +} + +static size_t resize_buf_to_offset(uint8_t **buf, unsigned int start, unsigned int len) +{ + uint8_t *old = *buf; // make a copy to free the old heap. + + *buf = calloc(1, len); + memcpy(*buf, &old[start], len); + free(old); + + return len; +} + +static uint8_t *resize_buf_from_offset(const uint8_t *buf, size_t len, unsigned int rstart, + unsigned int rlen) +{ + size_t nlen = rstart + rlen; + if (nlen > len) + return NULL; + + uint8_t *nbuf = calloc(1, len); /* NOTE: full len buf required for writes. */ + memcpy(nbuf + rstart, buf, rlen); + + return nbuf; +} + +/** + * @brief Reads from flash into a buffer with an optional region. + * + * @param image, containing the programmer to use, unallocated buffer and size. + * @param region, (optional) the string of the region to read from. + * @return 0 on success + */ +int flashrom_read(struct firmware_programmer *image, const char *region) +{ + int r = 0; + size_t len = 0; + + struct flashrom_programmer *prog = NULL; + struct flashrom_flashctx *flashctx = NULL; + struct flashrom_layout *layout = NULL; + + flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb); + + r |= flashrom_init(1); + r |= flashrom_programmer_init(&prog, image->programmer, NULL); + r |= flashrom_flash_probe(&flashctx, prog, NULL); + if (r) { + r = -1; + goto err_cleanup; + } + + len = flashrom_flash_getsize(flashctx); + if (region) { + r = flashrom_layout_read_fmap_from_rom(&layout, flashctx, 0, len); + if (r > 0) { + fprintf(stderr, "could not read fmap from rom, r=%d\n", r); + r = -1; + goto err_cleanup; + } + /* empty region causes seg fault in API. */ + r |= flashrom_layout_include_region(layout, region); + if (r > 0) { + fprintf(stderr, "could not include region = '%s'\n", region); + r = -1; + goto err_cleanup; + } + flashrom_layout_set(flashctx, layout); + } + /* Due to how the libflashrom API works we first need a buffer sized + * to the entire flash and after the read has finished, find the + * the precise region size then resize the buffer accordingly. + */ + image->data = calloc(1, len); + image->size = len; + + r |= flashrom_image_read(flashctx, image->data, len); + + /* Here we resize the buffer from being the entire flash down to the specific + * region size read and that we were interested in. Note that we only include + * a singular region. + */ + if (region) { + unsigned int r_start, r_len; + flashrom_layout_get_region_range(layout, region, &r_start, &r_len); + image->size = resize_buf_to_offset(&image->data, r_start, r_len); + } + +err_cleanup: + flashrom_programmer_shutdown(prog); + if (layout) + flashrom_layout_release(layout); + if (flashctx) + flashrom_flash_release(flashctx); + + return r; +} + +/** + * @brief Writes flash from a buffer with an optional region. + * + * @param image, containing the programmer to use, allocated buffer and its size. + * @param region, (optional) the string of the region to write to. + * @return 0 on success + */ +int flashrom_write(struct firmware_programmer *image, const char *region) +{ + int r = 0; + size_t len = 0; + uint8_t *buf = image->data; + + struct flashrom_programmer *prog = NULL; + struct flashrom_flashctx *flashctx = NULL; + struct flashrom_layout *layout = NULL; + + flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb); + + r |= flashrom_init(1); + r |= flashrom_programmer_init(&prog, image->programmer, NULL); + r |= flashrom_flash_probe(&flashctx, prog, NULL); + if (r) { + r = -1; + goto err_cleanup; + } + + len = flashrom_flash_getsize(flashctx); + if (len == 0) { + fprintf(stderr, "zero sized flash detected\n"); + r = -1; + goto err_cleanup; + } + if (region) { + r = flashrom_layout_read_fmap_from_buffer( + &layout, flashctx, (const uint8_t *)image->data, image->size); + if (r > 0) { + r = flashrom_layout_read_fmap_from_rom(&layout, flashctx, 0, len); + if (r > 0) { + fprintf(stderr, "could not read fmap from image or rom, r=%d\n", + r); + r = -1; + goto err_cleanup; + } + } + /* empty region causes seg fault in API. */ + r |= flashrom_layout_include_region(layout, region); + if (r > 0) { + fprintf(stderr, "could not include region = '%s'\n", region); + r = -1; + goto err_cleanup; + } + flashrom_layout_set(flashctx, layout); + + unsigned int r_start, r_len; + flashrom_layout_get_region_range(layout, region, &r_start, &r_len); + assert(r_len == image->size); + buf = resize_buf_from_offset(image->data, len, r_start, r_len); + if (!buf) { + r = -1; + goto err_cleanup_free; + } + } else if (image->size != len) { + r = -1; + goto err_cleanup; + } + + flashrom_flag_set(flashctx, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, false); + flashrom_flag_set(flashctx, FLASHROM_FLAG_VERIFY_AFTER_WRITE, true); + + r |= flashrom_image_write(flashctx, buf, len, NULL); + +err_cleanup_free: + if (region) + free(buf); +err_cleanup: + flashrom_programmer_shutdown(prog); + if (layout) + flashrom_layout_release(layout); + if (flashctx) + flashrom_flash_release(flashctx); + + return r; +} diff --git a/util/cbfstool/uflashrom.h b/util/cbfstool/uflashrom.h new file mode 100644 index 0000000000..1ef0085dd9 --- /dev/null +++ b/util/cbfstool/uflashrom.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef UFLASHROM_H +#define UFLASHROM_H + +#define FLASHROM_PROGRAMMER_INTERNAL_AP "internal" + +struct firmware_programmer { + const char *programmer; + uint32_t size; + uint8_t *data; +}; + +int flashrom_read(struct firmware_programmer *image, const char *region); +int flashrom_write(struct firmware_programmer *image, const char *region); + +#endif /* UFLASHROM_H */