Add multi-architecture support to cbfstool

This is an initial re-factoring of CBFS code to enable multiple
architectures. To achieve a clean solution, an additional field
describing the architecture has to be added to the master header.
Hence we also increase the version number in the master header.

Change-Id: Icda681673221f8c27efbc46f16c2c5682b16a265
Signed-off-by: Stefan Reinauer <reinauer@google.com>
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Signed-off-by: David Hendricks <dhendrix@chromium.org>
Reviewed-on: http://review.coreboot.org/1944
Tested-by: build bot (Jenkins)
diff --git a/util/cbfstool/cbfs-mkpayload.c b/util/cbfstool/cbfs-mkpayload.c
index e4ef5c8..0d3986e 100644
--- a/util/cbfstool/cbfs-mkpayload.c
+++ b/util/cbfstool/cbfs-mkpayload.c
@@ -35,7 +35,7 @@
 			 comp_algo algo)
 {
 	Elf32_Phdr *phdr;
-	Elf32_Ehdr *ehdr;
+	Elf32_Ehdr *ehdr = (Elf32_Ehdr *) input;
 	Elf32_Shdr *shdr;
 	char *header;
 	char *strtab;
@@ -48,16 +48,20 @@
 	int i;
 
 	if(!iself(input)){
-		printf("Fatal error: the payload file is not in ELF format!\n");
-		exit(1);
+		fprintf(stderr, "E: The payload file is not in ELF format!\n");
+		return -1;
 	}
 
+	if (!((ehdr->e_machine == EM_ARM) && (arch == CBFS_ARCHITECTURE_ARMV7)) &&
+	    !((ehdr->e_machine == EM_386) && (arch == CBFS_ARCHITECTURE_X86))) {
+		fprintf(stderr, "E: The payload file has the wrong architecture\n");
+		return -1;
+	}
 
 	comp_func_ptr compress = compression_function(algo);
 	if (!compress)
 		return -1;
 
-	ehdr = (Elf32_Ehdr *) input;
 	headers = ehdr->e_phnum;
 	header = (char *)ehdr;
 
diff --git a/util/cbfstool/cbfs-mkstage.c b/util/cbfstool/cbfs-mkstage.c
index 55c81c6..0541d4b 100644
--- a/util/cbfstool/cbfs-mkstage.c
+++ b/util/cbfstool/cbfs-mkstage.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
  *               2009 coresystems GmbH
  *                 written by Patrick Georgi <patrick.georgi@coresystems.de>
+ * Copyright (C) 2012 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
@@ -67,7 +68,13 @@
 		return -1;
 
 	if (!iself(input)) {
-		fprintf(stderr, "E:  The incoming file is not an ELF\n");
+		fprintf(stderr, "E: The stage file is not in ELF format!\n");
+		return -1;
+	}
+
+	if (!((ehdr->e_machine == EM_ARM) && (arch == CBFS_ARCHITECTURE_ARMV7)) &&
+	    !((ehdr->e_machine == EM_386) && (arch == CBFS_ARCHITECTURE_X86))) {
+		fprintf(stderr, "E: The stage file has the wrong architecture\n");
 		return -1;
 	}
 
diff --git a/util/cbfstool/cbfs.h b/util/cbfstool/cbfs.h
index f161ed4..5251d65 100644
--- a/util/cbfstool/cbfs.h
+++ b/util/cbfstool/cbfs.h
@@ -21,6 +21,12 @@
 
 #include <stdint.h>
 
+#define CBFS_HEADER_MAGIC  0x4F524243
+#define CBFS_HEADPTR_ADDR_X86 0xFFFFFFFC
+#define VERSION1 0x31313131
+#define VERSION2 0x31313132
+#define VERSION  VERSION2
+
 struct cbfs_header {
 	uint32_t magic;
 	uint32_t version;
@@ -28,9 +34,14 @@
 	uint32_t bootblocksize;
 	uint32_t align;
 	uint32_t offset;
-	uint32_t pad[2];
+	uint32_t architecture;	/* Version 2 */
+	uint32_t pad[1];
 } __attribute__ ((packed));
 
+#define CBFS_ARCHITECTURE_UNKNOWN  0xFFFFFFFF
+#define CBFS_ARCHITECTURE_X86      0x00000001
+#define CBFS_ARCHITECTURE_ARMV7    0x00000010
+
 struct cbfs_file {
 	uint8_t magic[8];
 	uint32_t len;
@@ -94,7 +105,7 @@
  */
 #define CBFS_COMPONENT_NULL 0xFFFFFFFF
 
-int cbfs_file_header(uint32_t physaddr);
+int cbfs_file_header(unsigned long physaddr);
 struct cbfs_file *cbfs_create_empty_file(uint32_t physaddr, uint32_t size);
 
 #endif
diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c
index 66db379..a09e75b 100644
--- a/util/cbfstool/cbfstool.c
+++ b/util/cbfstool/cbfstool.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2009 coresystems GmbH
  *                 written by Patrick Georgi <patrick.georgi@coresystems.de>
+ * Copyright (C) 2012 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
@@ -372,6 +373,11 @@
 		return 1;
 	}
 
+	if (arch == CBFS_ARCHITECTURE_UNKNOWN) {
+		fprintf(stderr, "E: You need to specify -m/--machine arch\n");
+		return 1;
+	}
+
 	return create_cbfs_image(cbfs_name, rom_size, rom_bootblock,
 						rom_alignment, rom_offset);
 }
@@ -450,7 +456,7 @@
 	{"add-stage", "f:n:t:c:b:h?", cbfs_add_stage},
 	{"add-flat-binary", "f:n:l:e:c:b:h?", cbfs_add_flat_binary},
 	{"remove", "n:h?", cbfs_remove},
-	{"create", "s:B:a:o:h?", cbfs_create},
+	{"create", "s:B:a:o:m:h?", cbfs_create},
 	{"locate", "f:n:a:h?", cbfs_locate},
 	{"print", "h?", cbfs_print},
 	{"extract", "n:f:h?", cbfs_extract},
@@ -468,6 +474,7 @@
 	{"alignment",    required_argument, 0, 'a' },
 	{"offset",       required_argument, 0, 'o' },
 	{"file",         required_argument, 0, 'f' },
+	{"arch",         required_argument, 0, 'm' },
 	{"verbose",      no_argument,       0, 'v' },
 	{"help",         no_argument,       0, 'h' },
 	{NULL,           0,                 0,  0  }
@@ -481,26 +488,28 @@
 	     " %s FILE COMMAND [PARAMETERS]...\n\n" "OPTIONs:\n"
 	     "  -h		Display this help message\n\n"
 	     "COMMANDs:\n"
-	     " add -f FILE -n NAME -t TYPE [-b base-address]             "
+	     " add -f FILE -n NAME -t TYPE [-b base-address]               "
 			"Add a component\n"
-	     " add-payload -f FILE -n NAME [-c compression] [-b base]    "
+	     " add-payload -f FILE -n NAME [-c compression] [-b base]      "
 			"Add a payload to the ROM\n"
-	     " add-stage -f FILE -n NAME [-c compression] [-b base]      "
+	     " add-stage -f FILE -n NAME [-c compression] [-b base]        "
 			"Add a stage to the ROM\n"
 	     " add-flat-binary -f FILE -n NAME -l load-address \\\n"
-	     "        -e entry-point [-c compression] [-b base]          "
+	     "        -e entry-point [-c compression] [-b base]            "
 			"Add a 32bit flat mode binary\n"
-	     " remove -n NAME                                            "
+	     " remove -n NAME                                              "
 			"Remove a component\n"
-	     " create -s size -B bootblock [-a align] [-o offset]        "
+	     " create -s size -B bootblock -m ARCH [-a align] [-o offset]  "
 			"Create a ROM file\n"
-	     " locate -f FILE -n NAME -a align                           "
+	     " locate -f FILE -n NAME -a align                             "
 			"Find a place for a file of that size\n"
-	     " print                                                     "
+	     " print                                                       "
 			"Show the contents of the ROM\n"
-	     " extract -n NAME -f FILE                                   "
+	     " extract -n NAME -f FILE                                     "
 			"Extracts a raw payload from ROM\n"
 	     "\n"
+	     "ARCHes:\n"
+	     "  armv7, x86\n"
 	     "TYPEs:\n", name, name
 	    );
 	print_supported_filetypes();
@@ -610,6 +619,9 @@
 			case 'v':
 				verbose++;
 				break;
+			case 'm':
+				arch = string_to_arch(optarg);
+				break;
 			case 'h':
 			case '?':
 				usage(argv[0]);
diff --git a/util/cbfstool/common.c b/util/cbfstool/common.c
index 6c67c39..d29df60 100644
--- a/util/cbfstool/common.c
+++ b/util/cbfstool/common.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2009 coresystems GmbH
  *                 written by Patrick Georgi <patrick.georgi@coresystems.de>
+ * Copyright (C) 2012 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
@@ -73,16 +74,101 @@
 static uint32_t phys_start, phys_end, align;
 uint32_t romsize;
 void *offset;
+uint32_t arch = CBFS_ARCHITECTURE_UNKNOWN;
+
+static struct {
+	uint32_t arch;
+	const char *name;
+} arch_names[] = {
+	{ CBFS_ARCHITECTURE_ARMV7, "armv7" },
+	{ CBFS_ARCHITECTURE_X86, "x86" },
+	{ CBFS_ARCHITECTURE_UNKNOWN, "unknown" }
+};
+
+uint32_t string_to_arch(const char *arch_string)
+{
+	int i;
+	uint32_t ret = CBFS_ARCHITECTURE_UNKNOWN;
+
+	for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
+		if (!strcasecmp(arch_string, arch_names[i].name)) {
+			ret = arch_names[i].arch;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+const char *arch_to_string(uint32_t a)
+{
+	int i;
+	const char *ret = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
+		if (a == arch_names[i].arch) {
+			ret = arch_names[i].name;
+			break;
+		}
+	}
+
+	return ret;
+
+}
+
+int find_master_header(void *romarea, size_t size)
+{
+	size_t offset;
+
+	if (master_header)
+		return 0;
+
+	for (offset = 0; offset < size - sizeof(struct cbfs_header); offset++) {
+		struct cbfs_header *tmp = romarea + offset;
+
+		if (tmp->magic == ntohl(CBFS_HEADER_MAGIC)) {
+			master_header = tmp;
+			break;
+		}
+	}
+
+	return master_header ? 0 : 1;
+}
 
 void recalculate_rom_geometry(void *romarea)
 {
-	offset = romarea + romsize - 0x100000000ULL;
-	master_header = (struct cbfs_header *)
-	    phys_to_virt(*((uint32_t *) phys_to_virt(0xfffffffc)));
-	phys_start = (0 - romsize + ntohl(master_header->offset)) & 0xffffffff;
-	phys_end =
-	    (0 - ntohl(master_header->bootblocksize) -
-	     sizeof(struct cbfs_header)) & 0xffffffff;
+	if (find_master_header(romarea, romsize)) {
+		fprintf(stderr, "E: Cannot find master header\n");
+		exit(1);
+	}
+
+	/* Update old headers */
+	if (master_header->version == VERSION1 &&
+	    ntohl(master_header->architecture) == CBFS_ARCHITECTURE_UNKNOWN) {
+		printf("Updating CBFS master header to version 2\n");
+		master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
+	}
+
+	arch = ntohl(master_header->architecture);
+
+	switch (arch) {
+	case CBFS_ARCHITECTURE_ARMV7:
+		offset = romarea;
+		phys_start = (0 + ntohl(master_header->offset)) & 0xffffffff;
+		phys_end = romsize & 0xffffffff;
+		break;
+	case CBFS_ARCHITECTURE_X86:
+		offset = romarea + romsize - 0x100000000ULL;
+		phys_start = (0 - romsize + ntohl(master_header->offset)) &
+				0xffffffff;
+		phys_end = (0 - ntohl(master_header->bootblocksize) -
+		     sizeof(struct cbfs_header)) & 0xffffffff;
+		break;
+	default:
+		fprintf(stderr, "E: Unknown architecture\n");
+		exit(1);
+	}
+
 	align = ntohl(master_header->align);
 }
 
@@ -114,7 +200,7 @@
 	return 0;
 }
 
-int cbfs_file_header(uint32_t physaddr)
+int cbfs_file_header(unsigned long physaddr)
 {
 	/* maybe improve this test */
 	return (strncmp(phys_to_virt(physaddr), "LARCHIVE", 8) == 0);
@@ -188,9 +274,10 @@
 void print_cbfs_directory(const char *filename)
 {
 	printf
-	    ("%s: %d kB, bootblocksize %d, romsize %d, offset 0x%x\nAlignment: %d bytes\n\n",
+	    ("%s: %d kB, bootblocksize %d, romsize %d, offset 0x%x\n"
+	     "Alignment: %d bytes, architecture: %s\n\n",
 	     basename((char *)filename), romsize / 1024, ntohl(master_header->bootblocksize),
-	     romsize, ntohl(master_header->offset), align);
+	     romsize, ntohl(master_header->offset), align, arch_to_string(arch));
 	printf("%-30s %-10s %-12s Size\n", "Name", "Offset", "Type");
 	uint32_t current = phys_start;
 	while (current < phys_end) {
@@ -206,8 +293,8 @@
 			fname = "(empty)";
 
 		printf("%-30s 0x%-8x %-12s %d\n", fname,
-		       current - phys_start, strfiletype(ntohl(thisfile->type)),
-		       length);
+		       current - phys_start + ntohl(master_header->offset),
+		       strfiletype(ntohl(thisfile->type)), length);
 		current =
 		    ALIGN(current + ntohl(thisfile->len) +
 			  ntohl(thisfile->offset), align);
@@ -451,7 +538,7 @@
 }
 
 int create_cbfs_image(const char *romfile, uint32_t _romsize,
-		      const char *bootblock, uint32_t align, uint32_t offs)
+		const char *bootblock, uint32_t align, uint32_t offs)
 {
 	uint32_t bootblocksize = 0;
 	struct cbfs_header *master_header;
@@ -466,9 +553,6 @@
 	}
 	memset(romarea, 0xff, romsize);
 
-	// Set up physical/virtual mapping
-	offset = romarea + romsize - 0x100000000ULL;
-
 	if (align == 0)
 		align = 64;
 
@@ -481,24 +565,83 @@
 		return 1;
 	}
 
-	master_header =
-	    (struct cbfs_header *)(romarea + romsize - bootblocksize -
-				   sizeof(struct cbfs_header));
-	master_header->magic = ntohl(0x4f524243);
-	master_header->version = ntohl(0x31313131);
-	master_header->romsize = htonl(romsize);
-	master_header->bootblocksize = htonl(bootblocksize);
-	master_header->align = htonl(align);
-	master_header->offset = htonl(offs);
-	((uint32_t *) phys_to_virt(0xfffffffc))[0] =
-	    virt_to_phys(master_header);
+	// TODO(hungte) Replace magic numbers by named constants.
+	switch (arch) {
+	case CBFS_ARCHITECTURE_ARMV7:
+		/* Set up physical/virtual mapping */
+		offset = romarea;
 
-	recalculate_rom_geometry(romarea);
+		// should be aligned to align but then we need to dynamically
+		// create the jump to the bootblock
+		loadfile(bootblock, &bootblocksize, romarea + 0x20 +
+			 sizeof(struct cbfs_header), SEEK_SET);
+		master_header = (struct cbfs_header *)(romarea + 0x20);
+		uint32_t *arm_vec = (uint32_t *)romarea;
+		/*
+		 * Encoding for this branch instruction is:
+		 * 31:28 - condition (0xe for always/unconditional)
+		 * 27:24 - 0xa
+		 * 23: 0 - sign-extended offset (in multiples of 4)
+		 *
+		 * When executing the branch, the PC will read as the address
+		 * of current instruction + 8.
+		 */
+		arm_vec[0] = htonl(0x0e0000ea);  // branch to . + 64 bytes
 
-	cbfs_create_empty_file((0 - romsize + offs) & 0xffffffff,
-				   romsize - offs - bootblocksize -
-				   sizeof(struct cbfs_header) -
-				   sizeof(struct cbfs_file) - 16);
+		master_header->magic = ntohl(CBFS_HEADER_MAGIC);
+		master_header->version = ntohl(VERSION);
+		master_header->romsize = htonl(romsize);
+		master_header->bootblocksize = htonl(bootblocksize);
+		master_header->align = htonl(align);
+		master_header->offset = htonl(
+				ALIGN((0x40 + bootblocksize), align));
+		master_header->architecture = htonl(CBFS_ARCHITECTURE_ARMV7);
+
+		((uint32_t *) phys_to_virt(0x4))[0] =
+				virt_to_phys(master_header);
+
+		recalculate_rom_geometry(romarea);
+
+		cbfs_create_empty_file(
+				ALIGN((0x40 + bootblocksize), align),
+				romsize - ALIGN((bootblocksize + 0x40), align)
+				//- sizeof(struct cbfs_header)
+				- sizeof(struct cbfs_file) );
+		break;
+
+	case CBFS_ARCHITECTURE_X86:
+		// Set up physical/virtual mapping
+		offset = romarea + romsize - 0x100000000ULL;
+
+		loadfile(bootblock, &bootblocksize, romarea + romsize,
+			 SEEK_END);
+		master_header = (struct cbfs_header *)(romarea + romsize -
+				  bootblocksize - sizeof(struct cbfs_header));
+
+		master_header->magic = ntohl(CBFS_HEADER_MAGIC);
+		master_header->version = ntohl(VERSION);
+		master_header->romsize = htonl(romsize);
+		master_header->bootblocksize = htonl(bootblocksize);
+		master_header->align = htonl(align);
+		master_header->offset = htonl(offs);
+		master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
+
+		((uint32_t *) phys_to_virt(CBFS_HEADPTR_ADDR_X86))[0] =
+		    virt_to_phys(master_header);
+
+		recalculate_rom_geometry(romarea);
+
+		cbfs_create_empty_file((0 - romsize + offs) & 0xffffffff,
+				       romsize - offs - bootblocksize -
+				       sizeof(struct cbfs_header) -
+				       sizeof(struct cbfs_file) - 16);
+		break;
+
+	default:
+		// Should not happen.
+		fprintf(stderr, "E: You found a bug in cbfstool.\n");
+		exit(1);
+	}
 
 	writerom(romfile, romarea, romsize);
 	free(romarea);
diff --git a/util/cbfstool/common.h b/util/cbfstool/common.h
index 4fcc1ee..b5258a3 100644
--- a/util/cbfstool/common.h
+++ b/util/cbfstool/common.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2009 coresystems GmbH
  *                 written by Patrick Georgi <patrick.georgi@coresystems.de>
+ * Copyright (C) 2012 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
@@ -29,6 +30,10 @@
 extern void *offset;
 extern uint32_t romsize;
 extern int host_bigendian;
+extern uint32_t arch;
+
+const char *arch_to_string(uint32_t a);
+uint32_t string_to_arch(const char *arch_string);
 
 static inline void *phys_to_virt(uint32_t addr)
 {