diff options
author | Aaron Durbin <adurbin@chromium.org> | 2013-02-08 17:05:36 -0600 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2013-03-21 17:53:20 +0100 |
commit | e8c866ad45d80de768c9422474449e171d133575 (patch) | |
tree | 7e302c3f0378fe87fa5c967d23f3059c0347ae4f /src | |
parent | 426ce4192bd127ceaab52d94468b66d718608572 (diff) |
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>
Diffstat (limited to 'src')
-rw-r--r-- | src/include/rmodule.h | 8 | ||||
-rw-r--r-- | src/lib/rmodule.c | 51 |
2 files changed, 59 insertions, 0 deletions
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 <stdint.h> +#include <stddef.h> #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 <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; +} |