blob: e1e6a57a22b0266c1d3e350a57228b92dbd7ae2f [file] [log] [blame]
Angel Pons118a9c72020-04-02 23:48:34 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Aaron Durbin0424c952015-03-28 23:56:22 -05002
3#include <boot_device.h>
Julius Wernercefe89e2019-11-06 19:29:44 -08004#include <cbmem.h>
Aaron Durbin0424c952015-03-28 23:56:22 -05005#include <console/console.h>
6#include <fmap.h>
Aaron Durbin0424c952015-03-28 23:56:22 -05007#include <stddef.h>
8#include <string.h>
Julius Wernercefe89e2019-11-06 19:29:44 -08009#include <symbols.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050010
Aaron Durbinbf1e4812016-05-10 15:12:08 -050011#include "fmap_config.h"
12
Aaron Durbin0424c952015-03-28 23:56:22 -050013/*
14 * See http://code.google.com/p/flashmap/ for more information on FMAP.
15 */
16
Arthur Heymansdba22d22019-11-20 19:57:49 +010017static int fmap_print_once;
18static struct mem_region_device fmap_cache;
Duncan Lauriebc2c0a32016-02-09 09:17:56 -080019
Julius Wernercefe89e2019-11-06 19:29:44 -080020#define print_once(...) do { \
Arthur Heymansdba22d22019-11-20 19:57:49 +010021 if (!fmap_print_once) \
Julius Wernercefe89e2019-11-06 19:29:44 -080022 printk(__VA_ARGS__); \
23 } while (0)
24
Furquan Shaikhb33a2b02019-09-26 23:51:46 -070025uint64_t get_fmap_flash_offset(void)
26{
27 return FMAP_OFFSET;
28}
29
Julius Wernercefe89e2019-11-06 19:29:44 -080030static int check_signature(const struct fmap *fmap)
31{
32 return memcmp(fmap->signature, FMAP_SIGNATURE, sizeof(fmap->signature));
33}
34
35static void report(const struct fmap *fmap)
36{
37 print_once(BIOS_DEBUG, "FMAP: Found \"%s\" version %d.%d at %#x.\n",
38 fmap->name, fmap->ver_major, fmap->ver_minor, FMAP_OFFSET);
39 print_once(BIOS_DEBUG, "FMAP: base = %#llx size = %#x #areas = %d\n",
40 (long long)fmap->base, fmap->size, fmap->nareas);
Arthur Heymansdba22d22019-11-20 19:57:49 +010041 fmap_print_once = 1;
Julius Wernercefe89e2019-11-06 19:29:44 -080042}
43
44static void setup_preram_cache(struct mem_region_device *cache_mrdev)
45{
Julius Werner7fc92862019-11-18 13:01:06 -080046 if (CONFIG(NO_FMAP_CACHE))
47 return;
48
Julius Wernercefe89e2019-11-06 19:29:44 -080049 if (!ENV_ROMSTAGE_OR_BEFORE) {
50 /* We get here if ramstage makes an FMAP access before calling
51 cbmem_initialize(). We should avoid letting it come to that,
52 so print a warning. */
53 print_once(BIOS_WARNING,
54 "WARNING: Post-RAM FMAP access too early for cache!\n");
55 return;
56 }
57
Julius Wernercefe89e2019-11-06 19:29:44 -080058 struct fmap *fmap = (struct fmap *)_fmap_cache;
Martin Roth1594e8f2020-07-15 13:57:54 -060059 if (!(ENV_INITIAL_STAGE)) {
60 /* NOTE: This assumes that the first stage will make
Julius Wernercefe89e2019-11-06 19:29:44 -080061 at least one FMAP access (usually from finding CBFS). */
62 if (!check_signature(fmap))
63 goto register_cache;
64
65 printk(BIOS_ERR, "ERROR: FMAP cache corrupted?!\n");
66 }
67
68 /* In case we fail below, make sure the cache is invalid. */
69 memset(fmap->signature, 0, sizeof(fmap->signature));
70
71 boot_device_init();
72 const struct region_device *boot_rdev = boot_device_ro();
73 if (!boot_rdev)
74 return;
75
76 /* memlayout statically guarantees that the FMAP_CACHE is big enough. */
77 if (rdev_readat(boot_rdev, fmap, FMAP_OFFSET, FMAP_SIZE) != FMAP_SIZE)
78 return;
79 if (check_signature(fmap))
80 return;
81 report(fmap);
82
83register_cache:
84 mem_region_device_ro_init(cache_mrdev, fmap, FMAP_SIZE);
85}
86
Furquan Shaikhb33a2b02019-09-26 23:51:46 -070087static int find_fmap_directory(struct region_device *fmrd)
Aaron Durbin0424c952015-03-28 23:56:22 -050088{
89 const struct region_device *boot;
90 struct fmap *fmap;
Aaron Durbinbf1e4812016-05-10 15:12:08 -050091 size_t offset = FMAP_OFFSET;
Aaron Durbin0424c952015-03-28 23:56:22 -050092
Julius Wernercefe89e2019-11-06 19:29:44 -080093 /* Try FMAP cache first */
Arthur Heymansdba22d22019-11-20 19:57:49 +010094 if (!region_device_sz(&fmap_cache.rdev))
95 setup_preram_cache(&fmap_cache);
96 if (region_device_sz(&fmap_cache.rdev))
97 return rdev_chain_full(fmrd, &fmap_cache.rdev);
Patrick Rudolph6d787c22019-09-12 13:21:37 +020098
Aaron Durbin0424c952015-03-28 23:56:22 -050099 boot_device_init();
100 boot = boot_device_ro();
101
102 if (boot == NULL)
103 return -1;
104
Julius Wernercefe89e2019-11-06 19:29:44 -0800105 fmap = rdev_mmap(boot, offset, sizeof(struct fmap));
Aaron Durbin0424c952015-03-28 23:56:22 -0500106
107 if (fmap == NULL)
108 return -1;
109
Julius Wernercefe89e2019-11-06 19:29:44 -0800110 if (check_signature(fmap)) {
Aaron Durbin0424c952015-03-28 23:56:22 -0500111 printk(BIOS_DEBUG, "No FMAP found at %zx offset.\n", offset);
112 rdev_munmap(boot, fmap);
113 return -1;
114 }
115
Julius Wernercefe89e2019-11-06 19:29:44 -0800116 report(fmap);
Aaron Durbin0424c952015-03-28 23:56:22 -0500117
118 rdev_munmap(boot, fmap);
119
Julius Wernercefe89e2019-11-06 19:29:44 -0800120 return rdev_chain(fmrd, boot, offset, FMAP_SIZE);
Aaron Durbin0424c952015-03-28 23:56:22 -0500121}
122
123int fmap_locate_area_as_rdev(const char *name, struct region_device *area)
124{
125 struct region ar;
126
127 if (fmap_locate_area(name, &ar))
128 return -1;
129
130 return boot_device_ro_subregion(&ar, area);
131}
132
Aaron Durbinbccaab82016-08-12 12:42:04 -0500133int fmap_locate_area_as_rdev_rw(const char *name, struct region_device *area)
134{
135 struct region ar;
136
137 if (fmap_locate_area(name, &ar))
138 return -1;
139
140 return boot_device_rw_subregion(&ar, area);
141}
142
Aaron Durbin0424c952015-03-28 23:56:22 -0500143int fmap_locate_area(const char *name, struct region *ar)
144{
145 struct region_device fmrd;
146 size_t offset;
147
148 if (find_fmap_directory(&fmrd))
149 return -1;
150
151 /* Start reading the areas just after fmap header. */
152 offset = sizeof(struct fmap);
153
154 while (1) {
155 struct fmap_area *area;
156
157 area = rdev_mmap(&fmrd, offset, sizeof(*area));
158
159 if (area == NULL)
160 return -1;
161
162 if (strcmp((const char *)area->name, name)) {
163 rdev_munmap(&fmrd, area);
164 offset += sizeof(struct fmap_area);
165 continue;
166 }
167
Duncan Lauriebc2c0a32016-02-09 09:17:56 -0800168 printk(BIOS_DEBUG, "FMAP: area %s found @ %x (%d bytes)\n",
169 name, area->offset, area->size);
Aaron Durbin0424c952015-03-28 23:56:22 -0500170
171 ar->offset = area->offset;
172 ar->size = area->size;
173
174 rdev_munmap(&fmrd, area);
175
176 return 0;
177 }
178
179 printk(BIOS_DEBUG, "FMAP: area %s not found\n", name);
180
181 return -1;
182}
Patrick Georgi99526902015-07-09 11:27:44 +0200183
184int fmap_find_region_name(const struct region * const ar,
185 char name[FMAP_STRLEN])
186{
187 struct region_device fmrd;
188 size_t offset;
189
190 if (find_fmap_directory(&fmrd))
191 return -1;
192
193 /* Start reading the areas just after fmap header. */
194 offset = sizeof(struct fmap);
195
196 while (1) {
197 struct fmap_area *area;
198
199 area = rdev_mmap(&fmrd, offset, sizeof(*area));
200
201 if (area == NULL)
202 return -1;
203
204 if ((ar->offset != area->offset) ||
205 (ar->size != area->size)) {
206 rdev_munmap(&fmrd, area);
207 offset += sizeof(struct fmap_area);
208 continue;
209 }
210
211 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) found, named %s\n",
212 ar->offset, ar->size, area->name);
213
214 memcpy(name, area->name, FMAP_STRLEN);
215
216 rdev_munmap(&fmrd, area);
217
218 return 0;
219 }
220
221 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) not found\n",
222 ar->offset, ar->size);
223
224 return -1;
225}
T Michael Turney19fcc892019-03-20 14:37:34 -0700226
227ssize_t fmap_read_area(const char *name, void *buffer, size_t size)
228{
229 struct region_device rdev;
230 if (fmap_locate_area_as_rdev(name, &rdev))
231 return -1;
232 return rdev_readat(&rdev, buffer, 0,
233 MIN(size, region_device_sz(&rdev)));
234}
235
236ssize_t fmap_overwrite_area(const char *name, const void *buffer, size_t size)
237{
238 struct region_device rdev;
239
240 if (fmap_locate_area_as_rdev_rw(name, &rdev))
241 return -1;
242 if (size > region_device_sz(&rdev))
243 return -1;
244 if (rdev_eraseat(&rdev, 0, region_device_sz(&rdev)) < 0)
245 return -1;
246 return rdev_writeat(&rdev, buffer, 0, size);
247}
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200248
Julius Wernercefe89e2019-11-06 19:29:44 -0800249static void fmap_register_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200250{
251 const struct cbmem_entry *e;
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200252
253 /* Find the FMAP cache installed by previous stage */
254 e = cbmem_entry_find(CBMEM_ID_FMAP);
255 /* Don't set fmap_cache so that find_fmap_directory will use regular path */
256 if (!e)
257 return;
258
Arthur Heymansdba22d22019-11-20 19:57:49 +0100259 mem_region_device_ro_init(&fmap_cache, cbmem_entry_start(e), cbmem_entry_size(e));
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200260}
261
262/*
263 * The main reason to copy the FMAP into CBMEM is to make it available to the
264 * OS on every architecture. As side effect use the CBMEM copy as cache.
265 */
Julius Wernercefe89e2019-11-06 19:29:44 -0800266static void fmap_setup_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200267{
268 struct region_device fmrd;
269
270 if (find_fmap_directory(&fmrd))
271 return;
272
273 /* Reloads the FMAP even on ACPI S3 resume */
274 const size_t s = region_device_sz(&fmrd);
275 struct fmap *fmap = cbmem_add(CBMEM_ID_FMAP, s);
276 if (!fmap) {
277 printk(BIOS_ERR, "ERROR: Failed to allocate CBMEM\n");
278 return;
279 }
280
281 const ssize_t ret = rdev_readat(&fmrd, fmap, 0, s);
282 if (ret != s) {
283 printk(BIOS_ERR, "ERROR: Failed to read FMAP into CBMEM\n");
284 cbmem_entry_remove(cbmem_entry_find(CBMEM_ID_FMAP));
285 return;
286 }
287
288 /* Finally advertise the cache for the current stage */
Julius Wernercefe89e2019-11-06 19:29:44 -0800289 fmap_register_cbmem_cache(unused);
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200290}
291
Julius Wernercefe89e2019-11-06 19:29:44 -0800292ROMSTAGE_CBMEM_INIT_HOOK(fmap_setup_cbmem_cache)
293RAMSTAGE_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)
294POSTCAR_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)