| // SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause |
| /* |
| * Copyright 2018 Google LLC |
| */ |
| |
| #define LOG_CATEGORY LOGC_VBOOT |
| |
| #include <common.h> |
| #include <errno.h> |
| #include <fdtdec.h> |
| #include <log.h> |
| #include <malloc.h> |
| #include <asm/io.h> |
| #include <cros/cros_common.h> |
| #include <cros/cros_ofnode.h> |
| #include <linux/string.h> |
| |
| ofnode cros_ofnode_config_node(void) |
| { |
| ofnode node = ofnode_path("/chromeos-config"); |
| |
| if (!ofnode_valid(node)) |
| log_debug("failed to find /chromeos-config\n"); |
| |
| return node; |
| } |
| |
| /* These are the various flashmap nodes that we are interested in */ |
| enum section_t { |
| SECTION_FIRMWARE_ID, |
| SECTION_BOOT, |
| SECTION_GBB, |
| SECTION_VBLOCK, |
| SECTION_FMAP, |
| SECTION_ECRW, |
| SECTION_ECRO, |
| SECTION_PDRW, |
| SECTION_PDRO, |
| SECTION_SPL, |
| SECTION_BOOT_REC, |
| SECTION_SPL_REC, |
| |
| SECTION_COUNT, |
| SECTION_NONE = -1, |
| }; |
| |
| /* Names for each section */ |
| static const char *section_name[SECTION_COUNT] = { |
| "firmware-id", |
| "u-boot", |
| "gbb", |
| "vblock", |
| "fmap", |
| "ecrw", |
| "ecro", |
| "pdrw", |
| "pdro", |
| "u-boot-spl", |
| "boot-rec", |
| "u-boot-spl-rec", |
| }; |
| |
| /** |
| * Look up a section name and return its type |
| * |
| * @name: Name of section |
| * @return section type section_t, or SECTION_NONE if none |
| */ |
| static enum section_t lookup_section(const char *name) |
| { |
| int i; |
| |
| for (i = 0; i < SECTION_COUNT; i++) |
| if (!strcmp(name, section_name[i])) |
| return i; |
| |
| return SECTION_NONE; |
| } |
| |
| /** |
| * Process a binman node, storing its information in our config. |
| * |
| * @node: ofnode to read |
| * @config: Place to put top-level information we read |
| * @fw: Place to put the entry information |
| * |
| * @return 0 if ok, -ve on error |
| */ |
| static int process_fmap_node(ofnode node, struct cros_fmap *config, |
| struct fmap_section *fw) |
| { |
| struct fmap_entry *entry; |
| enum section_t section; |
| const char *name; |
| int ret; |
| |
| name = ofnode_get_name(node); |
| if (!strcmp("rw-vblock-dev", name)) |
| return log_msg_ret("rw-vblock-dev", |
| ofnode_read_fmap_entry(node, |
| &config->readwrite_devkey)); |
| |
| if (!strcmp("rw-elog", name)) |
| return log_msg_ret("rw-elog", |
| ofnode_read_fmap_entry(node, &config->elog)); |
| |
| section = lookup_section(name); |
| log_debug("lookup_section '%s': %d\n", name, section); |
| entry = NULL; |
| |
| switch (section) { |
| case SECTION_FIRMWARE_ID: |
| entry = &fw->firmware_id; |
| break; |
| case SECTION_BOOT: |
| entry = &fw->boot; |
| break; |
| case SECTION_GBB: |
| entry = &fw->gbb; |
| break; |
| case SECTION_VBLOCK: |
| entry = &fw->vblock; |
| break; |
| case SECTION_FMAP: |
| entry = &fw->fmap; |
| break; |
| case SECTION_ECRW: |
| entry = &fw->ec[EC_MAIN].rw; |
| break; |
| case SECTION_ECRO: |
| entry = &fw->ec[EC_MAIN].ro; |
| break; |
| case SECTION_PDRW: |
| entry = &fw->ec[EC_PD].rw; |
| break; |
| case SECTION_PDRO: |
| entry = &fw->ec[EC_PD].ro; |
| break; |
| case SECTION_SPL: |
| entry = &fw->spl; |
| break; |
| case SECTION_BOOT_REC: |
| entry = &fw->boot_rec; |
| break; |
| case SECTION_SPL_REC: |
| entry = &fw->spl_rec; |
| break; |
| case SECTION_COUNT: |
| case SECTION_NONE: |
| return 0; |
| } |
| |
| /* Read in the properties */ |
| assert(entry); |
| if (entry) { |
| ret = ofnode_read_fmap_entry(node, entry); |
| if (ret) |
| return log_msg_ret(ofnode_get_name(node), ret); |
| } |
| |
| return 0; |
| } |
| |
| int cros_ofnode_flashmap(struct cros_fmap *config) |
| { |
| struct fmap_entry base_entry; |
| ofnode node; |
| int ret; |
| |
| memset(config, '\0', sizeof(*config)); |
| node = ofnode_by_compatible(ofnode_null(), "chromeos,flashmap"); |
| if (!ofnode_valid(node)) |
| return log_msg_ret("missing", -EINVAL); |
| |
| /* Read in the top-level offset */ |
| if (ofnode_read_fmap_entry(node, &base_entry)) |
| return log_msg_ret("size", -EINVAL); |
| config->flash_base = base_entry.offset; |
| |
| ofnode_for_each_subnode(node, node) { |
| struct fmap_section *fw; |
| struct fmap_entry *entry; |
| const char *name; |
| ofnode subnode; |
| |
| name = ofnode_get_name(node); |
| fw = NULL; |
| if (!strcmp(name, "read-only")) { |
| fw = &config->readonly; |
| } else if (!strcmp(name, "read-write-a")) { |
| fw = &config->readwrite_a; |
| } else if (!strcmp(name, "read-write-b")) { |
| fw = &config->readwrite_b; |
| } else { |
| log_debug("Ignoring section '%s'\n", name); |
| continue; |
| } |
| entry = &fw->all; |
| ret = ofnode_read_fmap_entry(node, entry); |
| if (ret) |
| return log_msg_ret(ofnode_get_name(node), ret); |
| ofnode_for_each_subnode(subnode, node) { |
| ret = process_fmap_node(subnode, config, fw); |
| if (ret) |
| return log_msg_ret("process", -EINVAL); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int cros_ofnode_find_locale(const char *name, struct fmap_entry *entry) |
| { |
| ofnode node, subnode; |
| int ret; |
| |
| node = ofnode_by_compatible(ofnode_null(), "chromeos,locales"); |
| if (!ofnode_valid(node)) |
| return log_msg_ret("node", -EINVAL); |
| subnode = ofnode_find_subnode(node, name); |
| if (!ofnode_valid(subnode)) { |
| log_err("Locale not found: %s\n", name); |
| return log_msg_ret("subnode", -ENOENT); |
| } |
| ret = ofnode_read_fmap_entry(subnode, entry); |
| if (ret) { |
| log_err("Can't read entry for locale '%s': %s\n", name, |
| ofnode_get_name(subnode)); |
| return log_msg_ret("entry", ret); |
| } |
| |
| return 0; |
| } |
| |
| int cros_ofnode_decode_region(const char *mem_type, const char *suffix, |
| fdt_addr_t *basep, fdt_size_t *sizep) |
| { |
| ofnode node = cros_ofnode_config_node(); |
| int ret; |
| |
| if (!ofnode_valid(node)) |
| return -ENOENT; |
| ret = ofnode_decode_memory_region(node, mem_type, suffix, basep, sizep); |
| if (ret) { |
| log_debug("failed to find %s suffix %s in /chromeos-config\n", |
| mem_type, suffix); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| int cros_ofnode_memory(const char *name, struct fdt_memory *config) |
| { |
| const fdt_addr_t *cell; |
| ofnode node; |
| int len; |
| |
| node = ofnode_path(name); |
| if (!ofnode_valid(node)) |
| return -EINVAL; |
| |
| cell = ofnode_get_property(node, "reg", &len); |
| if (cell && len >= sizeof(fdt_addr_t) * 2) { |
| config->start = fdt_addr_to_cpu(cell[0]); |
| config->end = config->start + fdt_addr_to_cpu(cell[1]); |
| } else { |
| return -FDT_ERR_BADLAYOUT; |
| } |
| |
| return 0; |
| } |
| |
| static void dump_fmap_entry(const char *path, struct fmap_entry *entry) |
| { |
| log_debug("%-20s %08x:%08x\n", path, entry->offset, entry->length); |
| if (entry->hash) { |
| int i; |
| |
| log_debug(" hash: "); |
| for (i = 0; i < entry->hash_size; i++) |
| log_debug("%02x", entry->hash[i]); |
| log_debug("\n"); |
| } |
| } |
| |
| static void dump_fmap_section(const char *name, |
| struct fmap_section *entry) |
| { |
| log_debug("%s\n", name); |
| dump_fmap_entry("all", &entry->all); |
| dump_fmap_entry("spl", &entry->spl); |
| dump_fmap_entry("boot", &entry->boot); |
| dump_fmap_entry("vblock", &entry->vblock); |
| dump_fmap_entry("firmware_id", &entry->firmware_id); |
| dump_fmap_entry("ecrw", &entry->ec[EC_MAIN].rw); |
| } |
| |
| void cros_ofnode_dump_fmap(struct cros_fmap *config) |
| { |
| dump_fmap_entry("fmap", &config->readonly.fmap); |
| dump_fmap_entry("gbb", &config->readonly.gbb); |
| dump_fmap_entry("firmware_id", &config->readonly.firmware_id); |
| dump_fmap_entry("boot-rec", &config->readonly.boot_rec); |
| dump_fmap_entry("spl-rec", &config->readonly.spl_rec); |
| dump_fmap_section("rw-a", &config->readwrite_a); |
| dump_fmap_section("rw-b", &config->readwrite_b); |
| } |