diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index e52524a9b2..4238445aa8 100644 --- a/util/cbfstool/Makefile +++ b/util/cbfstool/Makefile @@ -25,7 +25,8 @@ CBFSTOOL_COMMON+=flashmap/kv_pair.o flashmap/valstr.o CBFSTOOL_COMMON:=$(addprefix $(obj)/,$(CBFSTOOL_COMMON)) FMAPTOOL_BINARY:=$(obj)/fmaptool -FMAPTOOL_COMMON:=fmap_from_fmd.o fmd.o fmd_parser.o fmd_scanner.o +FMAPTOOL_COMMON:=cbfs_sections.o fmap_from_fmd.o +FMAPTOOL_COMMON+=fmd.o fmd_parser.o fmd_scanner.o # FMAP FMAPTOOL_COMMON+=flashmap/fmap.o FMAPTOOL_COMMON+=flashmap/kv_pair.o flashmap/valstr.o diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index ec858a9792..1aa9d76ae4 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -24,6 +24,7 @@ cbfsobj += cbfs-payload-linux.o fmapobj := fmapobj += fmaptool.o +fmapobj += cbfs_sections.o fmapobj += fmap_from_fmd.o fmapobj += fmd.o fmapobj += fmd_parser.o diff --git a/util/cbfstool/cbfs_sections.c b/util/cbfstool/cbfs_sections.c new file mode 100644 index 0000000000..81912d5b96 --- /dev/null +++ b/util/cbfstool/cbfs_sections.c @@ -0,0 +1,122 @@ +/* + * fmap_sections.c, track which sections of the image will contain CBFSes + * + * Copyright (C) 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include "cbfs_sections.h" + +#include +#include +#include + +struct descriptor_node { + const struct flashmap_descriptor *val; + struct descriptor_node *next; +}; + +static struct descriptor_list { + struct descriptor_node *head; + struct descriptor_node *tail; +} cbfs_sections; + +static bool seen_primary_section = false; + +static void descriptor_list_prepend(struct descriptor_list *list, + struct descriptor_node *new_head) +{ + assert(list); + assert(new_head); + + new_head->next = list->head; + list->head = new_head; + if (!list->tail) + list->tail = new_head; +} + +static void descriptor_list_append(struct descriptor_list *list, + struct descriptor_node *new_tail) +{ + assert(list); + assert(new_tail); + + if (list->tail) + list->tail->next = new_tail; + list->tail = new_tail; + if (!list->head) + list->head = new_tail; +} + +/* Implementation of cbfs module's callback; invoked during fmd file parsing */ +bool fmd_process_annotation_impl(const struct flashmap_descriptor *node, + const char *annotation) +{ + if (strcmp(annotation, SECTION_ANNOTATION_CBFS) == 0 && + node->list_len == 0) { + struct descriptor_node *list_node = malloc(sizeof(*list_node)); + list_node->val = node; + list_node->next = NULL; + + if (strcmp(node->name, SECTION_NAME_PRIMARY_CBFS) == 0) { + descriptor_list_prepend(&cbfs_sections, list_node); + seen_primary_section = true; + } else { + descriptor_list_append(&cbfs_sections, list_node); + } + + return true; + } + + return false; +} + +cbfs_section_iterator_t cbfs_sections_iterator(void) +{ + return cbfs_sections.head; +} + +bool cbfs_sections_iterator_advance(cbfs_section_iterator_t *it) +{ + assert(it); + if (!*it) + return false; + + *it = (*it)->next; + return true; +} + +const struct flashmap_descriptor *cbfs_sections_iterator_deref( + cbfs_section_iterator_t it) +{ + assert(it); + return it->val; +} + +bool cbfs_sections_primary_cbfs_accounted_for(void) +{ + return seen_primary_section; +} + +void cbfs_sections_cleanup(void) +{ + for (struct descriptor_node *cur = cbfs_sections.head, *next = NULL; + cur; cur = next) { + next = cur->next; + free(cur); + } + cbfs_sections.head = NULL; + cbfs_sections.tail = NULL; +} diff --git a/util/cbfstool/cbfs_sections.h b/util/cbfstool/cbfs_sections.h new file mode 100644 index 0000000000..5a23899de2 --- /dev/null +++ b/util/cbfstool/cbfs_sections.h @@ -0,0 +1,59 @@ +/* + * fmap_sections.h, track which sections of the image will contain CBFSes + * + * Copyright (C) 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#ifndef CBFS_SECTIONS_H_ +#define CBFS_SECTIONS_H_ + +#include "fmd.h" + +#include + +#define SECTION_NAME_FMAP "FMAP" +#define SECTION_NAME_PRIMARY_CBFS "COREBOOT" + +#define SECTION_ANNOTATION_CBFS "CBFS" + +typedef const struct descriptor_node *cbfs_section_iterator_t; + +/** @return Iterator pointing to first CBFS section, or NULL if none exist */ +cbfs_section_iterator_t cbfs_sections_iterator(void); + +/** + * Advance iterator to point to the next CBFS section. + * If it was already pointing to the last such section, it will be set to NULL. + * + * @param it (Non-NULL) pointer to (possibly NULL) iterator to be updated + * @return Whether it was successfully advanced (wasn't already NULL) + */ +bool cbfs_sections_iterator_advance(cbfs_section_iterator_t *it); + +/** + * @param it Iterator, which must currently be non-NULL + * @return Section to which it points + */ +const struct flashmap_descriptor *cbfs_sections_iterator_deref( + cbfs_section_iterator_t it); + +/** @return Whether a section named SECTION_NAME_PRIMARY_CBFS is in the list. */ +bool cbfs_sections_primary_cbfs_accounted_for(void); + +/** Reclaim the space used to store knowledge of which sections are CBFSes. */ +void cbfs_sections_cleanup(void); + +#endif diff --git a/util/cbfstool/fmaptool.c b/util/cbfstool/fmaptool.c index 961e9dc918..09b68d23bf 100644 --- a/util/cbfstool/fmaptool.c +++ b/util/cbfstool/fmaptool.c @@ -18,53 +18,149 @@ */ #include "common.h" +#include "cbfs_sections.h" #include "fmap_from_fmd.h" #include #include +#include #define STDIN_FILENAME_SENTINEL "-" +#define HEADER_FMAP_OFFSET "FMAP_OFFSET" + enum fmaptool_return { FMAPTOOL_EXIT_SUCCESS = 0, FMAPTOOL_EXIT_BAD_ARGS, FMAPTOOL_EXIT_BAD_INPUT_PATH, FMAPTOOL_EXIT_BAD_OUTPUT_PATH, FMAPTOOL_EXIT_FAILED_DESCRIPTOR, + FMAPTOOL_EXIT_MISSING_FMAP_SECTION, + FMAPTOOL_EXIT_MISSING_PRIMARY_CBFS, FMAPTOOL_EXIT_FAILED_FMAP_CONVERSION, FMAPTOOL_EXIT_UNKNOWN_FMAP_SIZE, - FMAPTOOL_EXIT_FAILED_WRITING_FILE, + FMAPTOOL_EXIT_FAILED_WRITING_OUTPUT, + FMAPTOOL_EXIT_FAILED_WRITING_HEADER, }; -bool fmd_process_annotation_impl(unused const struct flashmap_descriptor *node, - unused const char *annotation) +static void usage(const char *invoked_as) { - // We always accept annotations, but never act on them. + fputs("fmaptool: Compiler for fmd (flashmap descriptor) files\n", + stderr); + fputs("\nUSAGE:\n", stderr); + fprintf(stderr, + "\t%s [-h
] \n", + invoked_as); + fputs("\nMANDATORY ARGUMENTS:\n", stderr); + fprintf(stderr, + " may be '%s' to read from standard input\n", + STDIN_FILENAME_SENTINEL); + fputs(" must be a regular file\n", stderr); + fputs("\nOPTIONAL SWITCHES:\n", stderr); + fprintf(stderr, + "-h\tAlso produce a C header defining %s to the FMAP section's flash offset.\n", + HEADER_FMAP_OFFSET); + fputs("\nOUTPUT:\n", stderr); + fputs("A successful invocation prints a summary of work done to standard error, and a comma-separated list\n", + stderr); + fputs("of those sections that contain CBFSes, starting with the primary such section, to standard output.\n", + stderr); +} + +static void list_cbfs_section_names(void) +{ + cbfs_section_iterator_t cbfs_it = cbfs_sections_iterator(); + assert(cbfs_it); + + bool subsequent = false; + while (cbfs_it) { + const char *cur_name = + cbfs_sections_iterator_deref(cbfs_it)->name; + if (cbfs_sections_iterator_advance(&cbfs_it) && subsequent) + putchar(','); + fputs(cur_name, stdout); + subsequent = true; + } + putchar('\n'); +} + +static bool write_header(const char *out_fname, + const struct flashmap_descriptor *root) +{ + assert(out_fname); + + FILE *header = fopen(out_fname, "w"); + if (!header) { + fprintf(stderr, "FATAL: Unable to open file '%s' for writing\n", + out_fname); + return false; + } + + unsigned fmap_offset = + fmd_calc_absolute_offset(root, SECTION_NAME_FMAP); + assert(fmap_offset != FMD_NOTFOUND); + + fputs("#ifndef FMAPTOOL_GENERATED_HEADER_H_\n", header); + fputs("#define FMAPTOOL_GENERATED_HEADER_H_\n\n", header); + fprintf(header, "#define %s %#x\n\n", HEADER_FMAP_OFFSET, fmap_offset); + fputs("#endif\n", header); + + fclose(header); return true; } +static void full_fmd_cleanup(struct flashmap_descriptor **victim) +{ + assert(victim); + + cbfs_sections_cleanup(); + fmd_cleanup(*victim); + *victim = NULL; +} + int main(int argc, char **argv) { - if (argc != 3) { - fputs("Convert a human-readable flashmap descriptor (fmd) file into the binary FMAP format for use in firmware images\n", - stderr); - fprintf(stderr, - "USAGE: %s \n", - argv[0]); - fprintf(stderr, - "To read from standard input, provide '%s' as the input filename.\n", - STDIN_FILENAME_SENTINEL); + struct { + // Mandatory + const char *fmd_filename; + const char *fmap_filename; + + // Optional + const char *header_filename; + } args = {NULL, NULL, NULL}; + + bool show_usage = false; + int each_arg; + while (!show_usage && (each_arg = getopt(argc, argv, ":h:")) != -1) { + switch (each_arg) { + case 'h': + args.header_filename = optarg; + break; + case ':': + fprintf(stderr, "-%c: Expected an accompanying value\n", + optopt); + show_usage = true; + break; + default: + fprintf(stderr, "-%c: Unexpected command-line switch\n", + optopt); + show_usage = true; + } + } + + if (show_usage || argc - optind != 2) { + usage(argv[0]); return FMAPTOOL_EXIT_BAD_ARGS; } - const char *fmd_filename = argv[1]; - const char *fmap_filename = argv[2]; + args.fmd_filename = argv[optind]; + args.fmap_filename = argv[optind + 1]; FILE *fmd_file = stdin; - if (strcmp(fmd_filename, STDIN_FILENAME_SENTINEL) != 0) { - fmd_file = fopen(fmd_filename, "r"); + if (strcmp(args.fmd_filename, STDIN_FILENAME_SENTINEL) != 0) { + fmd_file = fopen(args.fmd_filename, "r"); if (!fmd_file) { fprintf(stderr, "FATAL: Unable to open file '%s'\n", - fmd_filename); + args.fmd_filename); return FMAPTOOL_EXIT_BAD_INPUT_PATH; } } @@ -74,14 +170,32 @@ int main(int argc, char **argv) if (!descriptor) { fputs("FATAL: Failed while processing provided descriptor\n", stderr); + full_fmd_cleanup(&descriptor); return FMAPTOOL_EXIT_FAILED_DESCRIPTOR; } + if (!fmd_find_node(descriptor, SECTION_NAME_FMAP)) { + fprintf(stderr, + "FATAL: Flashmap descriptor must have an '%s' section\n", + SECTION_NAME_FMAP); + full_fmd_cleanup(&descriptor); + return FMAPTOOL_EXIT_MISSING_FMAP_SECTION; + } + + if (!cbfs_sections_primary_cbfs_accounted_for()) { + fprintf(stderr, + "FATAL: Flashmap descriptor must have a '%s' section that is annotated with '(%s)'\n", + SECTION_NAME_PRIMARY_CBFS, + SECTION_ANNOTATION_CBFS); + full_fmd_cleanup(&descriptor); + return FMAPTOOL_EXIT_MISSING_PRIMARY_CBFS; + } + struct fmap *flashmap = fmap_from_fmd(descriptor); if (!flashmap) { fputs("FATAL: Failed while constructing FMAP section\n", stderr); - fmd_cleanup(descriptor); + full_fmd_cleanup(&descriptor); return FMAPTOOL_EXIT_FAILED_FMAP_CONVERSION; } @@ -90,16 +204,16 @@ int main(int argc, char **argv) fputs("FATAL: Failed to determine FMAP section size\n", stderr); fmap_destroy(flashmap); - fmd_cleanup(descriptor); + full_fmd_cleanup(&descriptor); return FMAPTOOL_EXIT_UNKNOWN_FMAP_SIZE; } - FILE *fmap_file = fopen(fmap_filename, "wb"); + FILE *fmap_file = fopen(args.fmap_filename, "wb"); if (!fmap_file) { fprintf(stderr, "FATAL: Unable to open file '%s' for writing\n", - fmap_filename); + args.fmap_filename); fmap_destroy(flashmap); - fmd_cleanup(descriptor); + full_fmd_cleanup(&descriptor); return FMAPTOOL_EXIT_BAD_OUTPUT_PATH; } @@ -107,13 +221,24 @@ int main(int argc, char **argv) fputs("FATAL: Failed to write final FMAP to file\n", stderr); fclose(fmap_file); fmap_destroy(flashmap); - fmd_cleanup(descriptor); - return FMAPTOOL_EXIT_FAILED_WRITING_FILE; + full_fmd_cleanup(&descriptor); + return FMAPTOOL_EXIT_FAILED_WRITING_OUTPUT; } fclose(fmap_file); - printf("SUCCESS: Wrote %d bytes to file '%s'\n", size, fmap_filename); - fmap_destroy(flashmap); - fmd_cleanup(descriptor); + + if (args.header_filename && + !write_header(args.header_filename, descriptor)) { + full_fmd_cleanup(&descriptor); + return FMAPTOOL_EXIT_FAILED_WRITING_HEADER; + } + + fprintf(stderr, "SUCCESS: Wrote %d bytes to file '%s'%s\n", size, + args.fmap_filename, + args.header_filename ? " (and generated header)" : ""); + fputs("The sections containing CBFSes are: ", stderr); + list_cbfs_section_names(); + + full_fmd_cleanup(&descriptor); return FMAPTOOL_EXIT_SUCCESS; }