blob: ecd23f6d3268a468b0537c1e389740c473bac29b [file] [log] [blame]
Angel Pons118a9c72020-04-02 23:48:34 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Aaron Durbin0424c952015-03-28 23:56:22 -05003
4#include <boot_device.h>
Julius Wernercefe89e2019-11-06 19:29:44 -08005#include <cbmem.h>
Aaron Durbin0424c952015-03-28 23:56:22 -05006#include <console/console.h>
7#include <fmap.h>
Aaron Durbin0424c952015-03-28 23:56:22 -05008#include <stddef.h>
9#include <string.h>
Julius Wernercefe89e2019-11-06 19:29:44 -080010#include <symbols.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050011
Aaron Durbinbf1e4812016-05-10 15:12:08 -050012#include "fmap_config.h"
13
Aaron Durbin0424c952015-03-28 23:56:22 -050014/*
15 * See http://code.google.com/p/flashmap/ for more information on FMAP.
16 */
17
Arthur Heymansdba22d22019-11-20 19:57:49 +010018static int fmap_print_once;
19static struct mem_region_device fmap_cache;
Duncan Lauriebc2c0a32016-02-09 09:17:56 -080020
Julius Wernercefe89e2019-11-06 19:29:44 -080021#define print_once(...) do { \
Arthur Heymansdba22d22019-11-20 19:57:49 +010022 if (!fmap_print_once) \
Julius Wernercefe89e2019-11-06 19:29:44 -080023 printk(__VA_ARGS__); \
24 } while (0)
25
Furquan Shaikhb33a2b02019-09-26 23:51:46 -070026uint64_t get_fmap_flash_offset(void)
27{
28 return FMAP_OFFSET;
29}
30
Julius Wernercefe89e2019-11-06 19:29:44 -080031static int check_signature(const struct fmap *fmap)
32{
33 return memcmp(fmap->signature, FMAP_SIGNATURE, sizeof(fmap->signature));
34}
35
36static void report(const struct fmap *fmap)
37{
38 print_once(BIOS_DEBUG, "FMAP: Found \"%s\" version %d.%d at %#x.\n",
39 fmap->name, fmap->ver_major, fmap->ver_minor, FMAP_OFFSET);
40 print_once(BIOS_DEBUG, "FMAP: base = %#llx size = %#x #areas = %d\n",
41 (long long)fmap->base, fmap->size, fmap->nareas);
Arthur Heymansdba22d22019-11-20 19:57:49 +010042 fmap_print_once = 1;
Julius Wernercefe89e2019-11-06 19:29:44 -080043}
44
45static void setup_preram_cache(struct mem_region_device *cache_mrdev)
46{
Julius Werner7fc92862019-11-18 13:01:06 -080047 if (CONFIG(NO_FMAP_CACHE))
48 return;
49
Julius Wernercefe89e2019-11-06 19:29:44 -080050 if (!ENV_ROMSTAGE_OR_BEFORE) {
51 /* We get here if ramstage makes an FMAP access before calling
52 cbmem_initialize(). We should avoid letting it come to that,
53 so print a warning. */
54 print_once(BIOS_WARNING,
55 "WARNING: Post-RAM FMAP access too early for cache!\n");
56 return;
57 }
58
Julius Wernercefe89e2019-11-06 19:29:44 -080059 struct fmap *fmap = (struct fmap *)_fmap_cache;
60 if (!ENV_BOOTBLOCK) {
61 /* NOTE: This assumes that for all platforms running this code,
62 the bootblock is the first stage and the bootblock will make
63 at least one FMAP access (usually from finding CBFS). */
64 if (!check_signature(fmap))
65 goto register_cache;
66
67 printk(BIOS_ERR, "ERROR: FMAP cache corrupted?!\n");
68 }
69
70 /* In case we fail below, make sure the cache is invalid. */
71 memset(fmap->signature, 0, sizeof(fmap->signature));
72
73 boot_device_init();
74 const struct region_device *boot_rdev = boot_device_ro();
75 if (!boot_rdev)
76 return;
77
78 /* memlayout statically guarantees that the FMAP_CACHE is big enough. */
79 if (rdev_readat(boot_rdev, fmap, FMAP_OFFSET, FMAP_SIZE) != FMAP_SIZE)
80 return;
81 if (check_signature(fmap))
82 return;
83 report(fmap);
84
85register_cache:
86 mem_region_device_ro_init(cache_mrdev, fmap, FMAP_SIZE);
87}
88
Furquan Shaikhb33a2b02019-09-26 23:51:46 -070089static int find_fmap_directory(struct region_device *fmrd)
Aaron Durbin0424c952015-03-28 23:56:22 -050090{
91 const struct region_device *boot;
92 struct fmap *fmap;
Aaron Durbinbf1e4812016-05-10 15:12:08 -050093 size_t offset = FMAP_OFFSET;
Aaron Durbin0424c952015-03-28 23:56:22 -050094
Julius Wernercefe89e2019-11-06 19:29:44 -080095 /* Try FMAP cache first */
Arthur Heymansdba22d22019-11-20 19:57:49 +010096 if (!region_device_sz(&fmap_cache.rdev))
97 setup_preram_cache(&fmap_cache);
98 if (region_device_sz(&fmap_cache.rdev))
99 return rdev_chain_full(fmrd, &fmap_cache.rdev);
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200100
Aaron Durbin0424c952015-03-28 23:56:22 -0500101 boot_device_init();
102 boot = boot_device_ro();
103
104 if (boot == NULL)
105 return -1;
106
Julius Wernercefe89e2019-11-06 19:29:44 -0800107 fmap = rdev_mmap(boot, offset, sizeof(struct fmap));
Aaron Durbin0424c952015-03-28 23:56:22 -0500108
109 if (fmap == NULL)
110 return -1;
111
Julius Wernercefe89e2019-11-06 19:29:44 -0800112 if (check_signature(fmap)) {
Aaron Durbin0424c952015-03-28 23:56:22 -0500113 printk(BIOS_DEBUG, "No FMAP found at %zx offset.\n", offset);
114 rdev_munmap(boot, fmap);
115 return -1;
116 }
117
Julius Wernercefe89e2019-11-06 19:29:44 -0800118 report(fmap);
Aaron Durbin0424c952015-03-28 23:56:22 -0500119
120 rdev_munmap(boot, fmap);
121
Julius Wernercefe89e2019-11-06 19:29:44 -0800122 return rdev_chain(fmrd, boot, offset, FMAP_SIZE);
Aaron Durbin0424c952015-03-28 23:56:22 -0500123}
124
125int fmap_locate_area_as_rdev(const char *name, struct region_device *area)
126{
127 struct region ar;
128
129 if (fmap_locate_area(name, &ar))
130 return -1;
131
132 return boot_device_ro_subregion(&ar, area);
133}
134
Aaron Durbinbccaab82016-08-12 12:42:04 -0500135int fmap_locate_area_as_rdev_rw(const char *name, struct region_device *area)
136{
137 struct region ar;
138
139 if (fmap_locate_area(name, &ar))
140 return -1;
141
142 return boot_device_rw_subregion(&ar, area);
143}
144
Aaron Durbin0424c952015-03-28 23:56:22 -0500145int fmap_locate_area(const char *name, struct region *ar)
146{
147 struct region_device fmrd;
148 size_t offset;
149
150 if (find_fmap_directory(&fmrd))
151 return -1;
152
153 /* Start reading the areas just after fmap header. */
154 offset = sizeof(struct fmap);
155
156 while (1) {
157 struct fmap_area *area;
158
159 area = rdev_mmap(&fmrd, offset, sizeof(*area));
160
161 if (area == NULL)
162 return -1;
163
164 if (strcmp((const char *)area->name, name)) {
165 rdev_munmap(&fmrd, area);
166 offset += sizeof(struct fmap_area);
167 continue;
168 }
169
Duncan Lauriebc2c0a32016-02-09 09:17:56 -0800170 printk(BIOS_DEBUG, "FMAP: area %s found @ %x (%d bytes)\n",
171 name, area->offset, area->size);
Aaron Durbin0424c952015-03-28 23:56:22 -0500172
173 ar->offset = area->offset;
174 ar->size = area->size;
175
176 rdev_munmap(&fmrd, area);
177
178 return 0;
179 }
180
181 printk(BIOS_DEBUG, "FMAP: area %s not found\n", name);
182
183 return -1;
184}
Patrick Georgi99526902015-07-09 11:27:44 +0200185
186int fmap_find_region_name(const struct region * const ar,
187 char name[FMAP_STRLEN])
188{
189 struct region_device fmrd;
190 size_t offset;
191
192 if (find_fmap_directory(&fmrd))
193 return -1;
194
195 /* Start reading the areas just after fmap header. */
196 offset = sizeof(struct fmap);
197
198 while (1) {
199 struct fmap_area *area;
200
201 area = rdev_mmap(&fmrd, offset, sizeof(*area));
202
203 if (area == NULL)
204 return -1;
205
206 if ((ar->offset != area->offset) ||
207 (ar->size != area->size)) {
208 rdev_munmap(&fmrd, area);
209 offset += sizeof(struct fmap_area);
210 continue;
211 }
212
213 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) found, named %s\n",
214 ar->offset, ar->size, area->name);
215
216 memcpy(name, area->name, FMAP_STRLEN);
217
218 rdev_munmap(&fmrd, area);
219
220 return 0;
221 }
222
223 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) not found\n",
224 ar->offset, ar->size);
225
226 return -1;
227}
T Michael Turney19fcc892019-03-20 14:37:34 -0700228
229ssize_t fmap_read_area(const char *name, void *buffer, size_t size)
230{
231 struct region_device rdev;
232 if (fmap_locate_area_as_rdev(name, &rdev))
233 return -1;
234 return rdev_readat(&rdev, buffer, 0,
235 MIN(size, region_device_sz(&rdev)));
236}
237
238ssize_t fmap_overwrite_area(const char *name, const void *buffer, size_t size)
239{
240 struct region_device rdev;
241
242 if (fmap_locate_area_as_rdev_rw(name, &rdev))
243 return -1;
244 if (size > region_device_sz(&rdev))
245 return -1;
246 if (rdev_eraseat(&rdev, 0, region_device_sz(&rdev)) < 0)
247 return -1;
248 return rdev_writeat(&rdev, buffer, 0, size);
249}
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200250
Julius Wernercefe89e2019-11-06 19:29:44 -0800251static void fmap_register_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200252{
253 const struct cbmem_entry *e;
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200254
255 /* Find the FMAP cache installed by previous stage */
256 e = cbmem_entry_find(CBMEM_ID_FMAP);
257 /* Don't set fmap_cache so that find_fmap_directory will use regular path */
258 if (!e)
259 return;
260
Arthur Heymansdba22d22019-11-20 19:57:49 +0100261 mem_region_device_ro_init(&fmap_cache, cbmem_entry_start(e), cbmem_entry_size(e));
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200262}
263
264/*
265 * The main reason to copy the FMAP into CBMEM is to make it available to the
266 * OS on every architecture. As side effect use the CBMEM copy as cache.
267 */
Julius Wernercefe89e2019-11-06 19:29:44 -0800268static void fmap_setup_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200269{
270 struct region_device fmrd;
271
272 if (find_fmap_directory(&fmrd))
273 return;
274
275 /* Reloads the FMAP even on ACPI S3 resume */
276 const size_t s = region_device_sz(&fmrd);
277 struct fmap *fmap = cbmem_add(CBMEM_ID_FMAP, s);
278 if (!fmap) {
279 printk(BIOS_ERR, "ERROR: Failed to allocate CBMEM\n");
280 return;
281 }
282
283 const ssize_t ret = rdev_readat(&fmrd, fmap, 0, s);
284 if (ret != s) {
285 printk(BIOS_ERR, "ERROR: Failed to read FMAP into CBMEM\n");
286 cbmem_entry_remove(cbmem_entry_find(CBMEM_ID_FMAP));
287 return;
288 }
289
290 /* Finally advertise the cache for the current stage */
Julius Wernercefe89e2019-11-06 19:29:44 -0800291 fmap_register_cbmem_cache(unused);
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200292}
293
Julius Wernercefe89e2019-11-06 19:29:44 -0800294ROMSTAGE_CBMEM_INIT_HOOK(fmap_setup_cbmem_cache)
295RAMSTAGE_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)
296POSTCAR_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)