xref: /aosp_15_r20/external/coreboot/src/soc/amd/common/block/apob/apob_cache.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <acpi/acpi.h>
4 #include <amdblocks/apob_cache.h>
5 #include <assert.h>
6 #include <boot_device.h>
7 #include <bootstate.h>
8 #include <commonlib/helpers.h>
9 #include <commonlib/region.h>
10 #include <console/console.h>
11 #include <fmap.h>
12 #include <fmap_config.h>
13 #include <security/vboot/vboot_common.h>
14 #include <spi_flash.h>
15 #include <string.h>
16 #include <thread.h>
17 #include <timestamp.h>
18 #include <types.h>
19 #include <xxhash.h>
20 
21 #define DEFAULT_MRC_CACHE	"RW_MRC_CACHE"
22 #define DEFAULT_MRC_CACHE_SIZE	FMAP_SECTION_RW_MRC_CACHE_SIZE
23 
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
27 #else
28 #define RECOVERY_MRC_CACHE	DEFAULT_MRC_CACHE
29 #define RECOVERY_MRC_CACHE_SIZE	DEFAULT_MRC_CACHE_SIZE
30 #endif
31 
32 #if CONFIG(SOC_AMD_COMMON_BLOCK_APOB_HASH)
33 #define MRC_HASH_SIZE		((uint32_t)sizeof(uint64_t))
34 #else
35 #define MRC_HASH_SIZE		0
36 #endif
37 #define MRC_HASH_OFFSET		(DEFAULT_MRC_CACHE_SIZE-MRC_HASH_SIZE)
38 #define MRC_HASH_UNINITIALIZED	0xffffffffull
39 
40 #if !CONFIG_PSP_APOB_DRAM_ADDRESS
41 #error Incorrect APOB configuration setting(s)
42 #endif
43 
44 _Static_assert(CONFIG_PSP_APOB_DRAM_SIZE == DEFAULT_MRC_CACHE_SIZE,
45 	"APOB DRAM reserved space != to MRC CACHE size - check your config");
46 
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");
49 
50 #define APOB_SIGNATURE 0x424F5041	/* 'APOB' */
51 
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 */
58 };
59 
apob_header_valid(const struct apob_base_header * apob_header_ptr,const char * where)60 static bool apob_header_valid(const struct apob_base_header *apob_header_ptr, const char *where)
61 {
62 	uint32_t size_plus_hash = apob_header_ptr->size + MRC_HASH_SIZE;
63 
64 	if (apob_header_ptr->signature != APOB_SIGNATURE) {
65 		printk(BIOS_WARNING, "Invalid %s APOB signature %x\n",
66 			where, apob_header_ptr->signature);
67 		return false;
68 	}
69 
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);
73 		return false;
74 	}
75 
76 	return true;
77 }
78 
get_apob_dram_address(void)79 static void *get_apob_dram_address(void)
80 {
81 	/*
82 	 * TODO: Find the APOB destination by parsing the PSP's tables
83 	 * (once vboot is implemented).
84 	 */
85 	void *apob_src_ram = (void *)(uintptr_t)CONFIG_PSP_APOB_DRAM_ADDRESS;
86 
87 	if (apob_header_valid(apob_src_ram, "RAM") == false)
88 		return NULL;
89 
90 	return apob_src_ram;
91 }
92 
get_nv_rdev(struct region_device * r)93 static enum cb_err get_nv_rdev(struct region_device *r)
94 {
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");
98 		return CB_ERR;
99 	}
100 
101 	return CB_SUCCESS;
102 }
103 
get_nv_rdev_rw(struct region_device * r)104 static enum cb_err get_nv_rdev_rw(struct region_device *r)
105 {
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");
109 		return CB_ERR;
110 	}
111 
112 	return CB_SUCCESS;
113 }
114 
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;
120 
apob_thread_entry(void * arg)121 static enum cb_err apob_thread_entry(void *arg)
122 {
123 	ssize_t size;
124 	struct apob_thread_context *thread = arg;
125 
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));
129 
130 	printk(BIOS_DEBUG, "APOB thread done\n");
131 
132 	if (size == region_device_sz(&thread->apob_rdev))
133 		return CB_SUCCESS;
134 
135 	return CB_ERR;
136 }
137 
start_apob_cache_read(void)138 void start_apob_cache_read(void)
139 {
140 	struct apob_thread_context *thread = &global_apob_thread;
141 
142 	if (!CONFIG(COOP_MULTITASKING) || CONFIG(SOC_AMD_COMMON_BLOCK_APOB_HASH))
143 		return;
144 
145 	/* We don't perform any comparison on S3 resume */
146 	if (acpi_is_wakeup_s3())
147 		return;
148 
149 	if (get_nv_rdev(&thread->apob_rdev) != CB_SUCCESS)
150 		return;
151 
152 	assert(ARRAY_SIZE(thread->buffer) == region_device_sz(&thread->apob_rdev));
153 
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");
157 }
158 
get_apob_from_nv_rdev(struct region_device * read_rdev)159 static void *get_apob_from_nv_rdev(struct region_device *read_rdev)
160 {
161 	struct apob_base_header apob_header;
162 
163 	if (rdev_readat(read_rdev, &apob_header, 0, sizeof(apob_header)) < 0) {
164 		printk(BIOS_ERR, "Couldn't read APOB header!\n");
165 		return NULL;
166 	}
167 
168 	if (apob_header_valid(&apob_header, "ROM") == false) {
169 		printk(BIOS_ERR, "No APOB NV data!\n");
170 		return NULL;
171 	}
172 
173 	assert(CONFIG(BOOT_DEVICE_MEMORY_MAPPED));
174 	return rdev_mmap_full(read_rdev);
175 }
176 
get_apob_hash_from_nv_rdev(const struct region_device * read_rdev)177 static uint64_t get_apob_hash_from_nv_rdev(const struct region_device *read_rdev)
178 {
179 	uint64_t hash;
180 
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;
184 	}
185 
186 	return hash;
187 }
188 
update_apob_nv_hash(uint64_t hash,struct region_device * write_rdev)189 static void update_apob_nv_hash(uint64_t hash, struct region_device *write_rdev)
190 {
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");
193 	}
194 }
195 
196 /* Save APOB buffer to flash */
soc_update_apob_cache(void * unused)197 static void soc_update_apob_cache(void *unused)
198 {
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;
204 
205 	/* Nothing to update in case of S3 resume. */
206 	if (acpi_is_wakeup_s3())
207 		return;
208 
209 	apob_src_ram = get_apob_dram_address();
210 	if (apob_src_ram == NULL)
211 		return;
212 
213 	if (get_nv_rdev(&read_rdev) != CB_SUCCESS)
214 		return;
215 
216 	timestamp_add_now(TS_AMD_APOB_READ_START);
217 
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);
221 
222 		if (nv_hash != ram_hash) {
223 			printk(BIOS_INFO, "APOB RAM hash differs from flash\n");
224 			update_needed = true;
225 		} else {
226 			printk(BIOS_DEBUG, "APOB hash matches flash\n");
227 			timestamp_add_now(TS_AMD_APOB_END);
228 			return;
229 		}
230 	}
231 
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);
236 
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;
242 	} else
243 		printk(BIOS_DEBUG, "APOB valid copy is already in flash\n");
244 
245 	if (!update_needed) {
246 		timestamp_add_now(TS_AMD_APOB_END);
247 		return;
248 	}
249 
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));
253 
254 	if (get_nv_rdev_rw(&write_rdev) != CB_SUCCESS)
255 		return;
256 
257 	timestamp_add_now(TS_AMD_APOB_ERASE_START);
258 
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");
262 		return;
263 	}
264 
265 	timestamp_add_now(TS_AMD_APOB_WRITE_START);
266 
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");
269 		return;
270 	}
271 
272 	if (CONFIG(SOC_AMD_COMMON_BLOCK_APOB_HASH))
273 		update_apob_nv_hash(ram_hash, &write_rdev);
274 
275 	timestamp_add_now(TS_AMD_APOB_END);
276 
277 	printk(BIOS_INFO, "Updated APOB in flash\n");
278 }
279 
get_apob_nv_address(void)280 static void *get_apob_nv_address(void)
281 {
282 	struct region_device rdev;
283 
284 	if (get_nv_rdev(&rdev) != CB_SUCCESS)
285 		return NULL;
286 
287 	return get_apob_from_nv_rdev(&rdev);
288 }
289 
soc_fill_apob_cache(void)290 void *soc_fill_apob_cache(void)
291 {
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();
295 
296 	/*
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.
300 	 */
301 	return get_apob_nv_address();
302 }
303 
304 /*
305  * BS_POST_DEVICE was chosen because this gives start_apob_cache_read plenty of time to read
306  * the APOB from SPI.
307  */
308 BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, soc_update_apob_cache, NULL);
309