blob: 8e3eb687741cdc76bf286c80e6a029021368f2b6 [file] [log] [blame]
Andrey Petrov465fc132016-02-25 14:16:33 -08001/*
2 * This file is part of the coreboot project.
3 *
Lee Leahy47bd2d92016-07-24 18:12:16 -07004 * Copyright (C) 2015-2016 Intel Corp.
Andrey Petrov465fc132016-02-25 14:16:33 -08005 * (Written by Andrey Petrov <andrey.petrov@intel.com> for Intel Corp.)
6 * (Written by Alexandru Gagniuc <alexandrux.gagniuc@intel.com> for Intel Corp.)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <arch/io.h>
15#include <arch/cpu.h>
Aaron Durbinb4302502016-07-17 17:04:37 -050016#include <arch/symbols.h>
Aaron Durbind04639b2016-07-17 23:23:59 -050017#include <cbfs.h>
Aaron Durbin27928682016-07-15 22:32:28 -050018#include <cbmem.h>
Andrey Petrov465fc132016-02-25 14:16:33 -080019#include <console/console.h>
20#include <fsp/api.h>
21#include <fsp/util.h>
22#include <memrange.h>
Aaron Durbind04639b2016-07-17 23:23:59 -050023#include <program_loading.h>
Aaron Durbinb4302502016-07-17 17:04:37 -050024#include <reset.h>
25#include <romstage_handoff.h>
26#include <soc/intel/common/mrc_cache.h>
Andrey Petrov465fc132016-02-25 14:16:33 -080027#include <string.h>
Aaron Durbind04639b2016-07-17 23:23:59 -050028#include <symbols.h>
Andrey Petrov465fc132016-02-25 14:16:33 -080029#include <timestamp.h>
Furquan Shaikh0325dc62016-07-25 13:02:36 -070030#include <vboot/vboot_common.h>
Andrey Petrov465fc132016-02-25 14:16:33 -080031
32typedef asmlinkage enum fsp_status (*fsp_memory_init_fn)
33 (void *raminit_upd, void **hob_list);
34
Aaron Durbinf0ec8242016-07-18 11:24:36 -050035static void save_memory_training_data(bool s3wake, uint32_t fsp_version)
Aaron Durbinb4302502016-07-17 17:04:37 -050036{
Aaron Durbinb4302502016-07-17 17:04:37 -050037 size_t mrc_data_size;
38 const void *mrc_data;
Aaron Durbinf0ec8242016-07-18 11:24:36 -050039
40 if (!IS_ENABLED(CONFIG_CACHE_MRC_SETTINGS) || s3wake)
41 return;
42
43 mrc_data = fsp_find_nv_storage_data(&mrc_data_size);
44 if (!mrc_data) {
45 printk(BIOS_ERR, "Couldn't find memory training data HOB.\n");
46 return;
47 }
48
49 /*
50 * Save MRC Data to CBMEM. By always saving the data this forces
51 * a retrain after a trip through Chrome OS recovery path. The
52 * code which saves the data to flash doesn't write if the latest
53 * training data matches this one.
54 */
55 if (mrc_cache_stash_data_with_version(mrc_data, mrc_data_size,
56 fsp_version) < 0)
57 printk(BIOS_ERR, "Failed to stash MRC data\n");
58}
59
Furquan Shaikhaf8ef2a2016-07-24 08:48:34 -070060/*
61 * On every trip to recovery, newly generated MRC data is stored with this
62 * version since it is not expected to be a legit version. This ensures that on
63 * next normal boot, memory re-training occurs and new MRC data is stored.
64 */
65#define MRC_DEAD_VERSION (0xdeaddead)
66
Aaron Durbinf0ec8242016-07-18 11:24:36 -050067static enum fsp_status do_fsp_post_memory_init(void *hob_list_ptr, bool s3wake,
68 uint32_t fsp_version)
69{
70 struct range_entry fsp_mem;
Aaron Durbinb4302502016-07-17 17:04:37 -050071 struct romstage_handoff *handoff;
72
73 fsp_find_reserved_memory(&fsp_mem, hob_list_ptr);
74
75 /* initialize cbmem by adding FSP reserved memory first thing */
76 if (!s3wake) {
77 cbmem_initialize_empty_id_size(CBMEM_ID_FSP_RESERVED_MEMORY,
78 range_entry_size(&fsp_mem));
79 } else if (cbmem_initialize_id_size(CBMEM_ID_FSP_RESERVED_MEMORY,
80 range_entry_size(&fsp_mem))) {
81 if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
82 printk(BIOS_DEBUG, "Failed to recover CBMEM in S3 resume.\n");
83 /* Failed S3 resume, reset to come up cleanly */
84 hard_reset();
85 }
86 }
87
88 /* make sure FSP memory is reserved in cbmem */
89 if (range_entry_base(&fsp_mem) !=
90 (uintptr_t)cbmem_find(CBMEM_ID_FSP_RESERVED_MEMORY))
91 die("Failed to accommodate FSP reserved memory request");
92
93 /* Now that CBMEM is up, save the list so ramstage can use it */
94 fsp_save_hob_list(hob_list_ptr);
95
Furquan Shaikh0325dc62016-07-25 13:02:36 -070096 if (vboot_recovery_mode_enabled())
Furquan Shaikhaf8ef2a2016-07-24 08:48:34 -070097 fsp_version = MRC_DEAD_VERSION;
98
Aaron Durbinf0ec8242016-07-18 11:24:36 -050099 save_memory_training_data(s3wake, fsp_version);
Aaron Durbinb4302502016-07-17 17:04:37 -0500100
101 /* Create romstage handof information */
102 handoff = romstage_handoff_find_or_add();
103 if (handoff != NULL)
104 handoff->s3_resume = s3wake;
105 else
106 printk(BIOS_DEBUG, "Romstage handoff structure not added!\n");
107
108 return FSP_SUCCESS;
109}
110
Aaron Durbinf0ec8242016-07-18 11:24:36 -0500111static void fsp_fill_mrc_cache(struct FSPM_ARCH_UPD *arch_upd, bool s3wake,
112 uint32_t fsp_version)
Aaron Durbinb4302502016-07-17 17:04:37 -0500113{
114 const struct mrc_saved_data *mrc_cache;
115
Aaron Durbinf0ec8242016-07-18 11:24:36 -0500116 arch_upd->NvsBufferPtr = NULL;
117
118 if (!IS_ENABLED(CONFIG_CACHE_MRC_SETTINGS))
119 return;
120
Aaron Durbin98ea6362016-07-18 11:31:53 -0500121 /* Don't use saved training data when recovery mode is enabled. */
Furquan Shaikh0325dc62016-07-25 13:02:36 -0700122 if (vboot_recovery_mode_enabled()) {
Aaron Durbin98ea6362016-07-18 11:31:53 -0500123 printk(BIOS_DEBUG, "Recovery mode. Not using MRC cache.\n");
124 return;
125 }
126
Aaron Durbinf0ec8242016-07-18 11:24:36 -0500127 if (mrc_cache_get_current_with_version(&mrc_cache, fsp_version)) {
128 printk(BIOS_DEBUG, "MRC cache was not found\n");
129 return;
130 }
131
132 /* MRC cache found */
133 arch_upd->NvsBufferPtr = (void *)mrc_cache->data;
134 arch_upd->BootMode = s3wake ?
135 FSP_BOOT_ON_S3_RESUME:
136 FSP_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES;
137 printk(BIOS_DEBUG, "MRC cache found, size %x bootmode:%d\n",
138 mrc_cache->size, arch_upd->BootMode);
139}
140
Aaron Durbin02e504c2016-07-18 11:53:10 -0500141static enum cb_err check_region_overlap(const struct memranges *ranges,
142 const char *description,
143 uintptr_t begin, uintptr_t end)
Aaron Durbinf0ec8242016-07-18 11:24:36 -0500144{
Aaron Durbin02e504c2016-07-18 11:53:10 -0500145 const struct range_entry *r;
146
147 memranges_each_entry(r, ranges) {
148 if (end <= range_entry_base(r))
149 continue;
150 if (begin >= range_entry_end(r))
151 continue;
152 printk(BIOS_ERR, "'%s' overlaps currently running program: "
153 "[%p, %p)\n", description, (void *)begin, (void *)end);
154 return CB_ERR;
155 }
156
157 return CB_SUCCESS;
158}
159
160static enum cb_err fsp_fill_common_arch_params(struct FSPM_ARCH_UPD *arch_upd,
161 bool s3wake, uint32_t fsp_version,
162 const struct memranges *memmap)
163{
164 uintptr_t stack_begin;
165 uintptr_t stack_end;
166
Aaron Durbinb4302502016-07-17 17:04:37 -0500167 /*
168 * FSPM_UPD passed here is populated with default values provided by
169 * the blob itself. We let FSPM use top of CAR region of the size it
170 * requests.
Aaron Durbinb4302502016-07-17 17:04:37 -0500171 */
Aaron Durbin02e504c2016-07-18 11:53:10 -0500172 stack_end = (uintptr_t)_car_region_end;
173 stack_begin = stack_end - arch_upd->StackSize;
174
175 if (check_region_overlap(memmap, "FSPM stack", stack_begin,
176 stack_end) != CB_SUCCESS)
177 return CB_ERR;
178
179 arch_upd->StackBase = (void *)stack_begin;
Aaron Durbinb4302502016-07-17 17:04:37 -0500180
181 arch_upd->BootMode = FSP_BOOT_WITH_FULL_CONFIGURATION;
182
Aaron Durbinf0ec8242016-07-18 11:24:36 -0500183 fsp_fill_mrc_cache(arch_upd, s3wake, fsp_version);
Aaron Durbin02e504c2016-07-18 11:53:10 -0500184
185 return CB_SUCCESS;
Aaron Durbinb4302502016-07-17 17:04:37 -0500186}
187
Aaron Durbin02e504c2016-07-18 11:53:10 -0500188static enum fsp_status do_fsp_memory_init(struct fsp_header *hdr, bool s3wake,
189 const struct memranges *memmap)
Andrey Petrov465fc132016-02-25 14:16:33 -0800190{
191 enum fsp_status status;
192 fsp_memory_init_fn fsp_raminit;
193 struct FSPM_UPD fspm_upd, *upd;
Aaron Durbinb4302502016-07-17 17:04:37 -0500194 void *hob_list_ptr;
Aaron Durbin02e504c2016-07-18 11:53:10 -0500195 struct FSPM_ARCH_UPD *arch_upd;
Andrey Petrov465fc132016-02-25 14:16:33 -0800196
197 post_code(0x34);
198
199 upd = (struct FSPM_UPD *)(hdr->cfg_region_offset + hdr->image_base);
200
201 if (upd->FspUpdHeader.Signature != FSPM_UPD_SIGNATURE) {
202 printk(BIOS_ERR, "Invalid FSPM signature\n");
203 return FSP_INCOMPATIBLE_VERSION;
204 }
205
206 /* Copy the default values from the UPD area */
207 memcpy(&fspm_upd, upd, sizeof(fspm_upd));
208
Aaron Durbin02e504c2016-07-18 11:53:10 -0500209 arch_upd = &fspm_upd.FspmArchUpd;
210
Aaron Durbin27928682016-07-15 22:32:28 -0500211 /* Reserve enough memory under TOLUD to save CBMEM header */
Aaron Durbin02e504c2016-07-18 11:53:10 -0500212 arch_upd->BootLoaderTolumSize = cbmem_overhead_size();
Aaron Durbin27928682016-07-15 22:32:28 -0500213
Aaron Durbinb4302502016-07-17 17:04:37 -0500214 /* Fill common settings on behalf of chipset. */
Aaron Durbin02e504c2016-07-18 11:53:10 -0500215 if (fsp_fill_common_arch_params(arch_upd, s3wake, hdr->fsp_revision,
216 memmap) != CB_SUCCESS)
217 return FSP_NOT_FOUND;
Aaron Durbinb4302502016-07-17 17:04:37 -0500218
Andrey Petrov465fc132016-02-25 14:16:33 -0800219 /* Give SoC and mainboard a chance to update the UPD */
220 platform_fsp_memory_init_params_cb(&fspm_upd);
221
222 /* Call FspMemoryInit */
223 fsp_raminit = (void *)(hdr->image_base + hdr->memory_init_entry_offset);
224 printk(BIOS_DEBUG, "Calling FspMemoryInit: 0x%p\n", fsp_raminit);
225 printk(BIOS_SPEW, "\t%p: raminit_upd\n", &fspm_upd);
Aaron Durbinb4302502016-07-17 17:04:37 -0500226 printk(BIOS_SPEW, "\t%p: hob_list ptr\n", &hob_list_ptr);
Andrey Petrov465fc132016-02-25 14:16:33 -0800227
Alexandru Gagniucc4ea8f72016-05-23 12:16:58 -0700228 post_code(POST_FSP_MEMORY_INIT);
Andrey Petrov465fc132016-02-25 14:16:33 -0800229 timestamp_add_now(TS_FSP_MEMORY_INIT_START);
Aaron Durbinb4302502016-07-17 17:04:37 -0500230 status = fsp_raminit(&fspm_upd, &hob_list_ptr);
Alexandru Gagniucc4ea8f72016-05-23 12:16:58 -0700231 post_code(POST_FSP_MEMORY_INIT);
Andrey Petrov465fc132016-02-25 14:16:33 -0800232 timestamp_add_now(TS_FSP_MEMORY_INIT_END);
233
234 printk(BIOS_DEBUG, "FspMemoryInit returned 0x%08x\n", status);
235
Aaron Durbinf41f2aa2016-07-18 12:03:58 -0500236 /* Handle any resets requested by FSPM. */
237 fsp_handle_reset(status);
238
Aaron Durbinb4302502016-07-17 17:04:37 -0500239 if (status != FSP_SUCCESS)
240 return status;
241
Aaron Durbinf0ec8242016-07-18 11:24:36 -0500242 return do_fsp_post_memory_init(hob_list_ptr, s3wake, hdr->fsp_revision);
Andrey Petrov465fc132016-02-25 14:16:33 -0800243}
244
Aaron Durbind04639b2016-07-17 23:23:59 -0500245/* Load the binary into the memory specified by the info header. */
246static enum cb_err load_fspm_mem(struct fsp_header *hdr,
Aaron Durbin02e504c2016-07-18 11:53:10 -0500247 const struct region_device *rdev,
248 const struct memranges *memmap)
Aaron Durbind04639b2016-07-17 23:23:59 -0500249{
Aaron Durbind04639b2016-07-17 23:23:59 -0500250 uintptr_t fspm_begin;
251 uintptr_t fspm_end;
252
253 if (fsp_validate_component(hdr, rdev) != CB_SUCCESS)
254 return CB_ERR;
255
256 fspm_begin = hdr->image_base;
257 fspm_end = fspm_begin + hdr->image_size;
258
Aaron Durbin02e504c2016-07-18 11:53:10 -0500259 if (check_region_overlap(memmap, "FSPM", fspm_begin, fspm_end) !=
260 CB_SUCCESS)
Aaron Durbind04639b2016-07-17 23:23:59 -0500261 return CB_ERR;
Aaron Durbind04639b2016-07-17 23:23:59 -0500262
263 /* Load binary into memory at provided address. */
264 if (rdev_readat(rdev, (void *)fspm_begin, 0, fspm_end - fspm_begin) < 0)
265 return CB_ERR;
266
267 return CB_SUCCESS;
268}
269
270/* Handle the case when FSPM is running XIP. */
271static enum cb_err load_fspm_xip(struct fsp_header *hdr,
272 const struct region_device *rdev)
273{
274 void *base;
275
276 if (fsp_validate_component(hdr, rdev) != CB_SUCCESS)
277 return CB_ERR;
278
279 base = rdev_mmap_full(rdev);
280 if ((uintptr_t)base != hdr->image_base) {
281 printk(BIOS_ERR, "FSPM XIP base does not match: %p vs %p\n",
282 (void *)(uintptr_t)hdr->image_base, base);
283 return CB_ERR;
284 }
285
286 /*
287 * Since the component is XIP it's already in the address space. Thus,
288 * there's no need to rdev_munmap().
289 */
290 return CB_SUCCESS;
291}
292
293enum fsp_status fsp_memory_init(bool s3wake)
Andrey Petrov465fc132016-02-25 14:16:33 -0800294{
295 struct fsp_header hdr;
Aaron Durbind04639b2016-07-17 23:23:59 -0500296 enum cb_err status;
297 struct cbfsf file_desc;
298 struct region_device file_data;
299 const char *name = CONFIG_FSP_M_CBFS;
Aaron Durbin02e504c2016-07-18 11:53:10 -0500300 struct memranges memmap;
301 struct range_entry freeranges[2];
Andrey Petrov465fc132016-02-25 14:16:33 -0800302
Aaron Durbind04639b2016-07-17 23:23:59 -0500303 if (cbfs_boot_locate(&file_desc, name, NULL)) {
304 printk(BIOS_ERR, "Could not locate %s in CBFS\n", name);
Andrey Petrov465fc132016-02-25 14:16:33 -0800305 return FSP_NOT_FOUND;
Aaron Durbind04639b2016-07-17 23:23:59 -0500306 }
307
308 cbfs_file_data(&file_data, &file_desc);
309
Aaron Durbin02e504c2016-07-18 11:53:10 -0500310 /* Build up memory map of romstage address space including CAR. */
311 memranges_init_empty(&memmap, &freeranges[0], ARRAY_SIZE(freeranges));
312 memranges_insert(&memmap, (uintptr_t)_car_region_start,
313 _car_relocatable_data_end - _car_region_start, 0);
314 memranges_insert(&memmap, (uintptr_t)_program, _program_size, 0);
315
Lee Leahy27cd96a2016-07-21 11:16:39 -0700316 if (!IS_ENABLED(CONFIG_FSP_M_XIP))
Aaron Durbin02e504c2016-07-18 11:53:10 -0500317 status = load_fspm_mem(&hdr, &file_data, &memmap);
Aaron Durbind04639b2016-07-17 23:23:59 -0500318 else
319 status = load_fspm_xip(&hdr, &file_data);
320
321 if (status != CB_SUCCESS) {
322 printk(BIOS_ERR, "Loading FSPM failed.\n");
323 return FSP_NOT_FOUND;
324 }
325
326 /* Signal that FSP component has been loaded. */
327 prog_segment_loaded(hdr.image_base, hdr.image_size, SEG_FINAL);
Andrey Petrov465fc132016-02-25 14:16:33 -0800328
Aaron Durbin02e504c2016-07-18 11:53:10 -0500329 return do_fsp_memory_init(&hdr, s3wake, &memmap);
Andrey Petrov465fc132016-02-25 14:16:33 -0800330}