blob: 48aab8f3d5d033f5501e4d84fc4a68cfe15e5a0a [file] [log] [blame]
Aaron Durbin0424c952015-03-28 23:56:22 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2012-2015 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Aaron Durbin0424c952015-03-28 23:56:22 -050014 */
15
Duncan Lauriebc2c0a32016-02-09 09:17:56 -080016#include <arch/early_variables.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050017#include <boot_device.h>
Julius Wernercefe89e2019-11-06 19:29:44 -080018#include <cbmem.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050019#include <console/console.h>
20#include <fmap.h>
Aaron Durbindc9f5cd2015-09-08 13:34:43 -050021#include <commonlib/fmap_serialized.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050022#include <stddef.h>
23#include <string.h>
Julius Wernercefe89e2019-11-06 19:29:44 -080024#include <symbols.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050025
Aaron Durbinbf1e4812016-05-10 15:12:08 -050026#include "fmap_config.h"
27
Aaron Durbin0424c952015-03-28 23:56:22 -050028/*
29 * See http://code.google.com/p/flashmap/ for more information on FMAP.
30 */
31
Duncan Lauriebc2c0a32016-02-09 09:17:56 -080032static int fmap_print_once CAR_GLOBAL;
Patrick Rudolph6d787c22019-09-12 13:21:37 +020033static struct mem_region_device fmap_cache CAR_GLOBAL;
Duncan Lauriebc2c0a32016-02-09 09:17:56 -080034
Julius Wernercefe89e2019-11-06 19:29:44 -080035#define print_once(...) do { \
36 if (!car_get_var(fmap_print_once)) \
37 printk(__VA_ARGS__); \
38 } while (0)
39
40DECLARE_OPTIONAL_REGION(fmap_cache);
41
Furquan Shaikhb33a2b02019-09-26 23:51:46 -070042uint64_t get_fmap_flash_offset(void)
43{
44 return FMAP_OFFSET;
45}
46
Julius Wernercefe89e2019-11-06 19:29:44 -080047static int check_signature(const struct fmap *fmap)
48{
49 return memcmp(fmap->signature, FMAP_SIGNATURE, sizeof(fmap->signature));
50}
51
52static void report(const struct fmap *fmap)
53{
54 print_once(BIOS_DEBUG, "FMAP: Found \"%s\" version %d.%d at %#x.\n",
55 fmap->name, fmap->ver_major, fmap->ver_minor, FMAP_OFFSET);
56 print_once(BIOS_DEBUG, "FMAP: base = %#llx size = %#x #areas = %d\n",
57 (long long)fmap->base, fmap->size, fmap->nareas);
58 car_set_var(fmap_print_once, 1);
59}
60
61static void setup_preram_cache(struct mem_region_device *cache_mrdev)
62{
Julius Werner7fc92862019-11-18 13:01:06 -080063 if (CONFIG(NO_FMAP_CACHE))
64 return;
65
Julius Wernercefe89e2019-11-06 19:29:44 -080066 if (!ENV_ROMSTAGE_OR_BEFORE) {
67 /* We get here if ramstage makes an FMAP access before calling
68 cbmem_initialize(). We should avoid letting it come to that,
69 so print a warning. */
70 print_once(BIOS_WARNING,
71 "WARNING: Post-RAM FMAP access too early for cache!\n");
72 return;
73 }
74
75 if (REGION_SIZE(fmap_cache) == 0) {
Julius Werner7fc92862019-11-18 13:01:06 -080076 /* If you see this you should add FMAP_CACHE() to your memlayout
77 (or select NO_FMAP_CACHE if you can't afford the 2K). */
78 print_once(BIOS_ERR,
79 "ERROR: FMAP_CACHE enabled but no region provided!\n");
Julius Wernercefe89e2019-11-06 19:29:44 -080080 return;
81 }
82
83 struct fmap *fmap = (struct fmap *)_fmap_cache;
84 if (!ENV_BOOTBLOCK) {
85 /* NOTE: This assumes that for all platforms running this code,
86 the bootblock is the first stage and the bootblock will make
87 at least one FMAP access (usually from finding CBFS). */
88 if (!check_signature(fmap))
89 goto register_cache;
90
91 printk(BIOS_ERR, "ERROR: FMAP cache corrupted?!\n");
92 }
93
94 /* In case we fail below, make sure the cache is invalid. */
95 memset(fmap->signature, 0, sizeof(fmap->signature));
96
97 boot_device_init();
98 const struct region_device *boot_rdev = boot_device_ro();
99 if (!boot_rdev)
100 return;
101
102 /* memlayout statically guarantees that the FMAP_CACHE is big enough. */
103 if (rdev_readat(boot_rdev, fmap, FMAP_OFFSET, FMAP_SIZE) != FMAP_SIZE)
104 return;
105 if (check_signature(fmap))
106 return;
107 report(fmap);
108
109register_cache:
110 mem_region_device_ro_init(cache_mrdev, fmap, FMAP_SIZE);
111}
112
Furquan Shaikhb33a2b02019-09-26 23:51:46 -0700113static int find_fmap_directory(struct region_device *fmrd)
Aaron Durbin0424c952015-03-28 23:56:22 -0500114{
115 const struct region_device *boot;
116 struct fmap *fmap;
Julius Wernercefe89e2019-11-06 19:29:44 -0800117 struct mem_region_device *cache;
Aaron Durbinbf1e4812016-05-10 15:12:08 -0500118 size_t offset = FMAP_OFFSET;
Aaron Durbin0424c952015-03-28 23:56:22 -0500119
Julius Wernercefe89e2019-11-06 19:29:44 -0800120 /* Try FMAP cache first */
121 cache = car_get_var_ptr(&fmap_cache);
122 if (!region_device_sz(&cache->rdev))
123 setup_preram_cache(cache);
124 if (region_device_sz(&cache->rdev))
125 return rdev_chain_full(fmrd, &cache->rdev);
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200126
Aaron Durbin0424c952015-03-28 23:56:22 -0500127 boot_device_init();
128 boot = boot_device_ro();
129
130 if (boot == NULL)
131 return -1;
132
Julius Wernercefe89e2019-11-06 19:29:44 -0800133 fmap = rdev_mmap(boot, offset, sizeof(struct fmap));
Aaron Durbin0424c952015-03-28 23:56:22 -0500134
135 if (fmap == NULL)
136 return -1;
137
Julius Wernercefe89e2019-11-06 19:29:44 -0800138 if (check_signature(fmap)) {
Aaron Durbin0424c952015-03-28 23:56:22 -0500139 printk(BIOS_DEBUG, "No FMAP found at %zx offset.\n", offset);
140 rdev_munmap(boot, fmap);
141 return -1;
142 }
143
Julius Wernercefe89e2019-11-06 19:29:44 -0800144 report(fmap);
Aaron Durbin0424c952015-03-28 23:56:22 -0500145
146 rdev_munmap(boot, fmap);
147
Julius Wernercefe89e2019-11-06 19:29:44 -0800148 return rdev_chain(fmrd, boot, offset, FMAP_SIZE);
Aaron Durbin0424c952015-03-28 23:56:22 -0500149}
150
151int fmap_locate_area_as_rdev(const char *name, struct region_device *area)
152{
153 struct region ar;
154
155 if (fmap_locate_area(name, &ar))
156 return -1;
157
158 return boot_device_ro_subregion(&ar, area);
159}
160
Aaron Durbinbccaab82016-08-12 12:42:04 -0500161int fmap_locate_area_as_rdev_rw(const char *name, struct region_device *area)
162{
163 struct region ar;
164
165 if (fmap_locate_area(name, &ar))
166 return -1;
167
168 return boot_device_rw_subregion(&ar, area);
169}
170
Aaron Durbin0424c952015-03-28 23:56:22 -0500171int fmap_locate_area(const char *name, struct region *ar)
172{
173 struct region_device fmrd;
174 size_t offset;
175
176 if (find_fmap_directory(&fmrd))
177 return -1;
178
179 /* Start reading the areas just after fmap header. */
180 offset = sizeof(struct fmap);
181
182 while (1) {
183 struct fmap_area *area;
184
185 area = rdev_mmap(&fmrd, offset, sizeof(*area));
186
187 if (area == NULL)
188 return -1;
189
190 if (strcmp((const char *)area->name, name)) {
191 rdev_munmap(&fmrd, area);
192 offset += sizeof(struct fmap_area);
193 continue;
194 }
195
Duncan Lauriebc2c0a32016-02-09 09:17:56 -0800196 printk(BIOS_DEBUG, "FMAP: area %s found @ %x (%d bytes)\n",
197 name, area->offset, area->size);
Aaron Durbin0424c952015-03-28 23:56:22 -0500198
199 ar->offset = area->offset;
200 ar->size = area->size;
201
202 rdev_munmap(&fmrd, area);
203
204 return 0;
205 }
206
207 printk(BIOS_DEBUG, "FMAP: area %s not found\n", name);
208
209 return -1;
210}
Patrick Georgi99526902015-07-09 11:27:44 +0200211
212int fmap_find_region_name(const struct region * const ar,
213 char name[FMAP_STRLEN])
214{
215 struct region_device fmrd;
216 size_t offset;
217
218 if (find_fmap_directory(&fmrd))
219 return -1;
220
221 /* Start reading the areas just after fmap header. */
222 offset = sizeof(struct fmap);
223
224 while (1) {
225 struct fmap_area *area;
226
227 area = rdev_mmap(&fmrd, offset, sizeof(*area));
228
229 if (area == NULL)
230 return -1;
231
232 if ((ar->offset != area->offset) ||
233 (ar->size != area->size)) {
234 rdev_munmap(&fmrd, area);
235 offset += sizeof(struct fmap_area);
236 continue;
237 }
238
239 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) found, named %s\n",
240 ar->offset, ar->size, area->name);
241
242 memcpy(name, area->name, FMAP_STRLEN);
243
244 rdev_munmap(&fmrd, area);
245
246 return 0;
247 }
248
249 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) not found\n",
250 ar->offset, ar->size);
251
252 return -1;
253}
T Michael Turney19fcc892019-03-20 14:37:34 -0700254
255ssize_t fmap_read_area(const char *name, void *buffer, size_t size)
256{
257 struct region_device rdev;
258 if (fmap_locate_area_as_rdev(name, &rdev))
259 return -1;
260 return rdev_readat(&rdev, buffer, 0,
261 MIN(size, region_device_sz(&rdev)));
262}
263
264ssize_t fmap_overwrite_area(const char *name, const void *buffer, size_t size)
265{
266 struct region_device rdev;
267
268 if (fmap_locate_area_as_rdev_rw(name, &rdev))
269 return -1;
270 if (size > region_device_sz(&rdev))
271 return -1;
272 if (rdev_eraseat(&rdev, 0, region_device_sz(&rdev)) < 0)
273 return -1;
274 return rdev_writeat(&rdev, buffer, 0, size);
275}
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200276
Julius Wernercefe89e2019-11-06 19:29:44 -0800277static void fmap_register_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200278{
279 const struct cbmem_entry *e;
280 struct mem_region_device *mdev;
281
282 mdev = car_get_var_ptr(&fmap_cache);
283
284 /* Find the FMAP cache installed by previous stage */
285 e = cbmem_entry_find(CBMEM_ID_FMAP);
286 /* Don't set fmap_cache so that find_fmap_directory will use regular path */
287 if (!e)
288 return;
289
290 mem_region_device_ro_init(mdev, cbmem_entry_start(e), cbmem_entry_size(e));
291}
292
293/*
294 * The main reason to copy the FMAP into CBMEM is to make it available to the
295 * OS on every architecture. As side effect use the CBMEM copy as cache.
296 */
Julius Wernercefe89e2019-11-06 19:29:44 -0800297static void fmap_setup_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200298{
299 struct region_device fmrd;
300
301 if (find_fmap_directory(&fmrd))
302 return;
303
304 /* Reloads the FMAP even on ACPI S3 resume */
305 const size_t s = region_device_sz(&fmrd);
306 struct fmap *fmap = cbmem_add(CBMEM_ID_FMAP, s);
307 if (!fmap) {
308 printk(BIOS_ERR, "ERROR: Failed to allocate CBMEM\n");
309 return;
310 }
311
312 const ssize_t ret = rdev_readat(&fmrd, fmap, 0, s);
313 if (ret != s) {
314 printk(BIOS_ERR, "ERROR: Failed to read FMAP into CBMEM\n");
315 cbmem_entry_remove(cbmem_entry_find(CBMEM_ID_FMAP));
316 return;
317 }
318
319 /* Finally advertise the cache for the current stage */
Julius Wernercefe89e2019-11-06 19:29:44 -0800320 fmap_register_cbmem_cache(unused);
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200321}
322
Julius Wernercefe89e2019-11-06 19:29:44 -0800323ROMSTAGE_CBMEM_INIT_HOOK(fmap_setup_cbmem_cache)
324RAMSTAGE_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)
325POSTCAR_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)