summaryrefslogtreecommitdiffstats
path: root/src/soc/amd/common/vboot/vboot_bootblock.c
blob: e3705d1d6a3a21219d66ec83778b9f79b68dae43 (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* SPDX-License-Identifier: GPL-2.0-only */

#include <amdblocks/reset.h>
#include <bootblock_common.h>
#include <console/cbmem_console.h>
#include <console/console.h>
#include <pc80/mc146818rtc.h>
#include <security/vboot/vbnv.h>
#include <security/vboot/symbols.h>
#include <soc/psp_transfer.h>
#include <timestamp.h>
#include <2struct.h>

static int transfer_buffer_valid(const struct transfer_info_struct *ptr)
{
	if (ptr->magic_val == TRANSFER_MAGIC_VAL && ptr->struct_bytes == sizeof(*ptr))
		return 1;
	else
		return 0;
}

void verify_psp_transfer_buf(void)
{
	if (*(uint32_t *)_vboot2_work == VB2_SHARED_DATA_MAGIC) {
		cmos_write(0x00, CMOS_RECOVERY_BYTE);
		return;
	}

	/*
	 * If CMOS is valid and the system has already been rebooted once, but
	 * still returns here, instead of rebooting to verstage again, assume
	 * that the system is in a reboot loop and halt.
	 */
	if ((!vbnv_cmos_failed()) && cmos_read(CMOS_RECOVERY_BYTE) ==
			CMOS_RECOVERY_MAGIC_VAL)
		die("Error: Reboot into recovery was unsuccessful.  Halting.");

	printk(BIOS_ERR, "VBOOT workbuf not valid.\n");
	printk(BIOS_DEBUG, "Signature: %#08x\n", *(uint32_t *)_vboot2_work);
	cmos_init(0);
	cmos_write(CMOS_RECOVERY_MAGIC_VAL, CMOS_RECOVERY_BYTE);
	warm_reset();
}

void show_psp_transfer_info(void)
{
	struct transfer_info_struct *info = (struct transfer_info_struct *)
			(void *)(uintptr_t)_transfer_buffer;

	if (transfer_buffer_valid(info)) {
		if ((info->psp_info & PSP_INFO_VALID) == 0) {
			printk(BIOS_INFO, "No PSP info found in transfer buffer.\n");
			return;
		}

		printk(BIOS_INFO, "PSP boot mode: %s\n",
				info->psp_info & PSP_INFO_PRODUCTION_MODE ?
				"Production" : "Development");
		printk(BIOS_INFO, "Silicon level: %s\n",
				info->psp_info & PSP_INFO_PRODUCTION_SILICON ?
				"Production" : "Pre-Production");
	}
}

static void setup_cbmem_console(const struct transfer_info_struct *info)
{

	void *cbmemc;
	size_t cbmemc_size;

	if (info->console_offset < sizeof(*info))
		return;

	if (info->timestamp_offset <= info->console_offset)
		return;

	cbmemc_size = info->timestamp_offset - info->console_offset;

	if (info->console_offset + cbmemc_size > info->buffer_size)
		return;

	cbmemc = (void *)((uintptr_t)info + info->console_offset);

	/* We need to manually initialize cbmemc so we can fill the new buffer. cbmemc_init()
	 * will also be called later in console_hw_init(), but it will be a no-op. */
	cbmemc_init();
	cbmemc_copy_in(cbmemc, cbmemc_size);
}

void boot_with_psp_timestamp(uint64_t base_timestamp)
{
	const struct transfer_info_struct *info = (const struct transfer_info_struct *)
		(void *)(uintptr_t)_transfer_buffer;

	if (!transfer_buffer_valid(info) || info->timestamp == 0)
		return;

	setup_cbmem_console(info);

	/*
	 * info->timestamp is PSP's timestamp (in microseconds)
	 * when x86 processor is released.
	 */
	uint64_t psp_last_ts = info->timestamp;

	int i;
	struct timestamp_table *psp_ts_table =
		(struct timestamp_table *)(void *)
		((uintptr_t)_transfer_buffer + info->timestamp_offset);
	/* new base_timestamp will be offset for all PSP timestamps. */
	base_timestamp -= psp_last_ts;

	for (i = 0; i < psp_ts_table->num_entries; i++) {
		struct timestamp_entry *tse = &psp_ts_table->entries[i];
		/*
		 * We ignore the time between x86 processor release and bootblock.
		 * Since timestamp_add subtracts base_time, we first add old base_time
		 * to make it absolute then add base_timestamp again since
		 * it'll be a new base_time.
		 *
		 * We don't need to convert unit since both PSP and coreboot
		 * will use 1us granularity.
		 *
		 */
		tse->entry_stamp += psp_ts_table->base_time + base_timestamp;
	}

	bootblock_main_with_timestamp(base_timestamp, psp_ts_table->entries,
				      psp_ts_table->num_entries);
}