blob: f61785dd8ca55b356358ee19320b78d99ccd4fec [file] [log] [blame]
Furquan Shaikhf9be2d12020-06-08 12:30:40 -07001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3#include <acpi/acpi.h>
Felix Held21c46c02021-03-05 00:13:16 +01004#include <amdblocks/apob_cache.h>
Furquan Shaikhf9be2d12020-06-08 12:30:40 -07005#include <assert.h>
6#include <boot_device.h>
Raul E Rangel73e0f182021-07-12 09:21:51 -06007#include <bootstate.h>
Raul E Rangelfca58332021-06-25 11:15:41 -06008#include <commonlib/helpers.h>
Furquan Shaikhf9be2d12020-06-08 12:30:40 -07009#include <commonlib/region.h>
10#include <console/console.h>
Raul E Rangelfca58332021-06-25 11:15:41 -060011#include <delay.h>
Furquan Shaikhf9be2d12020-06-08 12:30:40 -070012#include <fmap.h>
Furquan Shaikhf9be2d12020-06-08 12:30:40 -070013#include <spi_flash.h>
14#include <stdint.h>
15#include <string.h>
Raul E Rangelfca58332021-06-25 11:15:41 -060016#include <thread.h>
17#include <timer.h>
Raul E Rangeld742b5e2021-06-10 14:39:01 -060018#include <timestamp.h>
Furquan Shaikhf9be2d12020-06-08 12:30:40 -070019
20#define DEFAULT_MRC_CACHE "RW_MRC_CACHE"
21/* PSP requires this value to be 64KiB */
22#define DEFAULT_MRC_CACHE_SIZE 0x10000
23
24#if !CONFIG_PSP_APOB_DRAM_ADDRESS
25#error Incorrect APOB configuration setting(s)
26#endif
27
28#define APOB_SIGNATURE 0x424F5041 /* 'APOB' */
29
30/* APOB_BASE_HEADER from AGESA */
31struct apob_base_header {
32 uint32_t signature; /* APOB signature */
33 uint32_t version; /* Version */
34 uint32_t size; /* APOB Size */
35 uint32_t offset_of_first_entry; /* APOB Header Size */
36};
37
38static bool apob_header_valid(const struct apob_base_header *apob_header_ptr, const char *where)
39{
40 if (apob_header_ptr->signature != APOB_SIGNATURE) {
41 printk(BIOS_WARNING, "Invalid %s APOB signature %x\n",
42 where, apob_header_ptr->signature);
43 return false;
44 }
45
46 if (apob_header_ptr->size == 0 || apob_header_ptr->size > DEFAULT_MRC_CACHE_SIZE) {
47 printk(BIOS_WARNING, "%s APOB data is too large %x > %x\n",
48 where, apob_header_ptr->size, DEFAULT_MRC_CACHE_SIZE);
49 return false;
50 }
51
52 return true;
53}
54
55static void *get_apob_dram_address(void)
56{
57 /*
58 * TODO: Find the APOB destination by parsing the PSP's tables
59 * (once vboot is implemented).
60 */
61 void *apob_src_ram = (void *)(uintptr_t)CONFIG_PSP_APOB_DRAM_ADDRESS;
62
63 if (apob_header_valid(apob_src_ram, "RAM") == false)
64 return NULL;
65
66 return apob_src_ram;
67}
68
Raul E Rangelce63dc42021-07-16 10:37:58 -060069static int get_nv_rdev(struct region_device *r)
Furquan Shaikhf9be2d12020-06-08 12:30:40 -070070{
Raul E Rangelce63dc42021-07-16 10:37:58 -060071 if (fmap_locate_area_as_rdev(DEFAULT_MRC_CACHE, r) < 0) {
Furquan Shaikhf9be2d12020-06-08 12:30:40 -070072 printk(BIOS_ERR, "Error: No APOB NV region is found in flash\n");
73 return -1;
74 }
75
76 return 0;
77}
78
Raul E Rangelfca58332021-06-25 11:15:41 -060079static struct apob_thread_context {
80 uint8_t buffer[DEFAULT_MRC_CACHE_SIZE] __attribute__((aligned(64)));
81 struct thread_handle handle;
82 struct region_device apob_rdev;
83} global_apob_thread;
84
85static enum cb_err apob_thread_entry(void *arg)
86{
87 ssize_t size;
88 struct apob_thread_context *thread = arg;
89
90 printk(BIOS_DEBUG, "APOB thread running\n");
91 size = rdev_readat(&thread->apob_rdev, thread->buffer, 0,
92 region_device_sz(&thread->apob_rdev));
93
94 printk(BIOS_DEBUG, "APOB thread done\n");
95
96 if (size == region_device_sz(&thread->apob_rdev))
97 return CB_SUCCESS;
98
99 return CB_ERR;
100}
101
102void start_apob_cache_read(void)
103{
104 struct apob_thread_context *thread = &global_apob_thread;
105
106 if (!CONFIG(COOP_MULTITASKING))
107 return;
108
109 /* We don't perform any comparison on S3 resume */
110 if (acpi_is_wakeup_s3())
111 return;
112
113 if (get_nv_rdev(&thread->apob_rdev) != 0)
114 return;
115
116 assert(ARRAY_SIZE(thread->buffer) == region_device_sz(&thread->apob_rdev));
117
118 printk(BIOS_DEBUG, "Starting APOB preload\n");
119 if (thread_run(&thread->handle, apob_thread_entry, thread))
120 printk(BIOS_ERR, "Failed to start APOB preload thread\n");
121}
122
Raul E Rangelce63dc42021-07-16 10:37:58 -0600123static void *get_apob_from_nv_rdev(struct region_device *read_rdev)
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700124{
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700125 struct apob_base_header apob_header;
126
Raul E Rangelce63dc42021-07-16 10:37:58 -0600127 if (rdev_readat(read_rdev, &apob_header, 0, sizeof(apob_header)) < 0) {
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700128 printk(BIOS_ERR, "Couldn't read APOB header!\n");
129 return NULL;
130 }
131
132 if (apob_header_valid(&apob_header, "ROM") == false) {
133 printk(BIOS_ERR, "No APOB NV data!\n");
134 return NULL;
135 }
136
137 assert(CONFIG(BOOT_DEVICE_MEMORY_MAPPED));
Raul E Rangelce63dc42021-07-16 10:37:58 -0600138 return rdev_mmap_full(read_rdev);
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700139}
140
141/* Save APOB buffer to flash */
Raul E Rangel73e0f182021-07-12 09:21:51 -0600142static void soc_update_apob_cache(void *unused)
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700143{
144 struct apob_base_header *apob_rom;
Raul E Rangelce63dc42021-07-16 10:37:58 -0600145 struct region_device read_rdev, write_rdev;
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700146 bool update_needed = false;
147 const struct apob_base_header *apob_src_ram;
148
149 /* Nothing to update in case of S3 resume. */
150 if (acpi_is_wakeup_s3())
151 return;
152
153 apob_src_ram = get_apob_dram_address();
154 if (apob_src_ram == NULL)
155 return;
156
Raul E Rangelce63dc42021-07-16 10:37:58 -0600157 if (get_nv_rdev(&read_rdev) != 0)
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700158 return;
159
Raul E Rangeld742b5e2021-06-10 14:39:01 -0600160 timestamp_add_now(TS_AMD_APOB_READ_START);
161
Raul E Rangelfca58332021-06-25 11:15:41 -0600162 if (CONFIG(COOP_MULTITASKING) && thread_join(&global_apob_thread.handle) == CB_SUCCESS)
163 apob_rom = (struct apob_base_header *)global_apob_thread.buffer;
164 else
165 apob_rom = get_apob_from_nv_rdev(&read_rdev);
166
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700167 if (apob_rom == NULL) {
168 update_needed = true;
169 } else if (memcmp(apob_src_ram, apob_rom, apob_src_ram->size)) {
170 printk(BIOS_INFO, "APOB RAM copy differs from flash\n");
171 update_needed = true;
172 } else
173 printk(BIOS_DEBUG, "APOB valid copy is already in flash\n");
174
Raul E Rangeld742b5e2021-06-10 14:39:01 -0600175 if (!update_needed) {
176 timestamp_add_now(TS_AMD_APOB_DONE);
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700177 return;
Raul E Rangeld742b5e2021-06-10 14:39:01 -0600178 }
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700179
Raul E Rangel72240432021-07-12 09:50:49 -0600180 printk(BIOS_SPEW, "Copy APOB from RAM %p/%#x to flash %#zx/%#zx\n",
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700181 apob_src_ram, apob_src_ram->size,
Raul E Rangelce63dc42021-07-16 10:37:58 -0600182 region_device_offset(&read_rdev), region_device_sz(&read_rdev));
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700183
Raul E Rangelce63dc42021-07-16 10:37:58 -0600184 if (fmap_locate_area_as_rdev_rw(DEFAULT_MRC_CACHE, &write_rdev) < 0) {
185 printk(BIOS_ERR, "Error: No RW APOB NV region is found in flash\n");
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700186 return;
187 }
188
Raul E Rangeld742b5e2021-06-10 14:39:01 -0600189 timestamp_add_now(TS_AMD_APOB_ERASE_START);
190
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700191 /* write data to flash region */
192 if (rdev_eraseat(&write_rdev, 0, DEFAULT_MRC_CACHE_SIZE) < 0) {
193 printk(BIOS_ERR, "Error: APOB flash region erase failed\n");
194 return;
195 }
196
Raul E Rangeld742b5e2021-06-10 14:39:01 -0600197 timestamp_add_now(TS_AMD_APOB_WRITE_START);
198
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700199 if (rdev_writeat(&write_rdev, apob_src_ram, 0, apob_src_ram->size) < 0) {
200 printk(BIOS_ERR, "Error: APOB flash region update failed\n");
201 return;
202 }
203
Raul E Rangeld742b5e2021-06-10 14:39:01 -0600204 timestamp_add_now(TS_AMD_APOB_DONE);
205
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700206 printk(BIOS_INFO, "Updated APOB in flash\n");
207}
208
209static void *get_apob_nv_address(void)
210{
Raul E Rangelce63dc42021-07-16 10:37:58 -0600211 struct region_device rdev;
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700212
Raul E Rangelce63dc42021-07-16 10:37:58 -0600213 if (get_nv_rdev(&rdev) != 0)
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700214 return NULL;
215
Raul E Rangelce63dc42021-07-16 10:37:58 -0600216 return get_apob_from_nv_rdev(&rdev);
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700217}
218
Felix Held21c46c02021-03-05 00:13:16 +0100219void *soc_fill_apob_cache(void)
Furquan Shaikhf9be2d12020-06-08 12:30:40 -0700220{
221 /* If this is non-S3 boot, then use the APOB data placed by PSP in DRAM. */
222 if (!acpi_is_wakeup_s3())
223 return get_apob_dram_address();
224
225 /*
226 * In case of S3 resume, PSP does not copy APOB data to DRAM. Thus, coreboot needs to
227 * provide the APOB NV data from RW_MRC_CACHE on SPI flash so that FSP can use it
228 * without having to traverse the BIOS directory table.
229 */
230 return get_apob_nv_address();
231}
Raul E Rangelfca58332021-06-25 11:15:41 -0600232
233/*
234 * BS_POST_DEVICE was chosen because this gives start_apob_cache_read plenty of time to read
235 * the APOB from SPI.
236 */
Raul E Rangel73e0f182021-07-12 09:21:51 -0600237BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, soc_update_apob_cache, NULL);