summaryrefslogtreecommitdiff
path: root/src/vboot/vbnv_cmos.c
blob: 8bdcb31f9cdc94a37a2525c87f3964c4b5d7590f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2011 The ChromiumOS Authors.  All rights reserved.
 *
 * 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 <bootstate.h>
#include <console/console.h>
#include <types.h>
#include <pc80/mc146818rtc.h>
#include <vboot/vbnv.h>
#include <vboot/vbnv_layout.h>

static void clear_vbnv_battery_cutoff_flag(uint8_t *vbnv_copy)
{
	/*
	 * Currently battery cutoff is done in payload stage, which does not
	 * update backup VBNV. And doing battery cutoff will invalidate CMOS.
	 * This means for every reboot after cutoff, read_vbnv_cmos will reload
	 * backup VBNV and try to cutoff again, causing endless reboot loop.
	 * So we should always clear battery cutoff flag from loaded backup.
	 */
	if (vbnv_copy[MISC_FLAGS_OFFSET] & MISC_FLAGS_BATTERY_CUTOFF_MASK) {
		printk(BIOS_INFO, "VBNV: Remove battery cut-off request.\n");
		vbnv_copy[MISC_FLAGS_OFFSET] &= ~MISC_FLAGS_BATTERY_CUTOFF_MASK;
		regen_vbnv_crc(vbnv_copy);
	}
}

void read_vbnv_cmos(uint8_t *vbnv_copy)
{
	int i;

	for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++)
		vbnv_copy[i] = cmos_read(CONFIG_VBOOT_VBNV_OFFSET + 14 + i);

	if (IS_ENABLED(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH)) {
		if (verify_vbnv(vbnv_copy))
			return;

		printk(BIOS_INFO, "VBNV: CMOS invalid, restoring from flash\n");
		read_vbnv_flash(vbnv_copy);

		if (verify_vbnv(vbnv_copy)) {
			clear_vbnv_battery_cutoff_flag(vbnv_copy);
			save_vbnv_cmos(vbnv_copy);
			printk(BIOS_INFO, "VBNV: Flash backup restored\n");
		} else {
			printk(BIOS_INFO, "VBNV: Restore from flash failed\n");
		}
	}
}

void save_vbnv_cmos(const uint8_t *vbnv_copy)
{
	int i;

	for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++)
		cmos_write(vbnv_copy[i], CONFIG_VBOOT_VBNV_OFFSET + 14 + i);
}

#if IS_ENABLED(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH)
static void back_up_vbnv_cmos(void *unused)
{
	uint8_t vbnv_cmos[VBOOT_VBNV_BLOCK_SIZE];

	/* Read current VBNV from CMOS. */
	read_vbnv_cmos(vbnv_cmos);

	/* Save to flash, will only be saved if different. */
	save_vbnv_flash(vbnv_cmos);
}
BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, back_up_vbnv_cmos, NULL);
#endif