• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <bootstate.h>
4 #include <boot/coreboot_tables.h>
5 #include <console/console.h>
6 #include <types.h>
7 #include <pc80/mc146818rtc.h>
8 #include <security/vboot/vbnv.h>
9 #include <security/vboot/vbnv_layout.h>
10 
clear_vbnv_battery_cutoff_flag(uint8_t * vbnv_copy)11 static 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 
27 /* Return non-zero if backup was used. */
restore_from_backup(uint8_t * vbnv_copy)28 static int restore_from_backup(uint8_t *vbnv_copy)
29 {
30 	if (!CONFIG(VBOOT_VBNV_CMOS_BACKUP_TO_FLASH))
31 		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 
read_vbnv_cmos(uint8_t * vbnv_copy)48 void read_vbnv_cmos(uint8_t *vbnv_copy)
49 {
50 	int i;
51 
52 	for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++)
53 		vbnv_copy[i] = cmos_read(CONFIG_VBOOT_VBNV_OFFSET + 14 + i);
54 
55 	/* Verify contents before attempting a restore from backup storage. */
56 	if (verify_vbnv(vbnv_copy))
57 		return;
58 
59 	restore_from_backup(vbnv_copy);
60 }
61 
save_vbnv_cmos(const uint8_t * vbnv_copy)62 void save_vbnv_cmos(const uint8_t *vbnv_copy)
63 {
64 	int i;
65 
66 	for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++)
67 		cmos_write(vbnv_copy[i], CONFIG_VBOOT_VBNV_OFFSET + 14 + i);
68 }
69 
vbnv_init_cmos(uint8_t * vbnv_copy)70 void vbnv_init_cmos(uint8_t *vbnv_copy)
71 {
72 	/* If no CMOS failure just defer to the normal read path for checking
73 	   vbnv contents' integrity. */
74 	if (!vbnv_cmos_failed())
75 		return;
76 
77 	/* In the case of CMOS failure force the backup. If backup wasn't used
78 	   force the vbnv CMOS to be reset. */
79 	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 
lb_table_add_vbnv_cmos(struct lb_header * header)90 void 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 
101 #if CONFIG(VBOOT_VBNV_CMOS_BACKUP_TO_FLASH)
back_up_vbnv_cmos(void * unused)102 static void back_up_vbnv_cmos(void *unused)
103 {
104 	uint8_t vbnv_cmos[VBOOT_VBNV_BLOCK_SIZE];
105 
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 }
112 BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, back_up_vbnv_cmos, NULL);
113 #endif
114