blob: 35e4c410dacce8e326b273d27a27646a0a468904 [file] [log] [blame]
Angel Pons986d50e2020-04-02 23:48:53 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Stefan Reinauer9aea04a2012-03-30 12:01:06 -07002
Duncan Lauriefe970132016-01-26 16:30:36 -08003#include <bootstate.h>
Kyösti Mälkki64120762021-02-02 23:28:03 +02004#include <boot/coreboot_tables.h>
Duncan Lauriefe970132016-01-26 16:30:36 -08005#include <console/console.h>
Stefan Reinauer9aea04a2012-03-30 12:01:06 -07006#include <types.h>
Stefan Reinauer9aea04a2012-03-30 12:01:06 -07007#include <pc80/mc146818rtc.h>
Philipp Deppenwiesefea24292017-10-17 17:02:29 +02008#include <security/vboot/vbnv.h>
9#include <security/vboot/vbnv_layout.h>
Stefan Reinauer9aea04a2012-03-30 12:01:06 -070010
Hung-Te Linfe2fc832016-12-29 20:59:37 +080011static void clear_vbnv_battery_cutoff_flag(uint8_t *vbnv_copy)
12{
13 /*
14 * Currently battery cutoff is done in payload stage, which does not
15 * update backup VBNV. And doing battery cutoff will invalidate CMOS.
16 * This means for every reboot after cutoff, read_vbnv_cmos will reload
17 * backup VBNV and try to cutoff again, causing endless reboot loop.
18 * So we should always clear battery cutoff flag from loaded backup.
19 */
20 if (vbnv_copy[MISC_FLAGS_OFFSET] & MISC_FLAGS_BATTERY_CUTOFF_MASK) {
21 printk(BIOS_INFO, "VBNV: Remove battery cut-off request.\n");
22 vbnv_copy[MISC_FLAGS_OFFSET] &= ~MISC_FLAGS_BATTERY_CUTOFF_MASK;
23 regen_vbnv_crc(vbnv_copy);
24 }
25}
26
Aaron Durbin0990fbf2017-09-15 15:23:04 -060027/* Return non-zero if backup was used. */
28static int restore_from_backup(uint8_t *vbnv_copy)
29{
Julius Wernercd49cce2019-03-05 16:53:33 -080030 if (!CONFIG(VBOOT_VBNV_CMOS_BACKUP_TO_FLASH))
Aaron Durbin0990fbf2017-09-15 15:23:04 -060031 return 0;
32
33 printk(BIOS_INFO, "VBNV: CMOS invalid, restoring from flash\n");
34 read_vbnv_flash(vbnv_copy);
35
36 if (verify_vbnv(vbnv_copy)) {
37 clear_vbnv_battery_cutoff_flag(vbnv_copy);
38 save_vbnv_cmos(vbnv_copy);
39 printk(BIOS_INFO, "VBNV: Flash backup restored\n");
40 return 1;
41 }
42
43 printk(BIOS_INFO, "VBNV: Restore from flash failed\n");
44
45 return 0;
46}
47
Duncan Laurie88b28ad2016-01-25 17:13:27 -080048void read_vbnv_cmos(uint8_t *vbnv_copy)
Stefan Reinauer9aea04a2012-03-30 12:01:06 -070049{
50 int i;
51
Furquan Shaikh2a12e2e2016-07-25 11:48:03 -070052 for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++)
53 vbnv_copy[i] = cmos_read(CONFIG_VBOOT_VBNV_OFFSET + 14 + i);
Duncan Lauriefe970132016-01-26 16:30:36 -080054
Aaron Durbin0990fbf2017-09-15 15:23:04 -060055 /* Verify contents before attempting a restore from backup storage. */
56 if (verify_vbnv(vbnv_copy))
57 return;
Duncan Lauriefe970132016-01-26 16:30:36 -080058
Aaron Durbin0990fbf2017-09-15 15:23:04 -060059 restore_from_backup(vbnv_copy);
Aaron Durbinfd795622013-03-01 17:12:26 -060060}
61
Duncan Laurie88b28ad2016-01-25 17:13:27 -080062void save_vbnv_cmos(const uint8_t *vbnv_copy)
Aaron Durbinfd795622013-03-01 17:12:26 -060063{
64 int i;
65
Furquan Shaikh2a12e2e2016-07-25 11:48:03 -070066 for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++)
67 cmos_write(vbnv_copy[i], CONFIG_VBOOT_VBNV_OFFSET + 14 + i);
Aaron Durbinfd795622013-03-01 17:12:26 -060068}
Duncan Lauriefe970132016-01-26 16:30:36 -080069
Aaron Durbin0990fbf2017-09-15 15:23:04 -060070void vbnv_init_cmos(uint8_t *vbnv_copy)
71{
Elyes HAOUAS2119d0b2020-02-16 10:01:33 +010072 /* If no CMOS failure just defer to the normal read path for checking
Aaron Durbin0990fbf2017-09-15 15:23:04 -060073 vbnv contents' integrity. */
74 if (!vbnv_cmos_failed())
75 return;
76
Elyes HAOUAS2119d0b2020-02-16 10:01:33 +010077 /* In the case of CMOS failure force the backup. If backup wasn't used
78 force the vbnv CMOS to be reset. */
Aaron Durbin0990fbf2017-09-15 15:23:04 -060079 if (!restore_from_backup(vbnv_copy)) {
80 vbnv_reset(vbnv_copy);
81 /* This parallels the vboot_reference implementation. */
82 vbnv_copy[HEADER_OFFSET] = HEADER_SIGNATURE |
83 HEADER_FIRMWARE_SETTINGS_RESET |
84 HEADER_KERNEL_SETTINGS_RESET;
85 regen_vbnv_crc(vbnv_copy);
86 save_vbnv_cmos(vbnv_copy);
87 }
88}
89
Kyösti Mälkki64120762021-02-02 23:28:03 +020090void lb_table_add_vbnv_cmos(struct lb_header *header)
91{
92 struct lb_range *vbnv;
93
94 vbnv = (struct lb_range *)lb_new_record(header);
95 vbnv->tag = LB_TAG_VBNV;
96 vbnv->size = sizeof(*vbnv);
97 vbnv->range_start = CONFIG_VBOOT_VBNV_OFFSET + 14;
98 vbnv->range_size = VBOOT_VBNV_BLOCK_SIZE;
99}
100
Julius Wernercd49cce2019-03-05 16:53:33 -0800101#if CONFIG(VBOOT_VBNV_CMOS_BACKUP_TO_FLASH)
Patrick Georgi6b881b22016-02-09 14:01:08 +0100102static void back_up_vbnv_cmos(void *unused)
Duncan Lauriefe970132016-01-26 16:30:36 -0800103{
Furquan Shaikh2a12e2e2016-07-25 11:48:03 -0700104 uint8_t vbnv_cmos[VBOOT_VBNV_BLOCK_SIZE];
Duncan Lauriefe970132016-01-26 16:30:36 -0800105
106 /* Read current VBNV from CMOS. */
107 read_vbnv_cmos(vbnv_cmos);
108
109 /* Save to flash, will only be saved if different. */
110 save_vbnv_flash(vbnv_cmos);
111}
Patrick Georgi6b881b22016-02-09 14:01:08 +0100112BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, back_up_vbnv_cmos, NULL);
Duncan Lauriefe970132016-01-26 16:30:36 -0800113#endif