| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2012 - 2017 Advanced Micro Devices, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <arch/acpi.h> |
| #include <cbfs.h> |
| #include <cbmem.h> |
| #include <rmodule.h> |
| #include <stage_cache.h> |
| #include <amdblocks/agesawrapper.h> |
| #include <amdblocks/image.h> |
| |
| static int agesa_locate_file(const char *name, struct region_device *rdev, |
| uint32_t type) |
| { |
| struct cbfsf fh; |
| |
| if (cbfs_boot_locate(&fh, name, &type)) |
| return -1; |
| |
| cbfs_file_data(rdev, &fh); |
| return 0; |
| } |
| |
| static int agesa_locate_raw_file(const char *name, struct region_device *rdev) |
| { |
| return agesa_locate_file(name, rdev, CBFS_TYPE_RAW); |
| } |
| |
| static int agesa_locate_stage_file_early(const char *name, |
| struct region_device *rdev) |
| { |
| const size_t metadata_sz = sizeof(struct cbfs_stage); |
| |
| if (agesa_locate_file(name, rdev, CBFS_TYPE_STAGE)) |
| return -1; |
| |
| /* Peel off the cbfs stage metadata. */ |
| return rdev_chain(rdev, rdev, metadata_sz, |
| region_device_sz(rdev) - metadata_sz); |
| } |
| |
| static int agesa_locate_stage_file_ramstage(const char *name, |
| struct region_device *rdev) |
| { |
| struct prog prog = PROG_INIT(PROG_REFCODE, name); |
| struct rmod_stage_load rmod_agesa = { |
| .cbmem_id = CBMEM_ID_REFCODE, |
| .prog = &prog, |
| }; |
| |
| if (acpi_is_wakeup_s3() && !IS_ENABLED(CONFIG_NO_STAGE_CACHE)) { |
| printk(BIOS_INFO, "AGESA: Loading stage from cache\n"); |
| // There is no way to tell if this succeeded. |
| stage_cache_load_stage(STAGE_REFCODE, &prog); |
| } else { |
| if (prog_locate(&prog)) |
| return -1; |
| |
| if (rmodule_stage_load(&rmod_agesa) < 0) |
| return -1; |
| |
| if (!IS_ENABLED(CONFIG_NO_STAGE_CACHE)) { |
| printk(BIOS_INFO, "AGESA: Saving stage to cache\n"); |
| stage_cache_add(STAGE_REFCODE, &prog); |
| } |
| } |
| |
| return rdev_chain(rdev, prog_rdev(&prog), 0, |
| region_device_sz(prog_rdev(&prog))); |
| } |
| |
| static int agesa_locate_stage_file(const char *name, struct region_device *rdev) |
| { |
| if (!ENV_RAMSTAGE || !IS_ENABLED(CONFIG_AGESA_SPLIT_MEMORY_FILES)) |
| return agesa_locate_stage_file_early(name, rdev); |
| return agesa_locate_stage_file_ramstage(name, rdev); |
| } |
| |
| static const char *get_agesa_cbfs_name(void) |
| { |
| if (!IS_ENABLED(CONFIG_AGESA_SPLIT_MEMORY_FILES)) |
| return CONFIG_AGESA_CBFS_NAME; |
| if (!ENV_RAMSTAGE) |
| return CONFIG_AGESA_PRE_MEMORY_CBFS_NAME; |
| return CONFIG_AGESA_POST_MEMORY_CBFS_NAME; |
| } |
| |
| const void *agesawrapper_locate_module(const char name[8]) |
| { |
| const void *agesa; |
| const AMD_IMAGE_HEADER *image; |
| struct region_device rdev; |
| size_t file_size; |
| const char *fname; |
| int ret; |
| |
| fname = get_agesa_cbfs_name(); |
| |
| if (IS_ENABLED(CONFIG_AGESA_BINARY_PI_AS_STAGE)) |
| ret = agesa_locate_stage_file(fname, &rdev); |
| else |
| ret = agesa_locate_raw_file(fname, &rdev); |
| |
| if (ret) |
| return NULL; |
| |
| file_size = region_device_sz(&rdev); |
| |
| /* Assume boot device is memory mapped so the mapping can leak. */ |
| assert(IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)); |
| |
| agesa = rdev_mmap_full(&rdev); |
| |
| if (!agesa) |
| return NULL; |
| |
| image = amd_find_image(agesa, agesa + file_size, 4096, name); |
| |
| if (!image) |
| return NULL; |
| |
| return (AMD_MODULE_HEADER *)image->ModuleInfoOffset; |
| } |
| |
| static MODULE_ENTRY agesa_dispatcher; |
| |
| MODULE_ENTRY agesa_get_dispatcher(void) |
| { |
| const AMD_MODULE_HEADER *module; |
| static const char id[8] = AGESA_ID; |
| |
| if (agesa_dispatcher != NULL) |
| return agesa_dispatcher; |
| |
| module = agesawrapper_locate_module(id); |
| if (!module) |
| return NULL; |
| |
| agesa_dispatcher = module->ModuleDispatcher; |
| return agesa_dispatcher; |
| } |