diff --git a/src/include/rmodule.h b/src/include/rmodule.h index 5300c63766..2d8fc0fdc8 100644 --- a/src/include/rmodule.h +++ b/src/include/rmodule.h @@ -20,6 +20,7 @@ #define RMODULE_H #include +#include #define RMODULE_MAGIC 0xf8fe #define RMODULE_VERSION_1 1 @@ -40,6 +41,13 @@ int rmodule_entry_offset(const struct rmodule *m); int rmodule_memory_size(const struct rmodule *m); int rmodule_load(void *loc, struct rmodule *m); int rmodule_load_alignment(const struct rmodule *m); +/* Returns the an aligned pointer that reflects a region used below addr + * based on the rmodule_size. i.e. the returned pointer up to addr is memory + * that may be utilized by the rmodule. program_start and rmodule_start + * are pointers updated to reflect where the rmodule program starts and where + * the rmodule (including header) should be placed respectively. */ +void *rmodule_find_region_below(void *addr, size_t rmodule_size, + void **program_start, void **rmodule_start); #define FIELD_ENTRY(x_) ((u32)&x_) #define RMODULE_HEADER(entry_, type_) \ 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 +#include #include #include #include @@ -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; +}