summaryrefslogtreecommitdiff
path: root/src/lib/cbfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/cbfs.c')
-rw-r--r--src/lib/cbfs.c58
1 files changed, 47 insertions, 11 deletions
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c
index a3d08f33f4..6c44466cc6 100644
--- a/src/lib/cbfs.c
+++ b/src/lib/cbfs.c
@@ -120,10 +120,11 @@ void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor,
#include <rmodule.h>
#include <romstage_handoff.h>
/* When CONFIG_RELOCATABLE_RAMSTAGE is enabled and this file is being compiled
- * for the romstage the rmodule loader is used. The ramstage is placed just
- * below the cbemem location. */
+ * for the romstage, the rmodule loader is used. The ramstage is placed just
+ * below the cbmem location. */
-void * cbfs_load_stage(struct cbfs_media *media, const char *name)
+static void *load_stage_from_cbfs(struct cbfs_media *media, const char *name,
+ struct romstage_handoff *handoff)
{
struct cbfs_stage *stage;
struct rmodule ramstage;
@@ -131,7 +132,7 @@ void * cbfs_load_stage(struct cbfs_media *media, const char *name)
void *ramstage_base;
void *decompression_loc;
void *ramstage_loc;
- struct romstage_handoff *handoff;
+ void *entry_point;
stage = (struct cbfs_stage *)
cbfs_get_file_content(media, name, CBFS_TYPE_STAGE);
@@ -143,9 +144,10 @@ void * cbfs_load_stage(struct cbfs_media *media, const char *name)
if (cbmem_base == NULL)
return (void *) -1;
- ramstage_base = rmodule_find_region_below(cbmem_base, stage->memlen,
- &ramstage_loc,
- &decompression_loc);
+ ramstage_base =
+ rmodule_find_region_below(cbmem_base, stage->memlen,
+ &ramstage_loc,
+ &decompression_loc);
LOG("Decompressing stage %s @ 0x%p (%d bytes)\n",
name, decompression_loc, stage->memlen);
@@ -161,15 +163,49 @@ void * cbfs_load_stage(struct cbfs_media *media, const char *name)
if (rmodule_load(ramstage_loc, &ramstage))
return (void *) -1;
- handoff = romstage_handoff_find_or_add();
+ entry_point = rmodule_entry(&ramstage);
+
if (handoff) {
handoff->reserve_base = (uint32_t)ramstage_base;
handoff->reserve_size = (uint32_t)cbmem_base -
(uint32_t)ramstage_base;
- } else
- LOG("Couldn't allocate romstage handoff.\n");
+ /* Save an entire copy in RAM of the relocated ramstage for
+ * the S3 resume path. The size of the saved relocated ramstage
+ * is larger than necessary. It could be optimized by saving
+ * just the text/data segment of the ramstage. The rmodule
+ * API would need to be modified to expose these details. For
+ * the time being, just save the entire used region. */
+ memcpy((void *)(handoff->reserve_base - handoff->reserve_size),
+ (void *)handoff->reserve_base, handoff->reserve_size);
+ /* Update the size and base of the reserve region. */
+ handoff->reserve_base -= handoff->reserve_size;
+ handoff->reserve_size += handoff->reserve_size;
+ /* Save the entry point in the handoff area. */
+ handoff->ramstage_entry_point = (uint32_t)entry_point;
+ }
+
+ return entry_point;
+}
+
+void * cbfs_load_stage(struct cbfs_media *media, const char *name)
+{
+ struct romstage_handoff *handoff;
+
+ handoff = romstage_handoff_find_or_add();
+
+ if (handoff == NULL) {
+ LOG("Couldn't find or allocate romstage handoff.\n");
+ return load_stage_from_cbfs(media, name, handoff);
+ } else if (!handoff->s3_resume)
+ return load_stage_from_cbfs(media, name, handoff);
+
+ /* S3 resume path. Copy from the saved relocated program buffer to
+ * the running location. load_stage_from_cbfs() keeps a copy of the
+ * relocated program just below the relocated program. */
+ memcpy((void *)(handoff->reserve_base + (handoff->reserve_size / 2)),
+ (void *)handoff->reserve_base, handoff->reserve_size / 2);
- return rmodule_entry(&ramstage);
+ return (void *)handoff->ramstage_entry_point;
}
#else