Edward O'Callaghan has uploaded this change for review.
fmap: Bring in CrOS get_fmap_entries() verbatim
Change-Id: I0b2372e3473939064515f698b23ad4c0048bb408
Signed-off-by: Edward O'Callaghan <quasisec@google.com>
---
M fmap.c
M fmap.h
2 files changed, 314 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/63/47063/1
diff --git a/fmap.c b/fmap.c
index 2f34a5c..b3d8542 100644
--- a/fmap.c
+++ b/fmap.c
@@ -38,8 +38,15 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+#include <arpa/inet.h>
+
#include "flash.h"
#include "fmap.h"
+#include "layout.h"
+#include "search.h"
+
+#define ACPI_FMAP_PATH "/sys/devices/platform/chromeos_acpi/FMAP"
+#define FDT_FMAP_PATH "/proc/device-tree/firmware/chromeos/fmap-offset"
static size_t fmap_size(const struct fmap *fmap)
{
@@ -146,6 +153,14 @@
return 0;
}
+int fmap_find(struct fmap *fmap)
+{
+ if (!is_valid_fmap(fmap))
+ return 0;
+
+ return fmap_size(fmap);
+}
+
static int fmap_lsearch_rom(struct fmap **fmap_out,
struct flashctx *const flashctx, size_t rom_offset, size_t len)
{
@@ -329,3 +344,301 @@
return ret;
}
+
+/* Read value from ACPI in sysfs, if it exists. */
+__attribute__((unused)) static int read_fmap_base_acpi(uint32_t *out)
+{
+ int rv = 0;
+ FILE *f;
+
+ if (!(f = fopen(ACPI_FMAP_PATH, "r")))
+ return -1;
+
+ /* FMAP base is an ASCII signed integer. */
+ if (fscanf(f, "%d", (int *)out) != 1)
+ rv = -1;
+
+ fclose(f);
+
+ if (rv)
+ msg_gdbg("%s: failed to read fmap_base from ACPI\n", __func__);
+ else
+ msg_gdbg("%s: read fmap_base from ACPI\n", __func__);
+
+ return rv;
+}
+
+/* Read value from FDT, if it exists. */
+__attribute__((unused)) static int read_fmap_base_fdt(uint32_t *out)
+{
+ int rv = 0;
+ uint32_t data;
+ FILE *f;
+
+ if (!(f = fopen(FDT_FMAP_PATH, "r")))
+ return -1;
+
+ /* Value is stored as network-byte order dword. */
+ if (fread(&data, sizeof(data), 1, f) != 1)
+ rv = -1;
+ else
+ *out = ntohl(data);
+
+ fclose(f);
+
+ if (rv)
+ msg_gdbg("%s: failed to read fmap_base from FDT\n", __func__);
+ else
+ msg_gdbg("%s: read fmap_base from FDT\n", __func__);
+
+ return rv;
+}
+
+/*
+ * Find the FMAP base from ACPI or FDT.
+ * @search: Search information
+ * @offset: Place to put offset
+ * @return 0 if offset found, -1 if not
+ */
+static int get_fmap_base(struct search_info *search, off_t *offset)
+{
+ uint32_t fmap_base;
+ uint32_t from_top;
+
+#if IS_X86
+ if (read_fmap_base_acpi(&fmap_base) < 0)
+ return -1;
+#elif IS_ARM
+ if (read_fmap_base_fdt(&fmap_base) < 0)
+ return -1;
+#else
+ return -1;
+#endif
+
+ /*
+ * TODO(b/158017386): see if we can remove this hack. It may
+ * only apply to older platforms which are now AUE.
+ *
+ * There are 2 kinds of fmap_base.
+ *
+ * 1. Shadow ROM/BIOS area (x86), such as 0xFFxxxxxx.
+ * 2. Offset to start of flash, such as 0x00xxxxxx.
+ *
+ * The shadow ROM is a cached copy of the BIOS ROM which resides below
+ * 4GB host/CPU memory address space on x86. The top of BIOS address
+ * aligns to the last byte of address space, 0xFFFFFFFF. So to obtain
+ * the ROM offset when shadow ROM is used, we subtract the fmap_base
+ * from 4G minus 1.
+ *
+ * CPU address flash address
+ * space p space
+ * 0xFFFFFFFF +-------+ --- +-------+ 0x400000
+ * | | ^ | | ^
+ * | 4MB | | | | | from_top
+ * | | v | | v
+ * fmap_base--> | -fmap | ------|--fmap-|-- the offset we need.
+ * ^ | | | |
+ * | +-------+-------+-------+ 0x000000
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * 0x00000000 +-------+
+ *
+ * We'll use bit 31 to determine if the shadow BIOS area is being used.
+ * This is sort of a hack, but allows us to perform sanity checking for
+ * older x86-based Chrome OS platforms.
+ */
+
+ msg_gdbg("%s: fmap_base: %#x, ROM size: 0x%zx\n",
+ __func__, fmap_base, search->total_size);
+
+ if (fmap_base & (1 << 31)) {
+ from_top = 0xFFFFFFFF - fmap_base + 1;
+ msg_gdbg("%s: fmap is located in shadow ROM, from_top: %#x\n",
+ __func__, from_top);
+ if (from_top > search->total_size)
+ return -1;
+ *offset = search->total_size - from_top;
+ } else {
+ msg_gdbg("%s: fmap is located in physical ROM\n", __func__);
+ if (fmap_base > search->total_size)
+ return -1;
+ *offset = fmap_base;
+ }
+
+ msg_gdbg("%s: ROM offset: %#jx\n", __func__, (intmax_t)*offset);
+ return 0;
+}
+
+static int add_fmap_entries_from_buf(const uint8_t *buf)
+{
+ struct fmap *fmap;
+ int i;
+ struct flashrom_layout *const layout = get_global_layout();
+
+ fmap = (struct fmap *)(buf);
+
+ for (i = 0; i < fmap->nareas; i++) {
+ if (layout->num_entries >= MAX_ROMLAYOUT) {
+ msg_gerr("ROM image contains too many regions\n");
+ return -1;
+ }
+ layout->entries[layout->num_entries].start = fmap->areas[i].offset;
+
+ /*
+ * Flashrom rom entries use absolute addresses. So for non-zero
+ * length entries, we need to subtract 1 from offset + size to
+ * determine the end address.
+ */
+ layout->entries[layout->num_entries].end = fmap->areas[i].offset +
+ fmap->areas[i].size;
+ if (fmap->areas[i].size)
+ layout->entries[layout->num_entries].end--;
+
+ size_t name_len = sizeof(fmap->areas[i].name);
+ layout->entries[layout->num_entries].name = calloc(1, name_len);
+ memcpy(layout->entries[layout->num_entries].name, fmap->areas[i].name, name_len);
+
+ layout->entries[layout->num_entries].included = 0;
+ strcpy(layout->entries[layout->num_entries].file, "");
+
+ msg_gdbg("added fmap region \"%s\" (file=\"%s\") as %sincluded,"
+ " start: 0x%08x, end: 0x%08x\n",
+ layout->entries[layout->num_entries].name,
+ layout->entries[layout->num_entries].file,
+ layout->entries[layout->num_entries].included ? "" : "not ",
+ layout->entries[layout->num_entries].start,
+ layout->entries[layout->num_entries].end);
+ layout->num_entries++;
+ }
+
+ return layout->num_entries;
+}
+
+enum found_t {
+ FOUND_NONE,
+ FOUND_FMAP,
+};
+
+/* returns the number of entries added, or <0 to indicate error */
+static int add_fmap_entries(void *source_handle,
+ size_t image_size,
+ int (*read_chunk)(void *handle,
+ void *dest,
+ size_t offset,
+ size_t size))
+{
+ static enum found_t found = FOUND_NONE;
+ struct search_info search;
+ struct fmap fmap_header;
+ uint8_t *buf = NULL;
+ off_t offset;
+ struct flashrom_layout *const layout = get_global_layout();
+
+ if (found != FOUND_NONE) {
+ msg_gdbg("Already found fmap entries, not searching again.\n");
+ return 0;
+ }
+
+ search_init(&search, source_handle,
+ image_size, sizeof(fmap_header), read_chunk);
+ search.handler = get_fmap_base;
+ while (found == FOUND_NONE && !search_find_next(&search, &offset)) {
+ if (search.image) {
+ memcpy(&fmap_header, search.image + offset,
+ sizeof(fmap_header));
+ } else if (read_chunk(source_handle, (uint8_t *)&fmap_header,
+ offset, sizeof(fmap_header))) {
+ msg_gdbg("[L%d] failed to read flash at offset %#jx\n",
+ __LINE__, (intmax_t)offset);
+ return -1;
+ }
+ int buf_size = fmap_find(&fmap_header);
+ if (buf_size == 0)
+ continue;
+ buf = calloc(1, buf_size);
+
+ if (read_chunk(source_handle, buf, offset, buf_size)) {
+ msg_gdbg("[L%d] failed to read %d bytes at offset 0x%lx\n",
+ __LINE__, buf_size, (unsigned long)offset);
+ return -1;
+ } else {
+ found = FOUND_FMAP;
+ }
+ }
+
+ switch (found) {
+ case FOUND_FMAP:
+ layout->num_entries = add_fmap_entries_from_buf(buf);
+ break;
+ default:
+ msg_gdbg("%s: no fmap present\n", __func__);
+ }
+ if (buf)
+ free(buf);
+ search_free(&search);
+
+ return layout->num_entries;
+}
+
+/*
+ * read_chunk() callback used when reading contents from a file.
+ */
+static int read_from_file(void *fhandle,
+ void *dest,
+ size_t offset,
+ size_t size)
+{
+ FILE *handle = fhandle;
+
+ if (fseek(handle, offset, SEEK_SET)) {
+ msg_cerr("%s failed to seek to position %zd\n",
+ __func__, offset);
+ return 1;
+ }
+
+ if (fread(dest, 1, size, handle) != size) {
+ msg_cerr("%s failed to read %zd bytes\n",
+ __func__, offset);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * read_chunk() callback used when reading contents from the flash device.
+ */
+static int read_from_flash(void *handle,
+ void *dest,
+ size_t offset,
+ size_t size)
+{
+ struct flashctx *flash = handle;
+
+ return read_flash(flash, dest, offset, size);
+}
+
+int get_fmap_entries(const char *filename, struct flashctx *flash)
+{
+ int rv;
+ size_t image_size = flash->chip->total_size * 1024;
+
+ /* Let's try retrieving entries from file. */
+ if (filename) {
+ FILE *handle;
+
+ handle = fopen(filename, "r");
+ if (handle) {
+ rv = add_fmap_entries(handle,
+ image_size, read_from_file);
+ fclose(handle);
+ if (rv > 0)
+ return rv;
+ msg_cerr("No fmap entries found in %s\n", filename);
+ }
+ }
+
+ return add_fmap_entries(flash, image_size, read_from_flash);
+}
diff --git a/fmap.h b/fmap.h
index 924e11f..840de3d 100644
--- a/fmap.h
+++ b/fmap.h
@@ -67,5 +67,6 @@
int fmap_read_from_buffer(struct fmap **fmap_out, const uint8_t *buf, size_t len);
int fmap_read_from_rom(struct fmap **fmap_out, struct flashctx *const flashctx, size_t rom_offset, size_t len);
+int get_fmap_entries(const char *filename, struct flashctx *flash);
#endif /* __FMAP_H__*/
To view, visit change 47063. To unsubscribe, or for help writing mail filters, visit settings.