summaryrefslogtreecommitdiff
path: root/src/lib/rmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/rmodule.c')
-rw-r--r--src/lib/rmodule.c51
1 files changed, 51 insertions, 0 deletions
diff --git a/src/lib/rmodule.c b/src/lib/rmodule.c
index 56d7c6d646..81e9ef10ed 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 @@ static void rmodule_copy_payload(const struct rmodule *module)
"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 @@ int rmodule_load(void *base, struct rmodule *module)
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;
+}