blob: 19737a419239972b41a5e4429cec2f76127d8ee3 [file] [log] [blame]
Peter Stuge483b7bb2009-04-14 07:40:01 +00001/*
2 * This file is part of the coreboot project.
3 *
Aaron Durbin899d13d2015-05-15 23:39:23 -05004 * Copyright (C) 2011 secunet Security Networks AG
5 * Copyright 2015 Google Inc.
Peter Stuge483b7bb2009-04-14 07:40:01 +00006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Peter Stuge483b7bb2009-04-14 07:40:01 +000015 */
16
Aaron Durbin899d13d2015-05-15 23:39:23 -050017#include <assert.h>
18#include <string.h>
19#include <stdlib.h>
20#include <boot_device.h>
21#include <cbfs.h>
Julius Werner09f29212015-09-29 13:51:35 -070022#include <commonlib/compression.h>
Aaron Durbin899d13d2015-05-15 23:39:23 -050023#include <endian.h>
24#include <lib.h>
25#include <symbols.h>
Julius Werner09f29212015-09-29 13:51:35 -070026#include <timestamp.h>
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +080027
Patrick Georgi58a150a2016-05-02 17:22:29 +080028#include "fmap_config.h"
29
Aaron Durbin899d13d2015-05-15 23:39:23 -050030#define ERROR(x...) printk(BIOS_ERR, "CBFS: " x)
31#define LOG(x...) printk(BIOS_INFO, "CBFS: " x)
32#if IS_ENABLED(CONFIG_DEBUG_CBFS)
33#define DEBUG(x...) printk(BIOS_SPEW, "CBFS: " x)
34#else
35#define DEBUG(x...)
36#endif
Peter Stuge483b7bb2009-04-14 07:40:01 +000037
Aaron Durbin37a5d152015-09-17 16:09:30 -050038int cbfs_boot_locate(struct cbfsf *fh, const char *name, uint32_t *type)
Aaron Durbin899d13d2015-05-15 23:39:23 -050039{
Aaron Durbin899d13d2015-05-15 23:39:23 -050040 struct region_device rdev;
41 const struct region_device *boot_dev;
42 struct cbfs_props props;
43
Aaron Durbin899d13d2015-05-15 23:39:23 -050044 if (cbfs_boot_region_properties(&props))
45 return -1;
46
47 /* All boot CBFS operations are performed using the RO devie. */
48 boot_dev = boot_device_ro();
49
50 if (boot_dev == NULL)
51 return -1;
52
53 if (rdev_chain(&rdev, boot_dev, props.offset, props.size))
54 return -1;
55
Aaron Durbin37a5d152015-09-17 16:09:30 -050056 return cbfs_locate(fh, &rdev, name, type);
Aaron Durbin899d13d2015-05-15 23:39:23 -050057}
58
59void *cbfs_boot_map_with_leak(const char *name, uint32_t type, size_t *size)
60{
Aaron Durbin37a5d152015-09-17 16:09:30 -050061 struct cbfsf fh;
Aaron Durbin899d13d2015-05-15 23:39:23 -050062 size_t fsize;
63
64 if (cbfs_boot_locate(&fh, name, &type))
65 return NULL;
66
Aaron Durbin37a5d152015-09-17 16:09:30 -050067 fsize = region_device_sz(&fh.data);
Aaron Durbin899d13d2015-05-15 23:39:23 -050068
69 if (size != NULL)
70 *size = fsize;
71
Aaron Durbin37a5d152015-09-17 16:09:30 -050072 return rdev_mmap(&fh.data, 0, fsize);
Aaron Durbin899d13d2015-05-15 23:39:23 -050073}
74
Julius Werner09f29212015-09-29 13:51:35 -070075size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
76 size_t in_size, void *buffer, size_t buffer_size, uint32_t compression)
Aaron Durbin899d13d2015-05-15 23:39:23 -050077{
Julius Werner09f29212015-09-29 13:51:35 -070078 size_t out_size;
79
80 switch (compression) {
81 case CBFS_COMPRESS_NONE:
Julius Wernerf975e552016-08-19 15:43:06 -070082 if (buffer_size < in_size)
83 return 0;
Julius Werner09f29212015-09-29 13:51:35 -070084 if (rdev_readat(rdev, buffer, offset, in_size) != in_size)
85 return 0;
86 return in_size;
87
88 case CBFS_COMPRESS_LZ4:
89 if ((ENV_BOOTBLOCK || ENV_VERSTAGE) &&
90 !IS_ENABLED(CONFIG_COMPRESS_PRERAM_STAGES))
91 return 0;
92
93 /* Load the compressed image to the end of the available memory
94 * area for in-place decompression. It is the responsibility of
95 * the caller to ensure that buffer_size is large enough
96 * (see compression.h, guaranteed by cbfstool for stages). */
97 void *compr_start = buffer + buffer_size - in_size;
98 if (rdev_readat(rdev, compr_start, offset, in_size) != in_size)
99 return 0;
100
101 timestamp_add_now(TS_START_ULZ4F);
102 out_size = ulz4fn(compr_start, in_size, buffer, buffer_size);
103 timestamp_add_now(TS_END_ULZ4F);
104 return out_size;
105
106 case CBFS_COMPRESS_LZMA:
107 if (ENV_BOOTBLOCK || ENV_VERSTAGE)
108 return 0;
Lee Leahyd950f512016-07-25 09:53:35 -0700109 if ((ENV_ROMSTAGE || ENV_POSTCAR)
110 && !IS_ENABLED(CONFIG_COMPRESS_RAMSTAGE))
Julius Werner09f29212015-09-29 13:51:35 -0700111 return 0;
Julius Werner09f29212015-09-29 13:51:35 -0700112 void *map = rdev_mmap(rdev, offset, in_size);
113 if (map == NULL)
114 return 0;
115
116 /* Note: timestamp not useful for memory-mapped media (x86) */
117 timestamp_add_now(TS_START_ULZMA);
118 out_size = ulzman(map, in_size, buffer, buffer_size);
119 timestamp_add_now(TS_END_ULZMA);
120
121 rdev_munmap(rdev, map);
122
123 return out_size;
124
125 default:
Aaron Durbin899d13d2015-05-15 23:39:23 -0500126 return 0;
Julius Werner09f29212015-09-29 13:51:35 -0700127 }
Aaron Durbin899d13d2015-05-15 23:39:23 -0500128}
129
Stefan Reinauer800379f2010-03-01 08:34:19 +0000130static inline int tohex4(unsigned int c)
Patrick Georgib203c2f2009-08-20 14:48:03 +0000131{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800132 return (c <= 9) ? (c + '0') : (c - 10 + 'a');
Patrick Georgib203c2f2009-08-20 14:48:03 +0000133}
134
135static void tohex16(unsigned int val, char* dest)
136{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800137 dest[0] = tohex4(val>>12);
138 dest[1] = tohex4((val>>8) & 0xf);
139 dest[2] = tohex4((val>>4) & 0xf);
140 dest[3] = tohex4(val & 0xf);
Patrick Georgib203c2f2009-08-20 14:48:03 +0000141}
142
Aaron Durbin899d13d2015-05-15 23:39:23 -0500143void *cbfs_boot_map_optionrom(uint16_t vendor, uint16_t device)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000144{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800145 char name[17] = "pciXXXX,XXXX.rom";
Peter Stuge483b7bb2009-04-14 07:40:01 +0000146
Patrick Georgib203c2f2009-08-20 14:48:03 +0000147 tohex16(vendor, name+3);
148 tohex16(device, name+8);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000149
Aaron Durbin899d13d2015-05-15 23:39:23 -0500150 return cbfs_boot_map_with_leak(name, CBFS_TYPE_OPTIONROM, NULL);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000151}
152
Aaron Durbin899d13d2015-05-15 23:39:23 -0500153void *cbfs_boot_load_stage_by_name(const char *name)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000154{
Aaron Durbin37a5d152015-09-17 16:09:30 -0500155 struct cbfsf fh;
Aaron Durbin7e7a4df2015-12-08 14:34:35 -0600156 struct prog stage = PROG_INIT(PROG_UNKNOWN, name);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500157 uint32_t type = CBFS_TYPE_STAGE;
Aaron Durbin3948e532015-03-20 13:00:20 -0500158
Aaron Durbin37a5d152015-09-17 16:09:30 -0500159 if (cbfs_boot_locate(&fh, name, &type))
Aaron Durbin899d13d2015-05-15 23:39:23 -0500160 return NULL;
Aaron Durbin3948e532015-03-20 13:00:20 -0500161
Aaron Durbin37a5d152015-09-17 16:09:30 -0500162 /* Chain data portion in the prog. */
163 cbfs_file_data(prog_rdev(&stage), &fh);
164
Aaron Durbin899d13d2015-05-15 23:39:23 -0500165 if (cbfs_prog_stage_load(&stage))
166 return NULL;
167
168 return prog_entry(&stage);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000169}
170
Julius Wernerf975e552016-08-19 15:43:06 -0700171size_t cbfs_boot_load_struct(const char *name, void *buf, size_t buf_size)
172{
173 struct cbfsf fh;
174 uint32_t compression_algo;
175 size_t decompressed_size;
176 uint32_t type = CBFS_TYPE_STRUCT;
177
178 if (cbfs_boot_locate(&fh, name, &type) < 0)
179 return 0;
180
181 if (cbfsf_decompression_info(&fh, &compression_algo,
182 &decompressed_size) < 0
183 || decompressed_size > buf_size)
184 return 0;
185
186 return cbfs_load_and_decompress(&fh.data, 0, region_device_sz(&fh.data),
187 buf, buf_size, compression_algo);
188}
189
Kyösti Mälkki9d6f3652016-06-28 07:38:46 +0300190size_t cbfs_prog_stage_section(struct prog *pstage, uintptr_t *base)
191{
192 struct cbfs_stage stage;
193 const struct region_device *fh = prog_rdev(pstage);
194
195 if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage))
196 return 0;
197
198 *base = (uintptr_t)stage.load;
199 return stage.memlen;
200}
201
Aaron Durbin899d13d2015-05-15 23:39:23 -0500202int cbfs_prog_stage_load(struct prog *pstage)
203{
204 struct cbfs_stage stage;
205 uint8_t *load;
206 void *entry;
207 size_t fsize;
208 size_t foffset;
Aaron Durbin37a5d152015-09-17 16:09:30 -0500209 const struct region_device *fh = prog_rdev(pstage);
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800210
Aaron Durbin899d13d2015-05-15 23:39:23 -0500211 if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage))
Julius Wernerb29bd27b2015-12-03 11:29:12 -0800212 return -1;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500213
214 fsize = region_device_sz(fh);
215 fsize -= sizeof(stage);
216 foffset = 0;
217 foffset += sizeof(stage);
218
219 assert(fsize == stage.len);
220
221 /* Note: cbfs_stage fields are currently in the endianness of the
222 * running processor. */
223 load = (void *)(uintptr_t)stage.load;
224 entry = (void *)(uintptr_t)stage.entry;
225
Aaron Durbined253c82015-10-07 17:22:42 -0500226 /* Hacky way to not load programs over read only media. The stages
227 * that would hit this path initialize themselves. */
Furquan Shaikhd5583a52016-06-01 01:53:18 -0700228 if (ENV_VERSTAGE && !IS_ENABLED(CONFIG_NO_XIP_EARLY_STAGES) &&
Aaron Durbin16c173f2016-08-11 14:04:10 -0500229 IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)) {
Aaron Durbined253c82015-10-07 17:22:42 -0500230 void *mapping = rdev_mmap(fh, foffset, fsize);
231 rdev_munmap(fh, mapping);
232 if (mapping == load)
233 goto out;
234 }
235
Julius Werner09f29212015-09-29 13:51:35 -0700236 fsize = cbfs_load_and_decompress(fh, foffset, fsize, load,
237 stage.memlen, stage.compression);
238 if (!fsize)
Aaron Durbin899d13d2015-05-15 23:39:23 -0500239 return -1;
240
241 /* Clear area not covered by file. */
242 memset(&load[fsize], 0, stage.memlen - fsize);
243
Aaron Durbin096f4572016-03-31 13:49:00 -0500244 prog_segment_loaded((uintptr_t)load, stage.memlen, SEG_FINAL);
Aaron Durbined253c82015-10-07 17:22:42 -0500245
246out:
Aaron Durbin899d13d2015-05-15 23:39:23 -0500247 prog_set_area(pstage, load, stage.memlen);
248 prog_set_entry(pstage, entry, NULL);
249
250 return 0;
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800251}
Aaron Durbin6d720f32015-12-08 17:00:23 -0600252
Patrick Georgi58a150a2016-05-02 17:22:29 +0800253/* This only supports the "COREBOOT" fmap region. */
Aaron Durbin6d720f32015-12-08 17:00:23 -0600254static int cbfs_master_header_props(struct cbfs_props *props)
255{
256 struct cbfs_header header;
257 const struct region_device *bdev;
258 int32_t rel_offset;
259 size_t offset;
260
261 bdev = boot_device_ro();
262
263 if (bdev == NULL)
264 return -1;
265
Patrick Georgi58a150a2016-05-02 17:22:29 +0800266 size_t fmap_top = ___FMAP__COREBOOT_BASE + ___FMAP__COREBOOT_SIZE;
267
Aaron Durbin6d720f32015-12-08 17:00:23 -0600268 /* Find location of header using signed 32-bit offset from
269 * end of CBFS region. */
Patrick Georgi58a150a2016-05-02 17:22:29 +0800270 offset = fmap_top - sizeof(int32_t);
Aaron Durbin6d720f32015-12-08 17:00:23 -0600271 if (rdev_readat(bdev, &rel_offset, offset, sizeof(int32_t)) < 0)
272 return -1;
273
Patrick Georgi58a150a2016-05-02 17:22:29 +0800274 offset = fmap_top + rel_offset;
Aaron Durbin6d720f32015-12-08 17:00:23 -0600275 if (rdev_readat(bdev, &header, offset, sizeof(header)) < 0)
276 return -1;
277
278 header.magic = ntohl(header.magic);
279 header.romsize = ntohl(header.romsize);
280 header.offset = ntohl(header.offset);
281
282 if (header.magic != CBFS_HEADER_MAGIC)
283 return -1;
284
285 props->offset = header.offset;
286 props->size = header.romsize;
287 props->size -= props->offset;
288
289 printk(BIOS_SPEW, "CBFS @ %zx size %zx\n", props->offset, props->size);
290
291 return 0;
292}
293
294/* This struct is marked as weak to allow a particular platform to
295 * override the master header logic. This implementation should work for most
296 * devices. */
297const struct cbfs_locator __attribute__((weak)) cbfs_master_header_locator = {
298 .name = "Master Header Locator",
299 .locate = cbfs_master_header_props,
300};
301
302extern const struct cbfs_locator vboot_locator;
303
304static const struct cbfs_locator *locators[] = {
Furquan Shaikh2a12e2e2016-07-25 11:48:03 -0700305#if CONFIG_VBOOT
Aaron Durbin6d720f32015-12-08 17:00:23 -0600306 &vboot_locator,
307#endif
308 &cbfs_master_header_locator,
309};
310
311int cbfs_boot_region_properties(struct cbfs_props *props)
312{
313 int i;
314
315 boot_device_init();
316
317 for (i = 0; i < ARRAY_SIZE(locators); i++) {
318 const struct cbfs_locator *ops;
319
320 ops = locators[i];
321
322 if (ops->locate == NULL)
323 continue;
324
325 if (ops->locate(props))
326 continue;
327
328 LOG("'%s' located CBFS at [%zx:%zx)\n",
329 ops->name, props->offset, props->offset + props->size);
330
331 return 0;
332 }
333
334 return -1;
335}
336
337void cbfs_prepare_program_locate(void)
338{
339 int i;
340
341 boot_device_init();
342
343 for (i = 0; i < ARRAY_SIZE(locators); i++) {
344 if (locators[i]->prepare == NULL)
345 continue;
346 locators[i]->prepare();
347 }
348}