rmodule: add ability to calculate module placement

There is a need to calculate the proper placement for an rmodule
in memory. e.g. loading a compressed rmodule from flash into ram
can be an issue. Determining the placement is hard since the header
is not readable until it is decompressed so choosing the wrong location
may require a memmove() after decompression. This patch provides
a function to perform this calculation by finding region below a given
address while making an assumption on the size of the rmodule header..

Change-Id: I2703438f58ae847ed6e80b58063ff820fbcfcbc0
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/2788
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
diff --git a/src/lib/rmodule.c b/src/lib/rmodule.c
index 56d7c6d..81e9ef1 100644
--- a/src/lib/rmodule.c
+++ b/src/lib/rmodule.c
@@ -17,6 +17,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 #include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 #include <console/console.h>
 #include <rmodule.h>
@@ -165,6 +166,12 @@
 	       "filesize: 0x%x memsize: 0x%x\n",
 	       module->location, rmodule_entry(module),
 	       module->payload_size, rmodule_memory_size(module));
+
+	/* No need to copy the payload if the load location and the
+	 * payload location are the same. */
+	if (module->location == module->payload)
+		return;
+
 	memcpy(module->location, module->payload, module->payload_size);
 }
 
@@ -243,3 +250,47 @@
 	return rmodule_relocate(module);
 }
 
+void *rmodule_find_region_below(void *addr, size_t rmodule_size,
+                                void **program_start, void **rmodule_start)
+{
+	unsigned long ceiling;
+	unsigned long program_base;
+	unsigned long placement_loc;
+	unsigned long program_begin;
+
+	ceiling = (unsigned long)addr;
+	/* Place the rmodule just under the ceiling. The rmodule files
+	 * themselves are packed as a header and a payload, however the rmodule
+	 * itself is linked along with the header. The header starts at address
+	 * 0. Immediately following the header in the file is the program,
+	 * however its starting address is determined by the rmodule linker
+	 * script. In short, sizeof(struct rmodule_header) can be less than
+	 * or equal to the linked address of the program. Therefore we want
+	 * to place the rmodule so that the program falls on the aligned
+	 * address with the header just before it. Therefore, we need at least
+	 * a page to account for the size of the header. */
+	program_base = ALIGN((ceiling - (rmodule_size + 4096)), 4096);
+	/* The program starts immediately after the header. However,
+	 * it needs to be aligned to a 4KiB boundary. Therefore, adjust the
+	 * program location so that the program lands on a page boundary.  The
+	 * layout looks like the following:
+	 *
+	 * +--------------------------------+  ceiling
+	 * |  >= 0 bytes from alignment     |
+	 * +--------------------------------+  program end (4KiB aligned)
+	 * |  program size                  |
+	 * +--------------------------------+  program_begin (4KiB aligned)
+	 * |  sizeof(struct rmodule_header) |
+	 * +--------------------------------+  rmodule header start
+	 * |  >= 0 bytes from alignment     |
+	 * +--------------------------------+  program_base (4KiB aligned)
+	 */
+	placement_loc = ALIGN(program_base + sizeof(struct rmodule_header),
+	                      4096) - sizeof(struct rmodule_header);
+	program_begin = placement_loc + sizeof(struct rmodule_header);
+
+	*program_start = (void *)program_begin;
+	*rmodule_start = (void *)placement_loc;
+
+	return (void *)program_base;
+}