blob: 3a9689645faa20057ea49cb2ed69ab3f14647c44 [file] [log] [blame]
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -05001/*
2 * This file is part of the coreboot project.
3 *
Duncan Lauried8c4f2b2014-04-22 10:46:06 -07004 * Copyright (C) 2014 Google Inc.
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -05005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -050014 */
15
Stefan Reinauer6a001132017-07-13 02:20:27 +020016#include <compiler.h>
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -050017#include <string.h>
Aaron Durbin31be2c92016-12-03 22:08:20 -060018#include <boot_device.h>
Aaron Durbin4d3de7e2015-09-02 17:34:04 -050019#include <bootstate.h>
Aaron Durbincb0c40d2017-12-14 13:53:14 -070020#include <bootmode.h>
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -050021#include <console/console.h>
22#include <cbmem.h>
Aaron Durbin7d9068f2016-11-04 10:07:14 -050023#include <elog.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050024#include <fmap.h>
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -050025#include <ip_checksum.h>
Aaron Durbin31be2c92016-12-03 22:08:20 -060026#include <region_file.h>
Philipp Deppenwiesefea24292017-10-17 17:02:29 +020027#include <security/vboot/vboot_common.h>
Aaron Durbincb0c40d2017-12-14 13:53:14 -070028#include <spi_flash.h>
Furquan Shaikh0325dc62016-07-25 13:02:36 -070029
Duncan Lauried8c4f2b2014-04-22 10:46:06 -070030#include "mrc_cache.h"
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -050031
Aaron Durbin31be2c92016-12-03 22:08:20 -060032#define DEFAULT_MRC_CACHE "RW_MRC_CACHE"
33#define VARIABLE_MRC_CACHE "RW_VAR_MRC_CACHE"
34#define RECOVERY_MRC_CACHE "RECOVERY_MRC_CACHE"
35#define UNIFIED_MRC_CACHE "UNIFIED_MRC_CACHE"
36
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -050037#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24))
38
Aaron Durbin31be2c92016-12-03 22:08:20 -060039struct mrc_metadata {
40 uint32_t signature;
41 uint32_t data_size;
42 uint16_t data_checksum;
43 uint16_t header_checksum;
44 uint32_t version;
Stefan Reinauer6a001132017-07-13 02:20:27 +020045} __packed;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -050046
Andrey Petrovef9a9ea2016-11-08 08:30:06 -080047enum result {
Aaron Durbin31be2c92016-12-03 22:08:20 -060048 UPDATE_FAILURE = -1,
Andrey Petrovef9a9ea2016-11-08 08:30:06 -080049 UPDATE_SUCCESS = 0,
50 ALREADY_UPTODATE = 1
51};
52
Aaron Durbin31be2c92016-12-03 22:08:20 -060053#define NORMAL_FLAG (1 << 0)
54#define RECOVERY_FLAG (1 << 1)
55
56struct cache_region {
57 const char *name;
58 uint32_t cbmem_id;
59 int type;
60 int elog_slot;
61 int flags;
62};
63
64static const struct cache_region recovery_training = {
65 .name = RECOVERY_MRC_CACHE,
66 .cbmem_id = CBMEM_ID_MRCDATA,
67 .type = MRC_TRAINING_DATA,
68 .elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_RECOVERY,
69#if IS_ENABLED(CONFIG_HAS_RECOVERY_MRC_CACHE)
70 .flags = RECOVERY_FLAG,
71#else
72 .flags = 0,
73#endif
74};
75
76static const struct cache_region normal_training = {
77 .name = DEFAULT_MRC_CACHE,
78 .cbmem_id = CBMEM_ID_MRCDATA,
79 .type = MRC_TRAINING_DATA,
80 .elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_NORMAL,
81 .flags = NORMAL_FLAG | RECOVERY_FLAG,
82};
83
84static const struct cache_region variable_data = {
85 .name = VARIABLE_MRC_CACHE,
86 .cbmem_id = CBMEM_ID_VAR_MRCDATA,
87 .type = MRC_VARIABLE_DATA,
88 .elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_VARIABLE,
89 .flags = NORMAL_FLAG | RECOVERY_FLAG,
90};
91
92/* Order matters here for priority in matching. */
93static const struct cache_region *cache_regions[] = {
94 &recovery_training,
95 &normal_training,
96 &variable_data,
97};
98
99static int lookup_region_by_name(const char *name, struct region *r)
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500100{
Aaron Durbin31be2c92016-12-03 22:08:20 -0600101 /* This assumes memory mapped boot media just under 4GiB. */
102 const uint32_t pointer_base_32bit = -CONFIG_ROM_SIZE;
Aaron Durbin0424c952015-03-28 23:56:22 -0500103
Aaron Durbin31be2c92016-12-03 22:08:20 -0600104 if (fmap_locate_area(name, r) == 0)
105 return 0;
Aaron Durbin0424c952015-03-28 23:56:22 -0500106
Alexandru Gagniuc810caa92016-04-12 14:52:29 -0700107 /* CHROMEOS builds must get their MRC cache from FMAP. */
Aaron Durbin31be2c92016-12-03 22:08:20 -0600108 if (IS_ENABLED(CONFIG_CHROMEOS)) {
109 printk(BIOS_ERR, "MRC: Chrome OS lookup failure.\n");
110 return -1;
111 }
112
113 if (!IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED))
Alexandru Gagniuc810caa92016-04-12 14:52:29 -0700114 return -1;
115
Aaron Durbin31be2c92016-12-03 22:08:20 -0600116 /* Base is in the form of a pointer. Make it an offset. */
117 r->offset = CONFIG_MRC_SETTINGS_CACHE_BASE - pointer_base_32bit;
118 r->size = CONFIG_MRC_SETTINGS_CACHE_SIZE;
Alexandru Gagniuc810caa92016-04-12 14:52:29 -0700119
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500120 return 0;
121}
122
Aaron Durbin31be2c92016-12-03 22:08:20 -0600123static const struct cache_region *lookup_region_type(int type)
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700124{
Aaron Durbin31be2c92016-12-03 22:08:20 -0600125 int i;
126 int flags;
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700127
Aaron Durbin31be2c92016-12-03 22:08:20 -0600128 if (vboot_recovery_mode_enabled())
129 flags = RECOVERY_FLAG;
130 else
131 flags = NORMAL_FLAG;
132
133 for (i = 0; i < ARRAY_SIZE(cache_regions); i++) {
134 if (cache_regions[i]->type != type)
135 continue;
136 if ((cache_regions[i]->flags & flags) == flags)
137 return cache_regions[i];
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700138 }
139
Aaron Durbin31be2c92016-12-03 22:08:20 -0600140 return NULL;
141}
142
143int mrc_cache_stash_data(int type, uint32_t version, const void *data,
144 size_t size)
145{
146 const struct cache_region *cr;
147 size_t cbmem_size;
148 struct mrc_metadata *md;
149
150 cr = lookup_region_type(type);
151 if (cr == NULL) {
152 printk(BIOS_ERR, "MRC: failed to add to cbmem for type %d.\n",
153 type);
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700154 return -1;
155 }
156
Aaron Durbin31be2c92016-12-03 22:08:20 -0600157 cbmem_size = sizeof(*md) + size;
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700158
Aaron Durbin31be2c92016-12-03 22:08:20 -0600159 md = cbmem_add(cr->cbmem_id, cbmem_size);
160
161 if (md == NULL) {
162 printk(BIOS_ERR, "MRC: failed to add '%s' to cbmem.\n",
163 cr->name);
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700164 return -1;
165 }
166
Aaron Durbin31be2c92016-12-03 22:08:20 -0600167 memset(md, 0, sizeof(*md));
168 md->signature = MRC_DATA_SIGNATURE;
169 md->data_size = size;
170 md->version = version;
171 md->data_checksum = compute_ip_checksum(data, size);
172 md->header_checksum = compute_ip_checksum(md, sizeof(*md));
173 memcpy(&md[1], data, size);
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700174
175 return 0;
176}
177
Aaron Durbin31be2c92016-12-03 22:08:20 -0600178static const struct cache_region *lookup_region(struct region *r, int type)
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700179{
Aaron Durbin31be2c92016-12-03 22:08:20 -0600180 const struct cache_region *cr;
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700181
Aaron Durbin31be2c92016-12-03 22:08:20 -0600182 cr = lookup_region_type(type);
183
184 if (cr == NULL) {
185 printk(BIOS_ERR, "MRC: failed to locate region type %d.\n",
186 type);
187 return NULL;
188 }
189
190 if (lookup_region_by_name(cr->name, r) < 0)
191 return NULL;
192
193 return cr;
194}
195
196static int mrc_header_valid(struct region_device *rdev, struct mrc_metadata *md)
197{
198 uint16_t checksum;
199 uint16_t checksum_result;
200 size_t size;
201
202 if (rdev_readat(rdev, md, 0, sizeof(*md)) < 0) {
203 printk(BIOS_ERR, "MRC: couldn't read metadata\n");
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700204 return -1;
205 }
206
Aaron Durbin31be2c92016-12-03 22:08:20 -0600207 if (md->signature != MRC_DATA_SIGNATURE) {
208 printk(BIOS_ERR, "MRC: invalid header signature\n");
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700209 return -1;
210 }
211
Aaron Durbin31be2c92016-12-03 22:08:20 -0600212 /* Compute checksum over header with 0 as the value. */
213 checksum = md->header_checksum;
214 md->header_checksum = 0;
215 checksum_result = compute_ip_checksum(md, sizeof(*md));
216
217 if (checksum != checksum_result) {
218 printk(BIOS_ERR, "MRC: header checksum mismatch: %x vs %x\n",
219 checksum, checksum_result);
220 return -1;
221 }
222
223 /* Put back original. */
224 md->header_checksum = checksum;
225
226 /* Re-size the region device according to the metadata as a region_file
227 * does block allocation. */
228 size = sizeof(*md) + md->data_size;
229 if (rdev_chain(rdev, rdev, 0, size) < 0) {
230 printk(BIOS_ERR, "MRC: size exceeds rdev size: %zx vs %zx\n",
231 size, region_device_sz(rdev));
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700232 return -1;
233 }
Duncan Lauried8c4f2b2014-04-22 10:46:06 -0700234
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500235 return 0;
236}
237
Aaron Durbin31be2c92016-12-03 22:08:20 -0600238static int mrc_data_valid(const struct region_device *rdev,
239 const struct mrc_metadata *md)
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500240{
Aaron Durbin31be2c92016-12-03 22:08:20 -0600241 void *data;
242 uint16_t checksum;
243 const size_t md_size = sizeof(*md);
244 const size_t data_size = md->data_size;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500245
Aaron Durbin31be2c92016-12-03 22:08:20 -0600246 data = rdev_mmap(rdev, md_size, data_size);
247 if (data == NULL) {
248 printk(BIOS_ERR, "MRC: mmap failure on data verification.\n");
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500249 return -1;
250 }
251
Aaron Durbin31be2c92016-12-03 22:08:20 -0600252 checksum = compute_ip_checksum(data, data_size);
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500253
Aaron Durbin31be2c92016-12-03 22:08:20 -0600254 rdev_munmap(rdev, data);
255 if (md->data_checksum != checksum) {
256 printk(BIOS_ERR, "MRC: data checksum mismatch: %x vs %x\n",
257 md->data_checksum, checksum);
258 return -1;
259 }
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500260
261 return 0;
262}
263
Aaron Durbin31be2c92016-12-03 22:08:20 -0600264static int mrc_cache_latest(const char *name,
265 const struct region_device *backing_rdev,
266 struct mrc_metadata *md,
267 struct region_file *cache_file,
268 struct region_device *rdev,
269 bool fail_bad_data)
Andrey Petrovef9a9ea2016-11-08 08:30:06 -0800270{
Aaron Durbin31be2c92016-12-03 22:08:20 -0600271 /* Init and obtain a handle to the file data. */
272 if (region_file_init(cache_file, backing_rdev) < 0) {
273 printk(BIOS_ERR, "MRC: region file invalid in '%s'\n", name);
274 return -1;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500275 }
276
Aaron Durbin31be2c92016-12-03 22:08:20 -0600277 /* Provide a 0 sized region_device from here on out so the caller
278 * has a valid yet unusable region_device. */
279 rdev_chain(rdev, backing_rdev, 0, 0);
280
281 /* No data to return. */
282 if (region_file_data(cache_file, rdev) < 0) {
283 printk(BIOS_ERR, "MRC: no data in '%s'\n", name);
284 return fail_bad_data ? -1 : 0;
285 }
286
287 /* Validate header and resize region to reflect actual usage on the
288 * saved medium (including metadata and data). */
289 if (mrc_header_valid(rdev, md) < 0) {
290 printk(BIOS_ERR, "MRC: invalid header in '%s'\n", name);
291 return fail_bad_data ? -1 : 0;
292 }
293
294 /* Validate Data */
295 if (mrc_data_valid(rdev, md) < 0) {
296 printk(BIOS_ERR, "MRC: invalid data in '%s'\n", name);
297 return fail_bad_data ? -1 : 0;
298 }
299
300 return 0;
301}
302
303int mrc_cache_get_current(int type, uint32_t version,
304 struct region_device *rdev)
305{
306 const struct cache_region *cr;
307 struct region region;
308 struct region_device read_rdev;
309 struct region_file cache_file;
310 struct mrc_metadata md;
311 size_t data_size;
312 const size_t md_size = sizeof(md);
313 const bool fail_bad_data = true;
314
315 cr = lookup_region(&region, type);
316
317 if (cr == NULL)
318 return -1;
319
320 if (boot_device_ro_subregion(&region, &read_rdev) < 0)
321 return -1;
322
323 if (mrc_cache_latest(cr->name, &read_rdev, &md, &cache_file, rdev,
324 fail_bad_data) < 0)
325 return -1;
326
327 if (version != md.version) {
328 printk(BIOS_INFO, "MRC: version mismatch: %x vs %x\n",
329 md.version, version);
330 return -1;
331 }
332
333 /* Re-size rdev to only contain the data. i.e. remove metadata. */
334 data_size = md.data_size;
335 return rdev_chain(rdev, rdev, md_size, data_size);
336}
337
338static bool mrc_cache_needs_update(const struct region_device *rdev,
339 const struct cbmem_entry *to_be_updated)
340{
341 void *mapping;
342 size_t size = region_device_sz(rdev);
343 bool need_update = false;
344
345 if (cbmem_entry_size(to_be_updated) != size)
346 return true;
347
348 mapping = rdev_mmap_full(rdev);
349
350 if (memcmp(cbmem_entry_start(to_be_updated), mapping, size))
351 need_update = true;
352
353 rdev_munmap(rdev, mapping);
354
355 return need_update;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500356}
357
Andrey Petrovef9a9ea2016-11-08 08:30:06 -0800358static void log_event_cache_update(uint8_t slot, enum result res)
Aaron Durbin7d9068f2016-11-04 10:07:14 -0500359{
360 const int type = ELOG_TYPE_MEM_CACHE_UPDATE;
361 struct elog_event_mem_cache_update event = {
Andrey Petrovef9a9ea2016-11-08 08:30:06 -0800362 .slot = slot
Aaron Durbin7d9068f2016-11-04 10:07:14 -0500363 };
364
Andrey Petrovef9a9ea2016-11-08 08:30:06 -0800365 /* Filter through interesting events only */
366 switch (res) {
Aaron Durbin31be2c92016-12-03 22:08:20 -0600367 case UPDATE_FAILURE:
Andrey Petrovef9a9ea2016-11-08 08:30:06 -0800368 event.status = ELOG_MEM_CACHE_UPDATE_STATUS_FAIL;
369 break;
370 case UPDATE_SUCCESS:
371 event.status = ELOG_MEM_CACHE_UPDATE_STATUS_SUCCESS;
372 break;
373 default:
374 return;
375 }
376
Aaron Durbin7d9068f2016-11-04 10:07:14 -0500377 if (elog_add_event_raw(type, &event, sizeof(event)) < 0)
378 printk(BIOS_ERR, "Failed to log mem cache update event.\n");
379}
380
Aaron Durbin31be2c92016-12-03 22:08:20 -0600381/* During ramstage this code purposefully uses incoherent transactions between
382 * read and write. The read assumes a memory-mapped boot device that can be used
383 * to quickly locate and compare the up-to-date data. However, when an update
384 * is required it uses the writeable region access to perform the update. */
385static void update_mrc_cache_by_type(int type)
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500386{
Aaron Durbin31be2c92016-12-03 22:08:20 -0600387 const struct cache_region *cr;
388 struct region region;
389 struct region_device read_rdev;
390 struct region_device write_rdev;
391 struct region_file cache_file;
392 struct mrc_metadata md;
393 const struct cbmem_entry *to_be_updated;
394 struct incoherent_rdev backing_irdev;
395 const struct region_device *backing_rdev;
396 struct region_device latest_rdev;
397 const bool fail_bad_data = false;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500398
Aaron Durbin31be2c92016-12-03 22:08:20 -0600399 cr = lookup_region(&region, type);
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500400
Aaron Durbin31be2c92016-12-03 22:08:20 -0600401 if (cr == NULL)
402 return;
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700403
Aaron Durbin31be2c92016-12-03 22:08:20 -0600404 to_be_updated = cbmem_entry_find(cr->cbmem_id);
405 if (to_be_updated == NULL) {
406 printk(BIOS_ERR, "MRC: No data in cbmem for '%s'.\n",
407 cr->name);
408 return;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500409 }
410
Aaron Durbin31be2c92016-12-03 22:08:20 -0600411 printk(BIOS_DEBUG, "MRC: Checking cached data update for '%s'.\n",
412 cr->name);
413
414 if (boot_device_ro_subregion(&region, &read_rdev) < 0)
415 return;
416
417 if (boot_device_rw_subregion(&region, &write_rdev) < 0)
418 return;
419
420 backing_rdev = incoherent_rdev_init(&backing_irdev, &region, &read_rdev,
421 &write_rdev);
422
423 if (backing_rdev == NULL)
424 return;
425
426 if (mrc_cache_latest(cr->name, backing_rdev, &md, &cache_file,
427 &latest_rdev, fail_bad_data) < 0)
428 return;
429
430 if (!mrc_cache_needs_update(&latest_rdev, to_be_updated)) {
431 log_event_cache_update(cr->elog_slot, ALREADY_UPTODATE);
432 return;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500433 }
434
Aaron Durbin31be2c92016-12-03 22:08:20 -0600435 printk(BIOS_DEBUG, "MRC: cache data '%s' needs update.\n", cr->name);
436
437 if (region_file_update_data(&cache_file,
438 cbmem_entry_start(to_be_updated),
439 cbmem_entry_size(to_be_updated)) < 0)
440 log_event_cache_update(cr->elog_slot, UPDATE_FAILURE);
441 else
442 log_event_cache_update(cr->elog_slot, UPDATE_SUCCESS);
443}
444
Aaron Durbincb0c40d2017-12-14 13:53:14 -0700445/* Read flash status register to determine if write protect is active */
446static int nvm_is_write_protected(void)
447{
448 u8 sr1;
449 u8 wp_gpio;
450 u8 wp_spi;
451
452 if (!IS_ENABLED(CONFIG_CHROMEOS))
453 return 0;
454
455 if (!IS_ENABLED(CONFIG_BOOT_DEVICE_SPI_FLASH))
456 return 0;
457
458 /* Read Write Protect GPIO if available */
459 wp_gpio = get_write_protect_state();
460
461 /* Read Status Register 1 */
462 if (spi_flash_status(boot_device_spi_flash(), &sr1) < 0) {
463 printk(BIOS_ERR, "Failed to read SPI status register 1\n");
464 return -1;
465 }
466 wp_spi = !!(sr1 & 0x80);
467
468 printk(BIOS_DEBUG, "SPI flash protection: WPSW=%d SRP0=%d\n",
469 wp_gpio, wp_spi);
470
471 return wp_gpio && wp_spi;
472}
473
474/* Apply protection to a range of flash */
475static int nvm_protect(const struct region *r)
476{
477 if (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT))
478 return 0;
479
480 if (!IS_ENABLED(CONFIG_BOOT_DEVICE_SPI_FLASH))
481 return 0;
482
483 return spi_flash_ctrlr_protect_region(boot_device_spi_flash(), r);
484}
485
Aaron Durbin31be2c92016-12-03 22:08:20 -0600486/* Protect mrc region with a Protected Range Register */
487static int protect_mrc_cache(const char *name)
488{
489 struct region region;
490
491 if (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT))
492 return 0;
493
494 if (lookup_region_by_name(name, &region) < 0) {
495 printk(BIOS_ERR, "MRC: Could not find region '%s'\n", name);
496 return -1;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500497 }
498
Aaron Durbin31be2c92016-12-03 22:08:20 -0600499 if (nvm_is_write_protected() <= 0) {
500 printk(BIOS_INFO, "MRC: NOT enabling PRR for '%s'.\n", name);
501 return 0;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500502 }
503
Aaron Durbin31be2c92016-12-03 22:08:20 -0600504 if (nvm_protect(&region) < 0) {
505 printk(BIOS_ERR, "MRC: ERROR setting PRR for '%s'.\n", name);
506 return -1;
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500507 }
508
Aaron Durbin31be2c92016-12-03 22:08:20 -0600509 printk(BIOS_INFO, "MRC: Enabled Protected Range on '%s'.\n", name);
510 return 0;
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700511}
Aaron Durbin7d9068f2016-11-04 10:07:14 -0500512
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700513static void protect_mrc_region(void)
514{
515 /*
516 * Check if there is a single unified region that encompasses both
517 * RECOVERY_MRC_CACHE and DEFAULT_MRC_CACHE. In that case protect the
518 * entire region using a single PRR.
519 *
520 * If we are not able to protect the entire region, try protecting
521 * individual regions next.
522 */
523 if (protect_mrc_cache(UNIFIED_MRC_CACHE) == 0)
524 return;
525
526 if (IS_ENABLED(CONFIG_HAS_RECOVERY_MRC_CACHE))
527 protect_mrc_cache(RECOVERY_MRC_CACHE);
528
529 protect_mrc_cache(DEFAULT_MRC_CACHE);
530}
531
Aaron Durbind09142c2016-12-14 15:36:56 -0600532static void invalidate_normal_cache(void)
533{
534 struct region_file cache_file;
535 struct region_device rdev;
536 const char *name = DEFAULT_MRC_CACHE;
537 const uint32_t invalid = ~MRC_DATA_SIGNATURE;
538
539 /* Invalidate only on recovery mode with retraining enabled. */
540 if (!vboot_recovery_mode_enabled())
541 return;
542 if (!vboot_recovery_mode_memory_retrain())
543 return;
544
545 if (fmap_locate_area_as_rdev_rw(name, &rdev) < 0) {
546 printk(BIOS_ERR, "MRC: Couldn't find '%s' region. Invalidation failed\n",
547 name);
548 return;
549 }
550
551 if (region_file_init(&cache_file, &rdev) < 0) {
552 printk(BIOS_ERR, "MRC: region file invalid for '%s'. Invalidation failed\n",
553 name);
554 return;
555 }
556
557 /* Push an update that consists of 4 bytes that is smaller than the
558 * MRC metadata as well as an invalid signature. */
559 if (region_file_update_data(&cache_file, &invalid, sizeof(invalid)) < 0)
560 printk(BIOS_ERR, "MRC: invalidation failed for '%s'.\n", name);
561}
562
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700563static void update_mrc_cache(void *unused)
564{
Aaron Durbin31be2c92016-12-03 22:08:20 -0600565 update_mrc_cache_by_type(MRC_TRAINING_DATA);
Andrey Petrovef9a9ea2016-11-08 08:30:06 -0800566
Aaron Durbin31be2c92016-12-03 22:08:20 -0600567 if (IS_ENABLED(CONFIG_MRC_SETTINGS_VARIABLE_DATA))
568 update_mrc_cache_by_type(MRC_VARIABLE_DATA);
Andrey Petrovef9a9ea2016-11-08 08:30:06 -0800569
Aaron Durbind09142c2016-12-14 15:36:56 -0600570 if (IS_ENABLED(CONFIG_MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN))
571 invalidate_normal_cache();
572
Furquan Shaikhcab1c012016-11-05 23:57:02 -0700573 protect_mrc_region();
Aaron Durbin9a7d7bc2013-09-07 00:41:48 -0500574}
575
Subrata Banik2d1dd592017-08-16 16:42:46 +0530576/*
577 * Ensures MRC training data is stored into SPI after PCI enumeration is done
578 * during BS_DEV_ENUMERATE-BS_ON_EXIT and lock down SPI protected ranges
579 * during BS_DEV_RESOURCES-BS_ON_EXIT.
580 */
581BOOT_STATE_INIT_ENTRY(BS_DEV_ENUMERATE, BS_ON_EXIT, update_mrc_cache, NULL);