diff --git a/src/include/romstage_handoff.h b/src/include/romstage_handoff.h index c20b2614bd..4150e8e1cd 100644 --- a/src/include/romstage_handoff.h +++ b/src/include/romstage_handoff.h @@ -31,8 +31,16 @@ struct romstage_handoff { /* This indicates to the ramstage to reserve a chunk of memory. */ uint32_t reserve_base; uint32_t reserve_size; - /* Inidicate if the current boot is an S3 resume. */ + /* Inidicate if the current boot is an S3 resume. If + * CONFIG_RELOCTABLE_RAMSTAGE is enabled the chipset code is + * responsible for initializing this variable. Otherwise, ramstage + * will be re-loaded from cbfs (which can be slower since it lives + * in flash). */ uint32_t s3_resume; + /* The ramstage_entry_point is cached in the stag loading path. This + * cached value can only be utilized when the chipset code properly + * fills in the s3_resume field above. */ + uint32_t ramstage_entry_point; }; #if defined(__PRE_RAM__) 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 #include /* 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 rmodule_entry(&ramstage); + 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 (void *)handoff->ramstage_entry_point; } #else