blob: 4b4179c7698d6b26feea217342181eb8d891bb8d [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{
63 if (!ENV_ROMSTAGE_OR_BEFORE) {
64 /* We get here if ramstage makes an FMAP access before calling
65 cbmem_initialize(). We should avoid letting it come to that,
66 so print a warning. */
67 print_once(BIOS_WARNING,
68 "WARNING: Post-RAM FMAP access too early for cache!\n");
69 return;
70 }
71
72 if (REGION_SIZE(fmap_cache) == 0) {
73 /* If you see this you really want to add an FMAP_CACHE to your
74 memlayout, unless you absolutely can't affort the 2K. */
75 print_once(BIOS_NOTICE,
76 "NOTE: Running without FMAP_CACHE, should add it!\n");
77 return;
78 }
79
80 struct fmap *fmap = (struct fmap *)_fmap_cache;
81 if (!ENV_BOOTBLOCK) {
82 /* NOTE: This assumes that for all platforms running this code,
83 the bootblock is the first stage and the bootblock will make
84 at least one FMAP access (usually from finding CBFS). */
85 if (!check_signature(fmap))
86 goto register_cache;
87
88 printk(BIOS_ERR, "ERROR: FMAP cache corrupted?!\n");
89 }
90
91 /* In case we fail below, make sure the cache is invalid. */
92 memset(fmap->signature, 0, sizeof(fmap->signature));
93
94 boot_device_init();
95 const struct region_device *boot_rdev = boot_device_ro();
96 if (!boot_rdev)
97 return;
98
99 /* memlayout statically guarantees that the FMAP_CACHE is big enough. */
100 if (rdev_readat(boot_rdev, fmap, FMAP_OFFSET, FMAP_SIZE) != FMAP_SIZE)
101 return;
102 if (check_signature(fmap))
103 return;
104 report(fmap);
105
106register_cache:
107 mem_region_device_ro_init(cache_mrdev, fmap, FMAP_SIZE);
108}
109
Furquan Shaikhb33a2b02019-09-26 23:51:46 -0700110static int find_fmap_directory(struct region_device *fmrd)
Aaron Durbin0424c952015-03-28 23:56:22 -0500111{
112 const struct region_device *boot;
113 struct fmap *fmap;
Julius Wernercefe89e2019-11-06 19:29:44 -0800114 struct mem_region_device *cache;
Aaron Durbinbf1e4812016-05-10 15:12:08 -0500115 size_t offset = FMAP_OFFSET;
Aaron Durbin0424c952015-03-28 23:56:22 -0500116
Julius Wernercefe89e2019-11-06 19:29:44 -0800117 /* Try FMAP cache first */
118 cache = car_get_var_ptr(&fmap_cache);
119 if (!region_device_sz(&cache->rdev))
120 setup_preram_cache(cache);
121 if (region_device_sz(&cache->rdev))
122 return rdev_chain_full(fmrd, &cache->rdev);
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200123
Aaron Durbin0424c952015-03-28 23:56:22 -0500124 boot_device_init();
125 boot = boot_device_ro();
126
127 if (boot == NULL)
128 return -1;
129
Julius Wernercefe89e2019-11-06 19:29:44 -0800130 fmap = rdev_mmap(boot, offset, sizeof(struct fmap));
Aaron Durbin0424c952015-03-28 23:56:22 -0500131
132 if (fmap == NULL)
133 return -1;
134
Julius Wernercefe89e2019-11-06 19:29:44 -0800135 if (check_signature(fmap)) {
Aaron Durbin0424c952015-03-28 23:56:22 -0500136 printk(BIOS_DEBUG, "No FMAP found at %zx offset.\n", offset);
137 rdev_munmap(boot, fmap);
138 return -1;
139 }
140
Julius Wernercefe89e2019-11-06 19:29:44 -0800141 report(fmap);
Aaron Durbin0424c952015-03-28 23:56:22 -0500142
143 rdev_munmap(boot, fmap);
144
Julius Wernercefe89e2019-11-06 19:29:44 -0800145 return rdev_chain(fmrd, boot, offset, FMAP_SIZE);
Aaron Durbin0424c952015-03-28 23:56:22 -0500146}
147
148int fmap_locate_area_as_rdev(const char *name, struct region_device *area)
149{
150 struct region ar;
151
152 if (fmap_locate_area(name, &ar))
153 return -1;
154
155 return boot_device_ro_subregion(&ar, area);
156}
157
Aaron Durbinbccaab82016-08-12 12:42:04 -0500158int fmap_locate_area_as_rdev_rw(const char *name, struct region_device *area)
159{
160 struct region ar;
161
162 if (fmap_locate_area(name, &ar))
163 return -1;
164
165 return boot_device_rw_subregion(&ar, area);
166}
167
Aaron Durbin0424c952015-03-28 23:56:22 -0500168int fmap_locate_area(const char *name, struct region *ar)
169{
170 struct region_device fmrd;
171 size_t offset;
172
173 if (find_fmap_directory(&fmrd))
174 return -1;
175
176 /* Start reading the areas just after fmap header. */
177 offset = sizeof(struct fmap);
178
179 while (1) {
180 struct fmap_area *area;
181
182 area = rdev_mmap(&fmrd, offset, sizeof(*area));
183
184 if (area == NULL)
185 return -1;
186
187 if (strcmp((const char *)area->name, name)) {
188 rdev_munmap(&fmrd, area);
189 offset += sizeof(struct fmap_area);
190 continue;
191 }
192
Duncan Lauriebc2c0a32016-02-09 09:17:56 -0800193 printk(BIOS_DEBUG, "FMAP: area %s found @ %x (%d bytes)\n",
194 name, area->offset, area->size);
Aaron Durbin0424c952015-03-28 23:56:22 -0500195
196 ar->offset = area->offset;
197 ar->size = area->size;
198
199 rdev_munmap(&fmrd, area);
200
201 return 0;
202 }
203
204 printk(BIOS_DEBUG, "FMAP: area %s not found\n", name);
205
206 return -1;
207}
Patrick Georgi99526902015-07-09 11:27:44 +0200208
209int fmap_find_region_name(const struct region * const ar,
210 char name[FMAP_STRLEN])
211{
212 struct region_device fmrd;
213 size_t offset;
214
215 if (find_fmap_directory(&fmrd))
216 return -1;
217
218 /* Start reading the areas just after fmap header. */
219 offset = sizeof(struct fmap);
220
221 while (1) {
222 struct fmap_area *area;
223
224 area = rdev_mmap(&fmrd, offset, sizeof(*area));
225
226 if (area == NULL)
227 return -1;
228
229 if ((ar->offset != area->offset) ||
230 (ar->size != area->size)) {
231 rdev_munmap(&fmrd, area);
232 offset += sizeof(struct fmap_area);
233 continue;
234 }
235
236 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) found, named %s\n",
237 ar->offset, ar->size, area->name);
238
239 memcpy(name, area->name, FMAP_STRLEN);
240
241 rdev_munmap(&fmrd, area);
242
243 return 0;
244 }
245
246 printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) not found\n",
247 ar->offset, ar->size);
248
249 return -1;
250}
T Michael Turney19fcc892019-03-20 14:37:34 -0700251
252ssize_t fmap_read_area(const char *name, void *buffer, size_t size)
253{
254 struct region_device rdev;
255 if (fmap_locate_area_as_rdev(name, &rdev))
256 return -1;
257 return rdev_readat(&rdev, buffer, 0,
258 MIN(size, region_device_sz(&rdev)));
259}
260
261ssize_t fmap_overwrite_area(const char *name, const void *buffer, size_t size)
262{
263 struct region_device rdev;
264
265 if (fmap_locate_area_as_rdev_rw(name, &rdev))
266 return -1;
267 if (size > region_device_sz(&rdev))
268 return -1;
269 if (rdev_eraseat(&rdev, 0, region_device_sz(&rdev)) < 0)
270 return -1;
271 return rdev_writeat(&rdev, buffer, 0, size);
272}
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200273
Julius Wernercefe89e2019-11-06 19:29:44 -0800274static void fmap_register_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200275{
276 const struct cbmem_entry *e;
277 struct mem_region_device *mdev;
278
279 mdev = car_get_var_ptr(&fmap_cache);
280
281 /* Find the FMAP cache installed by previous stage */
282 e = cbmem_entry_find(CBMEM_ID_FMAP);
283 /* Don't set fmap_cache so that find_fmap_directory will use regular path */
284 if (!e)
285 return;
286
287 mem_region_device_ro_init(mdev, cbmem_entry_start(e), cbmem_entry_size(e));
288}
289
290/*
291 * The main reason to copy the FMAP into CBMEM is to make it available to the
292 * OS on every architecture. As side effect use the CBMEM copy as cache.
293 */
Julius Wernercefe89e2019-11-06 19:29:44 -0800294static void fmap_setup_cbmem_cache(int unused)
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200295{
296 struct region_device fmrd;
297
298 if (find_fmap_directory(&fmrd))
299 return;
300
301 /* Reloads the FMAP even on ACPI S3 resume */
302 const size_t s = region_device_sz(&fmrd);
303 struct fmap *fmap = cbmem_add(CBMEM_ID_FMAP, s);
304 if (!fmap) {
305 printk(BIOS_ERR, "ERROR: Failed to allocate CBMEM\n");
306 return;
307 }
308
309 const ssize_t ret = rdev_readat(&fmrd, fmap, 0, s);
310 if (ret != s) {
311 printk(BIOS_ERR, "ERROR: Failed to read FMAP into CBMEM\n");
312 cbmem_entry_remove(cbmem_entry_find(CBMEM_ID_FMAP));
313 return;
314 }
315
316 /* Finally advertise the cache for the current stage */
Julius Wernercefe89e2019-11-06 19:29:44 -0800317 fmap_register_cbmem_cache(unused);
Patrick Rudolph6d787c22019-09-12 13:21:37 +0200318}
319
Julius Wernercefe89e2019-11-06 19:29:44 -0800320ROMSTAGE_CBMEM_INIT_HOOK(fmap_setup_cbmem_cache)
321RAMSTAGE_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)
322POSTCAR_CBMEM_INIT_HOOK(fmap_register_cbmem_cache)