1 /* SPDX-License-Identifier: GPL-2.0-or-later */
4 #include <amdblocks/apob_cache.h>
6 #include <boot_device.h>
8 #include <commonlib/helpers.h>
9 #include <commonlib/region.h>
10 #include <console/console.h>
12 #include <fmap_config.h>
13 #include <security/vboot/vboot_common.h>
14 #include <spi_flash.h>
17 #include <timestamp.h>
21 #define DEFAULT_MRC_CACHE "RW_MRC_CACHE"
22 #define DEFAULT_MRC_CACHE_SIZE FMAP_SECTION_RW_MRC_CACHE_SIZE
24 #if CONFIG(HAS_RECOVERY_MRC_CACHE)
25 #define RECOVERY_MRC_CACHE "RECOVERY_MRC_CACHE"
26 #define RECOVERY_MRC_CACHE_SIZE FMAP_SECTION_RECOVERY_MRC_CACHE_SIZE
28 #define RECOVERY_MRC_CACHE DEFAULT_MRC_CACHE
29 #define RECOVERY_MRC_CACHE_SIZE DEFAULT_MRC_CACHE_SIZE
32 #if CONFIG(SOC_AMD_COMMON_BLOCK_APOB_HASH)
33 #define MRC_HASH_SIZE ((uint32_t)sizeof(uint64_t))
35 #define MRC_HASH_SIZE 0
37 #define MRC_HASH_OFFSET (DEFAULT_MRC_CACHE_SIZE-MRC_HASH_SIZE)
38 #define MRC_HASH_UNINITIALIZED 0xffffffffull
40 #if !CONFIG_PSP_APOB_DRAM_ADDRESS
41 #error Incorrect APOB configuration setting(s)
44 _Static_assert(CONFIG_PSP_APOB_DRAM_SIZE
== DEFAULT_MRC_CACHE_SIZE
,
45 "APOB DRAM reserved space != to MRC CACHE size - check your config");
47 _Static_assert(CONFIG_PSP_APOB_DRAM_SIZE
== RECOVERY_MRC_CACHE_SIZE
,
48 "APOB DRAM reserved space != to RECOVERY MRC CACHE size - check your config");
50 #define APOB_SIGNATURE 0x424F5041 /* 'APOB' */
52 /* APOB_BASE_HEADER from AGESA */
53 struct apob_base_header
{
54 uint32_t signature
; /* APOB signature */
55 uint32_t version
; /* Version */
56 uint32_t size
; /* APOB Size */
57 uint32_t offset_of_first_entry
; /* APOB Header Size */
60 static bool apob_header_valid(const struct apob_base_header
*apob_header_ptr
, const char *where
)
62 uint32_t size_plus_hash
= apob_header_ptr
->size
+ MRC_HASH_SIZE
;
64 if (apob_header_ptr
->signature
!= APOB_SIGNATURE
) {
65 printk(BIOS_WARNING
, "Invalid %s APOB signature %x\n",
66 where
, apob_header_ptr
->signature
);
70 if (apob_header_ptr
->size
== 0 || size_plus_hash
> DEFAULT_MRC_CACHE_SIZE
) {
71 printk(BIOS_WARNING
, "%s APOB data is too large (%x + %x) > %x\n",
72 where
, apob_header_ptr
->size
, MRC_HASH_SIZE
, DEFAULT_MRC_CACHE_SIZE
);
79 static void *get_apob_dram_address(void)
82 * TODO: Find the APOB destination by parsing the PSP's tables
83 * (once vboot is implemented).
85 void *apob_src_ram
= (void *)(uintptr_t)CONFIG_PSP_APOB_DRAM_ADDRESS
;
87 if (apob_header_valid(apob_src_ram
, "RAM") == false)
93 static enum cb_err
get_nv_rdev(struct region_device
*r
)
95 if (fmap_locate_area_as_rdev(vboot_recovery_mode_enabled() ?
96 RECOVERY_MRC_CACHE
: DEFAULT_MRC_CACHE
, r
) < 0) {
97 printk(BIOS_ERR
, "No APOB NV region is found in flash\n");
104 static enum cb_err
get_nv_rdev_rw(struct region_device
*r
)
106 if (fmap_locate_area_as_rdev_rw(vboot_recovery_mode_enabled() ?
107 RECOVERY_MRC_CACHE
: DEFAULT_MRC_CACHE
, r
) < 0) {
108 printk(BIOS_ERR
, "No APOB NV region is found in flash\n");
115 static struct apob_thread_context
{
116 uint8_t buffer
[DEFAULT_MRC_CACHE_SIZE
] __aligned(64);
117 struct thread_handle handle
;
118 struct region_device apob_rdev
;
119 } global_apob_thread
;
121 static enum cb_err
apob_thread_entry(void *arg
)
124 struct apob_thread_context
*thread
= arg
;
126 printk(BIOS_DEBUG
, "APOB thread running\n");
127 size
= rdev_readat(&thread
->apob_rdev
, thread
->buffer
, 0,
128 region_device_sz(&thread
->apob_rdev
));
130 printk(BIOS_DEBUG
, "APOB thread done\n");
132 if (size
== region_device_sz(&thread
->apob_rdev
))
138 void start_apob_cache_read(void)
140 struct apob_thread_context
*thread
= &global_apob_thread
;
142 if (!CONFIG(COOP_MULTITASKING
) || CONFIG(SOC_AMD_COMMON_BLOCK_APOB_HASH
))
145 /* We don't perform any comparison on S3 resume */
146 if (acpi_is_wakeup_s3())
149 if (get_nv_rdev(&thread
->apob_rdev
) != CB_SUCCESS
)
152 assert(ARRAY_SIZE(thread
->buffer
) == region_device_sz(&thread
->apob_rdev
));
154 printk(BIOS_DEBUG
, "Starting APOB preload\n");
155 if (thread_run(&thread
->handle
, apob_thread_entry
, thread
))
156 printk(BIOS_ERR
, "Failed to start APOB preload thread\n");
159 static void *get_apob_from_nv_rdev(struct region_device
*read_rdev
)
161 struct apob_base_header apob_header
;
163 if (rdev_readat(read_rdev
, &apob_header
, 0, sizeof(apob_header
)) < 0) {
164 printk(BIOS_ERR
, "Couldn't read APOB header!\n");
168 if (apob_header_valid(&apob_header
, "ROM") == false) {
169 printk(BIOS_ERR
, "No APOB NV data!\n");
173 assert(CONFIG(BOOT_DEVICE_MEMORY_MAPPED
));
174 return rdev_mmap_full(read_rdev
);
177 static uint64_t get_apob_hash_from_nv_rdev(const struct region_device
*read_rdev
)
181 if (rdev_readat(read_rdev
, &hash
, MRC_HASH_OFFSET
, MRC_HASH_SIZE
) < 0) {
182 printk(BIOS_ERR
, "Couldn't read APOB hash!\n");
183 return MRC_HASH_UNINITIALIZED
;
189 static void update_apob_nv_hash(uint64_t hash
, struct region_device
*write_rdev
)
191 if (rdev_writeat(write_rdev
, &hash
, MRC_HASH_OFFSET
, MRC_HASH_SIZE
) < 0) {
192 printk(BIOS_ERR
, "APOB hash flash region update failed\n");
196 /* Save APOB buffer to flash */
197 static void soc_update_apob_cache(void *unused
)
199 struct apob_base_header
*apob_rom
= NULL
;
200 struct region_device read_rdev
, write_rdev
;
201 bool update_needed
= false;
202 const struct apob_base_header
*apob_src_ram
;
203 uint64_t ram_hash
, nv_hash
;
205 /* Nothing to update in case of S3 resume. */
206 if (acpi_is_wakeup_s3())
209 apob_src_ram
= get_apob_dram_address();
210 if (apob_src_ram
== NULL
)
213 if (get_nv_rdev(&read_rdev
) != CB_SUCCESS
)
216 timestamp_add_now(TS_AMD_APOB_READ_START
);
218 if (CONFIG(SOC_AMD_COMMON_BLOCK_APOB_HASH
)) {
219 nv_hash
= get_apob_hash_from_nv_rdev(&read_rdev
);
220 ram_hash
= xxh64(apob_src_ram
, apob_src_ram
->size
, 0);
222 if (nv_hash
!= ram_hash
) {
223 printk(BIOS_INFO
, "APOB RAM hash differs from flash\n");
224 update_needed
= true;
226 printk(BIOS_DEBUG
, "APOB hash matches flash\n");
227 timestamp_add_now(TS_AMD_APOB_END
);
232 if (CONFIG(COOP_MULTITASKING
) && thread_join(&global_apob_thread
.handle
) == CB_SUCCESS
)
233 apob_rom
= (struct apob_base_header
*)global_apob_thread
.buffer
;
234 else if (!update_needed
)
235 apob_rom
= get_apob_from_nv_rdev(&read_rdev
);
237 if (apob_rom
== NULL
) {
238 update_needed
= true;
239 } else if (memcmp(apob_src_ram
, apob_rom
, apob_src_ram
->size
)) {
240 printk(BIOS_INFO
, "APOB RAM copy differs from flash\n");
241 update_needed
= true;
243 printk(BIOS_DEBUG
, "APOB valid copy is already in flash\n");
245 if (!update_needed
) {
246 timestamp_add_now(TS_AMD_APOB_END
);
250 printk(BIOS_SPEW
, "Copy APOB from RAM %p/%#x to flash %#zx/%#zx\n",
251 apob_src_ram
, apob_src_ram
->size
,
252 region_device_offset(&read_rdev
), region_device_sz(&read_rdev
));
254 if (get_nv_rdev_rw(&write_rdev
) != CB_SUCCESS
)
257 timestamp_add_now(TS_AMD_APOB_ERASE_START
);
259 /* write data to flash region */
260 if (rdev_eraseat(&write_rdev
, 0, DEFAULT_MRC_CACHE_SIZE
) < 0) {
261 printk(BIOS_ERR
, "APOB flash region erase failed\n");
265 timestamp_add_now(TS_AMD_APOB_WRITE_START
);
267 if (rdev_writeat(&write_rdev
, apob_src_ram
, 0, apob_src_ram
->size
) < 0) {
268 printk(BIOS_ERR
, "APOB flash region update failed\n");
272 if (CONFIG(SOC_AMD_COMMON_BLOCK_APOB_HASH
))
273 update_apob_nv_hash(ram_hash
, &write_rdev
);
275 timestamp_add_now(TS_AMD_APOB_END
);
277 printk(BIOS_INFO
, "Updated APOB in flash\n");
280 static void *get_apob_nv_address(void)
282 struct region_device rdev
;
284 if (get_nv_rdev(&rdev
) != CB_SUCCESS
)
287 return get_apob_from_nv_rdev(&rdev
);
290 void *soc_fill_apob_cache(void)
292 /* If this is non-S3 boot, then use the APOB data placed by PSP in DRAM. */
293 if (!acpi_is_wakeup_s3())
294 return get_apob_dram_address();
297 * In case of S3 resume, PSP does not copy APOB data to DRAM. Thus, coreboot needs to
298 * provide the APOB NV data from RW_MRC_CACHE on SPI flash so that FSP can use it
299 * without having to traverse the BIOS directory table.
301 return get_apob_nv_address();
305 * BS_POST_DEVICE was chosen because this gives start_apob_cache_read plenty of time to read
308 BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE
, BS_ON_EXIT
, soc_update_apob_cache
, NULL
);