
This patch adds cbfs support for XIP stages. An XIP stage is code in 
ROM that is not copied anywhere; is is eXecuted In Place. 
Such support is necessary if we are to have ROM code that is visible in 
cbfs. This change will also, over time, let us remove trickiness from 
the linker scripts. 

The tools/cbfs-mkstage tool parses an ELF file and returns a stream of 
bytes that is a stage. As opposed to a payload, a stage is a single binary 
object, with a header indicating start address, size, and entry. 

We modify the cbfs fs.c code and the cbfs-mkstage tool so that, if the load
address of the stage is in the top 4M, it is assumed to be XIP. We 
add a new function, cbfs_find_area, which given a (loadaddr, size) pair, 
searches the cbfs for an open area. The tool does as much checking as it
can, looking for too small ROM, overlaps, and so on. 

I've done my best to make the code as clear as I can; it is hence
not utterly elegant, but I hope the tradeoff in readability is worth it. 

We are one step closer to getting rid of the weird math in the Config.lb 
files. That said, this change will also be useful if we ever get to Kconfig. 

I don't have a way to test this yet, but bug reports are still welcome. 
I have done a lot of 'hexdump' of cbfs images and the images seem to be 
correct. But until we start testing, I'm not convinced. 

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>

Example usage: 
[rminnich@xcpu2 cbfstool]$ ./cbfstool testcbfs create 1048576 2048 /etc/hosts
[rminnich@xcpu2 cbfstool]$ ./cbfstool testcbfs add-stage testfixed t
[rminnich@xcpu2 cbfstool]$ ./cbfstool testcbfs print
testcbfs: 1024 kB, bootblocksize 2048, romsize 1048576, offset 0x0
Alignment: 16 bytes

Name                           Offset     Type         Size
t                              0xeffb0    stage        23404

Note that the offset of 't' is non-zero; it is placed in the FLASH such
that the data for it will be at 0xffff0000, which is what the ELF file says it 
should be. 

Index: fs.c
===================================================================
--- fs.c	(revision 4251)
+++ fs.c	(working copy)
@@ -49,6 +49,88 @@
 			      ntohl(rom->header->align)));
 }
 
+/**
+ * find an area of the rom that will allow a segment starting at 
+ * loadaddress for size size to fit in. 
+ * @param rom rom data structure
+ * @param loadaddress load address, usually derived from a stage. 
+ * N.B. Includes the cbfs header. 
+ * @param size the size of the area. 
+ * @return pointer to the area or NULL
+ */
+struct cbfs_file *rom_find_area(struct rom *rom, unsigned long loadaddress, unsigned long size)
+{
+	unsigned long rombase = 0 - ntohl(rom->header->romsize);
+
+	unsigned int offset = ntohl(rom->header->offset);
+	unsigned long loadoffset;
+	struct cbfs_file *c;
+
+	if (rombase > loadaddress) {
+		fprintf(stderr, 
+		"rom_find_area: loadaddress is %lx, rombase is %lx: ROM is too small\n", 
+				loadaddress, rombase);
+		return NULL;
+	}
+
+	if (size > rom->fssize) {
+		fprintf(stderr, 
+			"rom_find_area: size is %ld, rom->fssize is %d: ROM is too small\n", 
+				size, rom->fssize);
+		return NULL;
+	}
+
+	/* Convert the loadaddress into an offset in the ROM.
+	 * The ROM has to be large enough
+	 */
+	loadoffset = loadaddress - rombase;
+
+	/* now see if that area is clear. 
+	 * Walk all the headers at the start, until we find empty
+	 * space. Then, make sure there are no cbfs headers in that "empty" space. 
+	 * If there are none, we are OK
+	 */
+	while (offset < loadoffset) {
+
+		c = (struct cbfs_file *)ROM_PTR(rom, offset);
+
+		if (!strcmp(c->magic, COMPONENT_MAGIC)) {
+			offset += ALIGN(ntohl(c->offset) + ntohl(c->len),
+					ntohl(rom->header->align));
+		} else
+			offset += ntohl(rom->header->align);
+	}
+
+	/* is offset > loadoffset? No room */
+	if (offset > loadoffset) {
+		fprintf(stderr, 
+			"rom_find_area:  the desired loadoffset is in the middle of a file\n");
+		return NULL;
+	}
+
+	/* is there something already there? */
+	if (!strcmp(c->magic, COMPONENT_MAGIC)) {
+		fprintf(stderr, "rom_find_area: there is a file at the desired loadoffset\n");
+		return NULL;
+	}
+
+	/* now search for any file that might be in the area. */
+	while (offset < loadoffset+size) {
+		struct cbfs_file *c =
+		    (struct cbfs_file *)ROM_PTR(rom, offset);
+
+		if (!strcmp(c->magic, COMPONENT_MAGIC)) {
+			fprintf(stderr, 
+				"rom_find_area: file in the middle of the area (%lx,%lx)\n",
+				loadaddress, loadaddress+size);
+			return NULL;
+		} else
+			offset += ntohl(rom->header->align);
+	}
+	
+	return (struct cbfs_file *)ROM_PTR(rom, loadoffset);
+}
+
 struct cbfs_file *rom_find_empty(struct rom *rom)
 {
 	unsigned int offset = ntohl(rom->header->offset);
@@ -152,28 +234,65 @@
 
 int rom_add(struct rom *rom, const char *name, void *buffer, int size, int type)
 {
-	struct cbfs_file *c = rom_find_empty(rom);
+	struct cbfs_file *c;
+	unsigned int cbfs_file_address; 
+	unsigned int total_size;
 	unsigned int offset;
 	unsigned int csize;
+	unsigned long loadaddress = 0;
+	struct cbfs_stage *stage;
+	int align = ntohl(rom->header->align);
 
 	if (rom_find_by_name(rom, name)) {
 		ERROR("Component %s already exists in this rom\n", name);
 		return -1;
 	}
 
+	csize = sizeof(struct cbfs_file) + ALIGN(strlen(name) + 1, 16);
+	total_size = csize + size;
+
+	if (type == CBFS_COMPONENT_STAGE) {
+		stage = (struct cbfs_stage *)buffer;
+		/* top 4M by standard is reserved for flash. If we are there, then we are
+		 * XIP
+		 */
+		if (stage->load > 0xffc00000ULL) {
+			loadaddress = (unsigned long) stage->load;
+			/* the loadaddress needs to align the data.
+			 */
+			loadaddress -= sizeof(*stage);
+			/* Now we have the stage loadaddress. */
+			/* We have to compute the cbfs address */
+			cbfs_file_address = loadaddress;
+			cbfs_file_address -= csize;
+			/* now have to round cbfs_file_address DOWN to align. 
+ 			 * Which is easy; & */
+			cbfs_file_address &= ~(align-1);
+			/* the total size may be bigger then the sum
+ 			 * of the parts
+ 			 */
+			total_size = (loadaddress + size) - cbfs_file_address;
+			/* Have to now adjust csize */
+			csize = loadaddress - cbfs_file_address;
+		}
+	} 
+
+	if (! loadaddress)
+		c = rom_find_empty(rom);
+	else
+		c = rom_find_area(rom, cbfs_file_address, total_size);
+
 	if (c == NULL) {
 		ERROR("There is no more room in this ROM\n");
 		return -1;
 	}
 
-	csize = sizeof(struct cbfs_file) + ALIGN(strlen(name) + 1, 16);
-
 	offset = ROM_OFFSET(rom, c);
 
-	if (offset + csize + size > rom->fssize) {
+	if (offset + total_size > rom->fssize) {
 		ERROR("There is not enough room in this ROM for this\n");
 		ERROR("component. I need %d bytes, only have %d bytes avail\n",
-		      csize + size, rom->fssize - offset);
+		      total_size, rom->fssize - offset);
 
 		return -1;
 	}
Index: tools/cbfs-mkstage.c
===================================================================
--- tools/cbfs-mkstage.c	(revision 4251)
+++ tools/cbfs-mkstage.c	(working copy)
@@ -118,6 +118,11 @@
 
 	stage->load = data_start;
 	stage->memlen = mem_end - data_start;
+	/* if this is in the top 4M, i.e. it is XIP, there is no compression */
+	if (stage->load > 0xffc00000){
+		compress = none_compress;
+		mode = CBFS_COMPRESS_NONE;
+	}
 	stage->compression = mode;
 	stage->entry = ehdr->e_entry;
 
@@ -193,6 +198,7 @@
 		break;
 	}
 
+	/* this really needs a bit of refactoring */
 	osize = parse_elf(buffer, &obuffer, algo, compress);
 
 	if (osize == -1) {
