blob: 9938e6abbe46497bb15b82486d99aae33b7aecd2 [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 Durbin64031672018-04-21 14:45:32 -060023#include <compiler.h>
Aaron Durbin899d13d2015-05-15 23:39:23 -050024#include <endian.h>
25#include <lib.h>
26#include <symbols.h>
Julius Werner09f29212015-09-29 13:51:35 -070027#include <timestamp.h>
Pratik Prajapati2a7708a2016-11-30 17:29:10 -080028#include <fmap.h>
Patrick Georgi58a150a2016-05-02 17:22:29 +080029#include "fmap_config.h"
30
Aaron Durbin899d13d2015-05-15 23:39:23 -050031#define ERROR(x...) printk(BIOS_ERR, "CBFS: " x)
32#define LOG(x...) printk(BIOS_INFO, "CBFS: " x)
33#if IS_ENABLED(CONFIG_DEBUG_CBFS)
34#define DEBUG(x...) printk(BIOS_SPEW, "CBFS: " x)
35#else
36#define DEBUG(x...)
37#endif
Peter Stuge483b7bb2009-04-14 07:40:01 +000038
Aaron Durbin37a5d152015-09-17 16:09:30 -050039int cbfs_boot_locate(struct cbfsf *fh, const char *name, uint32_t *type)
Aaron Durbin899d13d2015-05-15 23:39:23 -050040{
Aaron Durbin899d13d2015-05-15 23:39:23 -050041 struct region_device rdev;
42 const struct region_device *boot_dev;
43 struct cbfs_props props;
44
Aaron Durbin899d13d2015-05-15 23:39:23 -050045 if (cbfs_boot_region_properties(&props))
46 return -1;
47
Patrick Georgid13c4cd2018-04-30 15:05:58 +020048 /* All boot CBFS operations are performed using the RO device. */
Aaron Durbin899d13d2015-05-15 23:39:23 -050049 boot_dev = boot_device_ro();
50
51 if (boot_dev == NULL)
52 return -1;
53
54 if (rdev_chain(&rdev, boot_dev, props.offset, props.size))
55 return -1;
56
Aaron Durbin37a5d152015-09-17 16:09:30 -050057 return cbfs_locate(fh, &rdev, name, type);
Aaron Durbin899d13d2015-05-15 23:39:23 -050058}
59
60void *cbfs_boot_map_with_leak(const char *name, uint32_t type, size_t *size)
61{
Aaron Durbin37a5d152015-09-17 16:09:30 -050062 struct cbfsf fh;
Aaron Durbin899d13d2015-05-15 23:39:23 -050063 size_t fsize;
64
65 if (cbfs_boot_locate(&fh, name, &type))
66 return NULL;
67
Aaron Durbin37a5d152015-09-17 16:09:30 -050068 fsize = region_device_sz(&fh.data);
Aaron Durbin899d13d2015-05-15 23:39:23 -050069
70 if (size != NULL)
71 *size = fsize;
72
Aaron Durbin37a5d152015-09-17 16:09:30 -050073 return rdev_mmap(&fh.data, 0, fsize);
Aaron Durbin899d13d2015-05-15 23:39:23 -050074}
75
Pratik Prajapati2a7708a2016-11-30 17:29:10 -080076int cbfs_locate_file_in_region(struct cbfsf *fh, const char *region_name,
77 const char *name, uint32_t *type)
78{
79 struct region_device rdev;
80
81 if (fmap_locate_area_as_rdev(region_name, &rdev)) {
82 LOG("%s region not found while looking for %s\n",
83 region_name, name);
84 return -1;
85 }
86
87 return cbfs_locate(fh, &rdev, name, type);
88}
89
Julius Werner09f29212015-09-29 13:51:35 -070090size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
91 size_t in_size, void *buffer, size_t buffer_size, uint32_t compression)
Aaron Durbin899d13d2015-05-15 23:39:23 -050092{
Julius Werner09f29212015-09-29 13:51:35 -070093 size_t out_size;
94
95 switch (compression) {
96 case CBFS_COMPRESS_NONE:
Julius Wernerf975e552016-08-19 15:43:06 -070097 if (buffer_size < in_size)
98 return 0;
Julius Werner09f29212015-09-29 13:51:35 -070099 if (rdev_readat(rdev, buffer, offset, in_size) != in_size)
100 return 0;
101 return in_size;
102
103 case CBFS_COMPRESS_LZ4:
104 if ((ENV_BOOTBLOCK || ENV_VERSTAGE) &&
105 !IS_ENABLED(CONFIG_COMPRESS_PRERAM_STAGES))
106 return 0;
107
108 /* Load the compressed image to the end of the available memory
109 * area for in-place decompression. It is the responsibility of
110 * the caller to ensure that buffer_size is large enough
111 * (see compression.h, guaranteed by cbfstool for stages). */
112 void *compr_start = buffer + buffer_size - in_size;
113 if (rdev_readat(rdev, compr_start, offset, in_size) != in_size)
114 return 0;
115
116 timestamp_add_now(TS_START_ULZ4F);
117 out_size = ulz4fn(compr_start, in_size, buffer, buffer_size);
118 timestamp_add_now(TS_END_ULZ4F);
119 return out_size;
120
121 case CBFS_COMPRESS_LZMA:
Kyösti Mälkkib5211ef2018-06-01 07:11:25 +0300122 /* We assume here romstage and postcar are never compressed. */
Julius Werner09f29212015-09-29 13:51:35 -0700123 if (ENV_BOOTBLOCK || ENV_VERSTAGE)
124 return 0;
Kyösti Mälkkib5211ef2018-06-01 07:11:25 +0300125 if (ENV_ROMSTAGE && IS_ENABLED(CONFIG_POSTCAR_STAGE))
126 return 0;
Lee Leahyd950f512016-07-25 09:53:35 -0700127 if ((ENV_ROMSTAGE || ENV_POSTCAR)
128 && !IS_ENABLED(CONFIG_COMPRESS_RAMSTAGE))
Julius Werner09f29212015-09-29 13:51:35 -0700129 return 0;
Julius Werner09f29212015-09-29 13:51:35 -0700130 void *map = rdev_mmap(rdev, offset, in_size);
131 if (map == NULL)
132 return 0;
133
134 /* Note: timestamp not useful for memory-mapped media (x86) */
135 timestamp_add_now(TS_START_ULZMA);
136 out_size = ulzman(map, in_size, buffer, buffer_size);
137 timestamp_add_now(TS_END_ULZMA);
138
139 rdev_munmap(rdev, map);
140
141 return out_size;
142
143 default:
Aaron Durbin899d13d2015-05-15 23:39:23 -0500144 return 0;
Julius Werner09f29212015-09-29 13:51:35 -0700145 }
Aaron Durbin899d13d2015-05-15 23:39:23 -0500146}
147
Stefan Reinauer800379f2010-03-01 08:34:19 +0000148static inline int tohex4(unsigned int c)
Patrick Georgib203c2f2009-08-20 14:48:03 +0000149{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800150 return (c <= 9) ? (c + '0') : (c - 10 + 'a');
Patrick Georgib203c2f2009-08-20 14:48:03 +0000151}
152
Lee Leahyb2d834a2017-03-08 16:52:22 -0800153static void tohex16(unsigned int val, char *dest)
Patrick Georgib203c2f2009-08-20 14:48:03 +0000154{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800155 dest[0] = tohex4(val>>12);
156 dest[1] = tohex4((val>>8) & 0xf);
157 dest[2] = tohex4((val>>4) & 0xf);
158 dest[3] = tohex4(val & 0xf);
Patrick Georgib203c2f2009-08-20 14:48:03 +0000159}
160
Aaron Durbin899d13d2015-05-15 23:39:23 -0500161void *cbfs_boot_map_optionrom(uint16_t vendor, uint16_t device)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000162{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800163 char name[17] = "pciXXXX,XXXX.rom";
Peter Stuge483b7bb2009-04-14 07:40:01 +0000164
Patrick Georgib203c2f2009-08-20 14:48:03 +0000165 tohex16(vendor, name+3);
166 tohex16(device, name+8);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000167
Aaron Durbin899d13d2015-05-15 23:39:23 -0500168 return cbfs_boot_map_with_leak(name, CBFS_TYPE_OPTIONROM, NULL);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000169}
170
Aaron Durbin899d13d2015-05-15 23:39:23 -0500171void *cbfs_boot_load_stage_by_name(const char *name)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000172{
Aaron Durbin37a5d152015-09-17 16:09:30 -0500173 struct cbfsf fh;
Aaron Durbin7e7a4df2015-12-08 14:34:35 -0600174 struct prog stage = PROG_INIT(PROG_UNKNOWN, name);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500175 uint32_t type = CBFS_TYPE_STAGE;
Aaron Durbin3948e532015-03-20 13:00:20 -0500176
Aaron Durbin37a5d152015-09-17 16:09:30 -0500177 if (cbfs_boot_locate(&fh, name, &type))
Aaron Durbin899d13d2015-05-15 23:39:23 -0500178 return NULL;
Aaron Durbin3948e532015-03-20 13:00:20 -0500179
Aaron Durbin37a5d152015-09-17 16:09:30 -0500180 /* Chain data portion in the prog. */
181 cbfs_file_data(prog_rdev(&stage), &fh);
182
Aaron Durbin899d13d2015-05-15 23:39:23 -0500183 if (cbfs_prog_stage_load(&stage))
184 return NULL;
185
186 return prog_entry(&stage);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000187}
188
T Michael Turney809fa7b2018-04-12 13:36:40 -0700189size_t cbfs_boot_load_file(const char *name, void *buf, size_t buf_size,
190 uint32_t type)
Julius Wernerf975e552016-08-19 15:43:06 -0700191{
192 struct cbfsf fh;
193 uint32_t compression_algo;
194 size_t decompressed_size;
Julius Wernerf975e552016-08-19 15:43:06 -0700195
196 if (cbfs_boot_locate(&fh, name, &type) < 0)
197 return 0;
198
199 if (cbfsf_decompression_info(&fh, &compression_algo,
200 &decompressed_size) < 0
201 || decompressed_size > buf_size)
202 return 0;
203
204 return cbfs_load_and_decompress(&fh.data, 0, region_device_sz(&fh.data),
205 buf, buf_size, compression_algo);
206}
207
Kyösti Mälkki9d6f3652016-06-28 07:38:46 +0300208size_t cbfs_prog_stage_section(struct prog *pstage, uintptr_t *base)
209{
210 struct cbfs_stage stage;
211 const struct region_device *fh = prog_rdev(pstage);
212
213 if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage))
214 return 0;
215
216 *base = (uintptr_t)stage.load;
217 return stage.memlen;
218}
219
Aaron Durbin899d13d2015-05-15 23:39:23 -0500220int cbfs_prog_stage_load(struct prog *pstage)
221{
222 struct cbfs_stage stage;
223 uint8_t *load;
224 void *entry;
225 size_t fsize;
226 size_t foffset;
Aaron Durbin37a5d152015-09-17 16:09:30 -0500227 const struct region_device *fh = prog_rdev(pstage);
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800228
Aaron Durbin899d13d2015-05-15 23:39:23 -0500229 if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage))
Julius Wernerb29bd27b2015-12-03 11:29:12 -0800230 return -1;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500231
232 fsize = region_device_sz(fh);
233 fsize -= sizeof(stage);
234 foffset = 0;
235 foffset += sizeof(stage);
236
237 assert(fsize == stage.len);
238
239 /* Note: cbfs_stage fields are currently in the endianness of the
240 * running processor. */
241 load = (void *)(uintptr_t)stage.load;
242 entry = (void *)(uintptr_t)stage.entry;
243
Aaron Durbined253c82015-10-07 17:22:42 -0500244 /* Hacky way to not load programs over read only media. The stages
245 * that would hit this path initialize themselves. */
Furquan Shaikhd5583a52016-06-01 01:53:18 -0700246 if (ENV_VERSTAGE && !IS_ENABLED(CONFIG_NO_XIP_EARLY_STAGES) &&
Aaron Durbin16c173f2016-08-11 14:04:10 -0500247 IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)) {
Aaron Durbined253c82015-10-07 17:22:42 -0500248 void *mapping = rdev_mmap(fh, foffset, fsize);
249 rdev_munmap(fh, mapping);
250 if (mapping == load)
251 goto out;
252 }
253
Julius Werner09f29212015-09-29 13:51:35 -0700254 fsize = cbfs_load_and_decompress(fh, foffset, fsize, load,
255 stage.memlen, stage.compression);
256 if (!fsize)
Aaron Durbin899d13d2015-05-15 23:39:23 -0500257 return -1;
258
259 /* Clear area not covered by file. */
260 memset(&load[fsize], 0, stage.memlen - fsize);
261
Aaron Durbin096f4572016-03-31 13:49:00 -0500262 prog_segment_loaded((uintptr_t)load, stage.memlen, SEG_FINAL);
Aaron Durbined253c82015-10-07 17:22:42 -0500263
264out:
Aaron Durbin899d13d2015-05-15 23:39:23 -0500265 prog_set_area(pstage, load, stage.memlen);
266 prog_set_entry(pstage, entry, NULL);
267
268 return 0;
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800269}
Aaron Durbin6d720f32015-12-08 17:00:23 -0600270
Patrick Georgi58a150a2016-05-02 17:22:29 +0800271/* This only supports the "COREBOOT" fmap region. */
Aaron Durbin6d720f32015-12-08 17:00:23 -0600272static int cbfs_master_header_props(struct cbfs_props *props)
273{
274 struct cbfs_header header;
275 const struct region_device *bdev;
276 int32_t rel_offset;
277 size_t offset;
278
279 bdev = boot_device_ro();
280
281 if (bdev == NULL)
282 return -1;
283
Patrick Georgi58a150a2016-05-02 17:22:29 +0800284 size_t fmap_top = ___FMAP__COREBOOT_BASE + ___FMAP__COREBOOT_SIZE;
285
Aaron Durbin6d720f32015-12-08 17:00:23 -0600286 /* Find location of header using signed 32-bit offset from
287 * end of CBFS region. */
Patrick Georgi58a150a2016-05-02 17:22:29 +0800288 offset = fmap_top - sizeof(int32_t);
Aaron Durbin6d720f32015-12-08 17:00:23 -0600289 if (rdev_readat(bdev, &rel_offset, offset, sizeof(int32_t)) < 0)
290 return -1;
291
Patrick Georgi58a150a2016-05-02 17:22:29 +0800292 offset = fmap_top + rel_offset;
Aaron Durbin6d720f32015-12-08 17:00:23 -0600293 if (rdev_readat(bdev, &header, offset, sizeof(header)) < 0)
294 return -1;
295
296 header.magic = ntohl(header.magic);
297 header.romsize = ntohl(header.romsize);
298 header.offset = ntohl(header.offset);
299
300 if (header.magic != CBFS_HEADER_MAGIC)
301 return -1;
302
303 props->offset = header.offset;
304 props->size = header.romsize;
305 props->size -= props->offset;
306
307 printk(BIOS_SPEW, "CBFS @ %zx size %zx\n", props->offset, props->size);
308
309 return 0;
310}
311
312/* This struct is marked as weak to allow a particular platform to
313 * override the master header logic. This implementation should work for most
314 * devices. */
Aaron Durbin64031672018-04-21 14:45:32 -0600315const struct cbfs_locator __weak cbfs_master_header_locator = {
Aaron Durbin6d720f32015-12-08 17:00:23 -0600316 .name = "Master Header Locator",
317 .locate = cbfs_master_header_props,
318};
319
320extern const struct cbfs_locator vboot_locator;
321
322static const struct cbfs_locator *locators[] = {
Martin Roth1bf55b42017-06-24 14:16:38 -0600323#if IS_ENABLED(CONFIG_VBOOT)
Aaron Durbin6d720f32015-12-08 17:00:23 -0600324 &vboot_locator,
325#endif
326 &cbfs_master_header_locator,
327};
328
329int cbfs_boot_region_properties(struct cbfs_props *props)
330{
331 int i;
332
333 boot_device_init();
334
335 for (i = 0; i < ARRAY_SIZE(locators); i++) {
336 const struct cbfs_locator *ops;
337
338 ops = locators[i];
339
340 if (ops->locate == NULL)
341 continue;
342
343 if (ops->locate(props))
344 continue;
345
346 LOG("'%s' located CBFS at [%zx:%zx)\n",
347 ops->name, props->offset, props->offset + props->size);
348
349 return 0;
350 }
351
352 return -1;
353}
354
355void cbfs_prepare_program_locate(void)
356{
357 int i;
358
359 boot_device_init();
360
361 for (i = 0; i < ARRAY_SIZE(locators); i++) {
362 if (locators[i]->prepare == NULL)
363 continue;
364 locators[i]->prepare();
365 }
366}