coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
apob_cache.c
Go to the documentation of this file.
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 <spi_flash.h>
14 #include <stdint.h>
15 #include <string.h>
16 #include <thread.h>
17 #include <timestamp.h>
18 
19 #define DEFAULT_MRC_CACHE "RW_MRC_CACHE"
20 #define DEFAULT_MRC_CACHE_SIZE FMAP_SECTION_RW_MRC_CACHE_SIZE
21 
22 #if !CONFIG_PSP_APOB_DRAM_ADDRESS
23 #error Incorrect APOB configuration setting(s)
24 #endif
25 
26 #define APOB_SIGNATURE 0x424F5041 /* 'APOB' */
27 
28 /* APOB_BASE_HEADER from AGESA */
30  uint32_t signature; /* APOB signature */
31  uint32_t version; /* Version */
32  uint32_t size; /* APOB Size */
33  uint32_t offset_of_first_entry; /* APOB Header Size */
34 };
35 
36 static bool apob_header_valid(const struct apob_base_header *apob_header_ptr, const char *where)
37 {
38  if (apob_header_ptr->signature != APOB_SIGNATURE) {
39  printk(BIOS_WARNING, "Invalid %s APOB signature %x\n",
40  where, apob_header_ptr->signature);
41  return false;
42  }
43 
44  if (apob_header_ptr->size == 0 || apob_header_ptr->size > DEFAULT_MRC_CACHE_SIZE) {
45  printk(BIOS_WARNING, "%s APOB data is too large %x > %x\n",
46  where, apob_header_ptr->size, DEFAULT_MRC_CACHE_SIZE);
47  return false;
48  }
49 
50  return true;
51 }
52 
53 static void *get_apob_dram_address(void)
54 {
55  /*
56  * TODO: Find the APOB destination by parsing the PSP's tables
57  * (once vboot is implemented).
58  */
59  void *apob_src_ram = (void *)(uintptr_t)CONFIG_PSP_APOB_DRAM_ADDRESS;
60 
61  if (apob_header_valid(apob_src_ram, "RAM") == false)
62  return NULL;
63 
64  return apob_src_ram;
65 }
66 
67 static int get_nv_rdev(struct region_device *r)
68 {
70  printk(BIOS_ERR, "No APOB NV region is found in flash\n");
71  return -1;
72  }
73 
74  return 0;
75 }
76 
77 static struct apob_thread_context {
78  uint8_t buffer[DEFAULT_MRC_CACHE_SIZE] __attribute__((aligned(64)));
79  struct thread_handle handle;
80  struct region_device apob_rdev;
82 
83 static enum cb_err apob_thread_entry(void *arg)
84 {
85  ssize_t size;
86  struct apob_thread_context *thread = arg;
87 
88  printk(BIOS_DEBUG, "APOB thread running\n");
89  size = rdev_readat(&thread->apob_rdev, thread->buffer, 0,
90  region_device_sz(&thread->apob_rdev));
91 
92  printk(BIOS_DEBUG, "APOB thread done\n");
93 
94  if (size == region_device_sz(&thread->apob_rdev))
95  return CB_SUCCESS;
96 
97  return CB_ERR;
98 }
99 
101 {
102  struct apob_thread_context *thread = &global_apob_thread;
103 
104  if (!CONFIG(COOP_MULTITASKING))
105  return;
106 
107  /* We don't perform any comparison on S3 resume */
108  if (acpi_is_wakeup_s3())
109  return;
110 
111  if (get_nv_rdev(&thread->apob_rdev) != 0)
112  return;
113 
114  assert(ARRAY_SIZE(thread->buffer) == region_device_sz(&thread->apob_rdev));
115 
116  printk(BIOS_DEBUG, "Starting APOB preload\n");
117  if (thread_run(&thread->handle, apob_thread_entry, thread))
118  printk(BIOS_ERR, "Failed to start APOB preload thread\n");
119 }
120 
121 static void *get_apob_from_nv_rdev(struct region_device *read_rdev)
122 {
123  struct apob_base_header apob_header;
124 
125  if (rdev_readat(read_rdev, &apob_header, 0, sizeof(apob_header)) < 0) {
126  printk(BIOS_ERR, "Couldn't read APOB header!\n");
127  return NULL;
128  }
129 
130  if (apob_header_valid(&apob_header, "ROM") == false) {
131  printk(BIOS_ERR, "No APOB NV data!\n");
132  return NULL;
133  }
134 
135  assert(CONFIG(BOOT_DEVICE_MEMORY_MAPPED));
136  return rdev_mmap_full(read_rdev);
137 }
138 
139 /* Save APOB buffer to flash */
140 static void soc_update_apob_cache(void *unused)
141 {
142  struct apob_base_header *apob_rom;
143  struct region_device read_rdev, write_rdev;
144  bool update_needed = false;
145  const struct apob_base_header *apob_src_ram;
146 
147  /* Nothing to update in case of S3 resume. */
148  if (acpi_is_wakeup_s3())
149  return;
150 
151  apob_src_ram = get_apob_dram_address();
152  if (apob_src_ram == NULL)
153  return;
154 
155  if (get_nv_rdev(&read_rdev) != 0)
156  return;
157 
159 
160  if (CONFIG(COOP_MULTITASKING) && thread_join(&global_apob_thread.handle) == CB_SUCCESS)
161  apob_rom = (struct apob_base_header *)global_apob_thread.buffer;
162  else
163  apob_rom = get_apob_from_nv_rdev(&read_rdev);
164 
165  if (apob_rom == NULL) {
166  update_needed = true;
167  } else if (memcmp(apob_src_ram, apob_rom, apob_src_ram->size)) {
168  printk(BIOS_INFO, "APOB RAM copy differs from flash\n");
169  update_needed = true;
170  } else
171  printk(BIOS_DEBUG, "APOB valid copy is already in flash\n");
172 
173  if (!update_needed) {
175  return;
176  }
177 
178  printk(BIOS_SPEW, "Copy APOB from RAM %p/%#x to flash %#zx/%#zx\n",
179  apob_src_ram, apob_src_ram->size,
180  region_device_offset(&read_rdev), region_device_sz(&read_rdev));
181 
182  if (fmap_locate_area_as_rdev_rw(DEFAULT_MRC_CACHE, &write_rdev) < 0) {
183  printk(BIOS_ERR, "No RW APOB NV region is found in flash\n");
184  return;
185  }
186 
188 
189  /* write data to flash region */
190  if (rdev_eraseat(&write_rdev, 0, DEFAULT_MRC_CACHE_SIZE) < 0) {
191  printk(BIOS_ERR, "APOB flash region erase failed\n");
192  return;
193  }
194 
196 
197  if (rdev_writeat(&write_rdev, apob_src_ram, 0, apob_src_ram->size) < 0) {
198  printk(BIOS_ERR, "APOB flash region update failed\n");
199  return;
200  }
201 
203 
204  printk(BIOS_INFO, "Updated APOB in flash\n");
205 }
206 
207 static void *get_apob_nv_address(void)
208 {
209  struct region_device rdev;
210 
211  if (get_nv_rdev(&rdev) != 0)
212  return NULL;
213 
214  return get_apob_from_nv_rdev(&rdev);
215 }
216 
218 {
219  /* If this is non-S3 boot, then use the APOB data placed by PSP in DRAM. */
220  if (!acpi_is_wakeup_s3())
221  return get_apob_dram_address();
222 
223  /*
224  * In case of S3 resume, PSP does not copy APOB data to DRAM. Thus, coreboot needs to
225  * provide the APOB NV data from RW_MRC_CACHE on SPI flash so that FSP can use it
226  * without having to traverse the BIOS directory table.
227  */
228  return get_apob_nv_address();
229 }
230 
231 /*
232  * BS_POST_DEVICE was chosen because this gives start_apob_cache_read plenty of time to read
233  * the APOB from SPI.
234  */
static struct apob_thread_context global_apob_thread
static enum cb_err apob_thread_entry(void *arg)
Definition: apob_cache.c:83
static void * get_apob_from_nv_rdev(struct region_device *read_rdev)
Definition: apob_cache.c:121
static bool apob_header_valid(const struct apob_base_header *apob_header_ptr, const char *where)
Definition: apob_cache.c:36
#define DEFAULT_MRC_CACHE_SIZE
Definition: apob_cache.c:20
BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, soc_update_apob_cache, NULL)
#define APOB_SIGNATURE
Definition: apob_cache.c:26
static int get_nv_rdev(struct region_device *r)
Definition: apob_cache.c:67
void * soc_fill_apob_cache(void)
Definition: apob_cache.c:217
static void soc_update_apob_cache(void *unused)
Definition: apob_cache.c:140
void start_apob_cache_read(void)
Definition: apob_cache.c:100
static void * get_apob_nv_address(void)
Definition: apob_cache.c:207
#define DEFAULT_MRC_CACHE
Definition: apob_cache.c:19
static void * get_apob_dram_address(void)
Definition: apob_cache.c:53
static int acpi_is_wakeup_s3(void)
Definition: acpi.h:9
#define assert(statement)
Definition: assert.h:74
@ BS_POST_DEVICE
Definition: bootstate.h:84
@ BS_ON_EXIT
Definition: bootstate.h:96
#define ARRAY_SIZE(a)
Definition: helpers.h:12
cb_err
coreboot error codes
Definition: cb_err.h:15
@ CB_ERR
Generic error code.
Definition: cb_err.h:17
@ CB_SUCCESS
Call completed successfully.
Definition: cb_err.h:16
#define printk(level,...)
Definition: stdlib.h:16
@ CONFIG
Definition: dsi_common.h:201
static struct region_device rdev
Definition: flashconsole.c:14
int fmap_locate_area_as_rdev(const char *name, struct region_device *area)
Definition: fmap.c:144
int fmap_locate_area_as_rdev_rw(const char *name, struct region_device *area)
Definition: fmap.c:154
struct bootblock_arg arg
Definition: decompressor.c:22
void timestamp_add_now(enum timestamp_id id)
Definition: timestamp.c:141
#define BIOS_INFO
BIOS_INFO - Expected events.
Definition: loglevel.h:113
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_SPEW
BIOS_SPEW - Excessively verbose output.
Definition: loglevel.h:142
#define BIOS_WARNING
BIOS_WARNING - Bad configuration.
Definition: loglevel.h:86
ssize_t rdev_eraseat(const struct region_device *rd, size_t offset, size_t size)
Definition: region.c:114
static size_t region_device_sz(const struct region_device *rdev)
Definition: region.h:132
ssize_t rdev_writeat(const struct region_device *rd, const void *b, size_t offset, size_t size)
Definition: region.c:94
static void * rdev_mmap_full(const struct region_device *rd)
Definition: region.h:148
ssize_t rdev_readat(const struct region_device *rd, void *b, size_t offset, size_t size)
Definition: region.c:77
static size_t region_device_offset(const struct region_device *rdev)
Definition: region.h:137
#define NULL
Definition: stddef.h:19
__SIZE_TYPE__ ssize_t
Definition: stddef.h:13
unsigned int uint32_t
Definition: stdint.h:14
unsigned long uintptr_t
Definition: stdint.h:21
unsigned char uint8_t
Definition: stdint.h:8
int memcmp(const void *s1, const void *s2, size_t n)
Definition: memcmp.c:3
uint32_t version
Definition: apob_cache.c:31
uint32_t size
Definition: apob_cache.c:32
uint32_t signature
Definition: apob_cache.c:30
uint32_t offset_of_first_entry
Definition: apob_cache.c:33
uint8_t buffer[DEFAULT_MRC_CACHE_SIZE]
Definition: apob_cache.c:78
struct thread_handle handle
Definition: apob_cache.c:79
struct region_device apob_rdev
Definition: apob_cache.c:80
int thread_run(struct thread_handle *handle, enum cb_err(*func)(void *), void *arg)
Definition: thread.c:272
enum cb_err thread_join(struct thread_handle *handle)
Definition: thread.c:384
@ TS_AMD_APOB_ERASE_START
@ TS_AMD_APOB_END
@ TS_AMD_APOB_READ_START
@ TS_AMD_APOB_WRITE_START