summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward O'Callaghan <quasisec@google.com>2021-11-29 11:02:46 +1100
committerEdward O'Callaghan <quasisec@chromium.org>2022-01-14 23:10:55 +0000
commitd74b8d9c990780ba64515b36aaff79d719d71ead (patch)
treed6adb48a2f271cb333f9182e8b839015b612950b
parente565f752217d8f67504f65b980fe7511724e8dd9 (diff)
util/cbfstool: Port elogtool to libflashrom
This also uncouples cbfstool from being overly Chromium specific. However the main objective is to not subprocess flashrom any more and instead use the programmatic API. BUG=b:207808292 TEST=built and ran `elogtool (list|clear|add 0x16 C0FFEE)`. Change-Id: I79df2934b9b0492a554a4fecdd533a0abe1df231 Signed-off-by: Edward O'Callaghan <quasisec@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/59714 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Sam McNally <sammc@google.com>
-rw-r--r--util/cbfstool/Makefile.inc13
-rw-r--r--util/cbfstool/elogtool.c25
-rw-r--r--util/cbfstool/uflashrom.c210
-rw-r--r--util/cbfstool/uflashrom.h17
4 files changed, 254 insertions, 11 deletions
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 <common.h>
#include <commonlib/bsd/elog.h>
-#include <flashrom.h>
#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 <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libflashrom.h>
+
+#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 */