blob: 11bce2c5304783e02d49855660bae30f8be63fc7 [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>
Pratik Prajapati2a7708a2016-11-30 17:29:10 -080027#include <fmap.h>
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
Pratik Prajapati2a7708a2016-11-30 17:29:10 -080075int cbfs_locate_file_in_region(struct cbfsf *fh, const char *region_name,
76 const char *name, uint32_t *type)
77{
78 struct region_device rdev;
79
80 if (fmap_locate_area_as_rdev(region_name, &rdev)) {
81 LOG("%s region not found while looking for %s\n",
82 region_name, name);
83 return -1;
84 }
85
86 return cbfs_locate(fh, &rdev, name, type);
87}
88
Julius Werner09f29212015-09-29 13:51:35 -070089size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
90 size_t in_size, void *buffer, size_t buffer_size, uint32_t compression)
Aaron Durbin899d13d2015-05-15 23:39:23 -050091{
Julius Werner09f29212015-09-29 13:51:35 -070092 size_t out_size;
93
94 switch (compression) {
95 case CBFS_COMPRESS_NONE:
Julius Wernerf975e552016-08-19 15:43:06 -070096 if (buffer_size < in_size)
97 return 0;
Julius Werner09f29212015-09-29 13:51:35 -070098 if (rdev_readat(rdev, buffer, offset, in_size) != in_size)
99 return 0;
100 return in_size;
101
102 case CBFS_COMPRESS_LZ4:
103 if ((ENV_BOOTBLOCK || ENV_VERSTAGE) &&
104 !IS_ENABLED(CONFIG_COMPRESS_PRERAM_STAGES))
105 return 0;
106
107 /* Load the compressed image to the end of the available memory
108 * area for in-place decompression. It is the responsibility of
109 * the caller to ensure that buffer_size is large enough
110 * (see compression.h, guaranteed by cbfstool for stages). */
111 void *compr_start = buffer + buffer_size - in_size;
112 if (rdev_readat(rdev, compr_start, offset, in_size) != in_size)
113 return 0;
114
115 timestamp_add_now(TS_START_ULZ4F);
116 out_size = ulz4fn(compr_start, in_size, buffer, buffer_size);
117 timestamp_add_now(TS_END_ULZ4F);
118 return out_size;
119
120 case CBFS_COMPRESS_LZMA:
121 if (ENV_BOOTBLOCK || ENV_VERSTAGE)
122 return 0;
Lee Leahyd950f512016-07-25 09:53:35 -0700123 if ((ENV_ROMSTAGE || ENV_POSTCAR)
124 && !IS_ENABLED(CONFIG_COMPRESS_RAMSTAGE))
Julius Werner09f29212015-09-29 13:51:35 -0700125 return 0;
Julius Werner09f29212015-09-29 13:51:35 -0700126 void *map = rdev_mmap(rdev, offset, in_size);
127 if (map == NULL)
128 return 0;
129
130 /* Note: timestamp not useful for memory-mapped media (x86) */
131 timestamp_add_now(TS_START_ULZMA);
132 out_size = ulzman(map, in_size, buffer, buffer_size);
133 timestamp_add_now(TS_END_ULZMA);
134
135 rdev_munmap(rdev, map);
136
137 return out_size;
138
139 default:
Aaron Durbin899d13d2015-05-15 23:39:23 -0500140 return 0;
Julius Werner09f29212015-09-29 13:51:35 -0700141 }
Aaron Durbin899d13d2015-05-15 23:39:23 -0500142}
143
Stefan Reinauer800379f2010-03-01 08:34:19 +0000144static inline int tohex4(unsigned int c)
Patrick Georgib203c2f2009-08-20 14:48:03 +0000145{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800146 return (c <= 9) ? (c + '0') : (c - 10 + 'a');
Patrick Georgib203c2f2009-08-20 14:48:03 +0000147}
148
Lee Leahyb2d834a2017-03-08 16:52:22 -0800149static void tohex16(unsigned int val, char *dest)
Patrick Georgib203c2f2009-08-20 14:48:03 +0000150{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800151 dest[0] = tohex4(val>>12);
152 dest[1] = tohex4((val>>8) & 0xf);
153 dest[2] = tohex4((val>>4) & 0xf);
154 dest[3] = tohex4(val & 0xf);
Patrick Georgib203c2f2009-08-20 14:48:03 +0000155}
156
Aaron Durbin899d13d2015-05-15 23:39:23 -0500157void *cbfs_boot_map_optionrom(uint16_t vendor, uint16_t device)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000158{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800159 char name[17] = "pciXXXX,XXXX.rom";
Peter Stuge483b7bb2009-04-14 07:40:01 +0000160
Patrick Georgib203c2f2009-08-20 14:48:03 +0000161 tohex16(vendor, name+3);
162 tohex16(device, name+8);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000163
Aaron Durbin899d13d2015-05-15 23:39:23 -0500164 return cbfs_boot_map_with_leak(name, CBFS_TYPE_OPTIONROM, NULL);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000165}
166
Aaron Durbin899d13d2015-05-15 23:39:23 -0500167void *cbfs_boot_load_stage_by_name(const char *name)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000168{
Aaron Durbin37a5d152015-09-17 16:09:30 -0500169 struct cbfsf fh;
Aaron Durbin7e7a4df2015-12-08 14:34:35 -0600170 struct prog stage = PROG_INIT(PROG_UNKNOWN, name);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500171 uint32_t type = CBFS_TYPE_STAGE;
Aaron Durbin3948e532015-03-20 13:00:20 -0500172
Aaron Durbin37a5d152015-09-17 16:09:30 -0500173 if (cbfs_boot_locate(&fh, name, &type))
Aaron Durbin899d13d2015-05-15 23:39:23 -0500174 return NULL;
Aaron Durbin3948e532015-03-20 13:00:20 -0500175
Aaron Durbin37a5d152015-09-17 16:09:30 -0500176 /* Chain data portion in the prog. */
177 cbfs_file_data(prog_rdev(&stage), &fh);
178
Aaron Durbin899d13d2015-05-15 23:39:23 -0500179 if (cbfs_prog_stage_load(&stage))
180 return NULL;
181
182 return prog_entry(&stage);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000183}
184
Julius Wernerf975e552016-08-19 15:43:06 -0700185size_t cbfs_boot_load_struct(const char *name, void *buf, size_t buf_size)
186{
187 struct cbfsf fh;
188 uint32_t compression_algo;
189 size_t decompressed_size;
190 uint32_t type = CBFS_TYPE_STRUCT;
191
192 if (cbfs_boot_locate(&fh, name, &type) < 0)
193 return 0;
194
195 if (cbfsf_decompression_info(&fh, &compression_algo,
196 &decompressed_size) < 0
197 || decompressed_size > buf_size)
198 return 0;
199
200 return cbfs_load_and_decompress(&fh.data, 0, region_device_sz(&fh.data),
201 buf, buf_size, compression_algo);
202}
203
Kyösti Mälkki9d6f3652016-06-28 07:38:46 +0300204size_t cbfs_prog_stage_section(struct prog *pstage, uintptr_t *base)
205{
206 struct cbfs_stage stage;
207 const struct region_device *fh = prog_rdev(pstage);
208
209 if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage))
210 return 0;
211
212 *base = (uintptr_t)stage.load;
213 return stage.memlen;
214}
215
Aaron Durbin899d13d2015-05-15 23:39:23 -0500216int cbfs_prog_stage_load(struct prog *pstage)
217{
218 struct cbfs_stage stage;
219 uint8_t *load;
220 void *entry;
221 size_t fsize;
222 size_t foffset;
Aaron Durbin37a5d152015-09-17 16:09:30 -0500223 const struct region_device *fh = prog_rdev(pstage);
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800224
Aaron Durbin899d13d2015-05-15 23:39:23 -0500225 if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage))
Julius Wernerb29bd27b2015-12-03 11:29:12 -0800226 return -1;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500227
228 fsize = region_device_sz(fh);
229 fsize -= sizeof(stage);
230 foffset = 0;
231 foffset += sizeof(stage);
232
233 assert(fsize == stage.len);
234
235 /* Note: cbfs_stage fields are currently in the endianness of the
236 * running processor. */
237 load = (void *)(uintptr_t)stage.load;
238 entry = (void *)(uintptr_t)stage.entry;
239
Aaron Durbined253c82015-10-07 17:22:42 -0500240 /* Hacky way to not load programs over read only media. The stages
241 * that would hit this path initialize themselves. */
Furquan Shaikhd5583a52016-06-01 01:53:18 -0700242 if (ENV_VERSTAGE && !IS_ENABLED(CONFIG_NO_XIP_EARLY_STAGES) &&
Aaron Durbin16c173f2016-08-11 14:04:10 -0500243 IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)) {
Aaron Durbined253c82015-10-07 17:22:42 -0500244 void *mapping = rdev_mmap(fh, foffset, fsize);
245 rdev_munmap(fh, mapping);
246 if (mapping == load)
247 goto out;
248 }
249
Julius Werner09f29212015-09-29 13:51:35 -0700250 fsize = cbfs_load_and_decompress(fh, foffset, fsize, load,
251 stage.memlen, stage.compression);
252 if (!fsize)
Aaron Durbin899d13d2015-05-15 23:39:23 -0500253 return -1;
254
255 /* Clear area not covered by file. */
256 memset(&load[fsize], 0, stage.memlen - fsize);
257
Aaron Durbin096f4572016-03-31 13:49:00 -0500258 prog_segment_loaded((uintptr_t)load, stage.memlen, SEG_FINAL);
Aaron Durbined253c82015-10-07 17:22:42 -0500259
260out:
Aaron Durbin899d13d2015-05-15 23:39:23 -0500261 prog_set_area(pstage, load, stage.memlen);
262 prog_set_entry(pstage, entry, NULL);
263
264 return 0;
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800265}
Aaron Durbin6d720f32015-12-08 17:00:23 -0600266
Patrick Georgi58a150a2016-05-02 17:22:29 +0800267/* This only supports the "COREBOOT" fmap region. */
Aaron Durbin6d720f32015-12-08 17:00:23 -0600268static int cbfs_master_header_props(struct cbfs_props *props)
269{
270 struct cbfs_header header;
271 const struct region_device *bdev;
272 int32_t rel_offset;
273 size_t offset;
274
275 bdev = boot_device_ro();
276
277 if (bdev == NULL)
278 return -1;
279
Patrick Georgi58a150a2016-05-02 17:22:29 +0800280 size_t fmap_top = ___FMAP__COREBOOT_BASE + ___FMAP__COREBOOT_SIZE;
281
Aaron Durbin6d720f32015-12-08 17:00:23 -0600282 /* Find location of header using signed 32-bit offset from
283 * end of CBFS region. */
Patrick Georgi58a150a2016-05-02 17:22:29 +0800284 offset = fmap_top - sizeof(int32_t);
Aaron Durbin6d720f32015-12-08 17:00:23 -0600285 if (rdev_readat(bdev, &rel_offset, offset, sizeof(int32_t)) < 0)
286 return -1;
287
Patrick Georgi58a150a2016-05-02 17:22:29 +0800288 offset = fmap_top + rel_offset;
Aaron Durbin6d720f32015-12-08 17:00:23 -0600289 if (rdev_readat(bdev, &header, offset, sizeof(header)) < 0)
290 return -1;
291
292 header.magic = ntohl(header.magic);
293 header.romsize = ntohl(header.romsize);
294 header.offset = ntohl(header.offset);
295
296 if (header.magic != CBFS_HEADER_MAGIC)
297 return -1;
298
299 props->offset = header.offset;
300 props->size = header.romsize;
301 props->size -= props->offset;
302
303 printk(BIOS_SPEW, "CBFS @ %zx size %zx\n", props->offset, props->size);
304
305 return 0;
306}
307
308/* This struct is marked as weak to allow a particular platform to
309 * override the master header logic. This implementation should work for most
310 * devices. */
311const struct cbfs_locator __attribute__((weak)) cbfs_master_header_locator = {
312 .name = "Master Header Locator",
313 .locate = cbfs_master_header_props,
314};
315
316extern const struct cbfs_locator vboot_locator;
317
318static const struct cbfs_locator *locators[] = {
Furquan Shaikh2a12e2e2016-07-25 11:48:03 -0700319#if CONFIG_VBOOT
Aaron Durbin6d720f32015-12-08 17:00:23 -0600320 &vboot_locator,
321#endif
322 &cbfs_master_header_locator,
323};
324
325int cbfs_boot_region_properties(struct cbfs_props *props)
326{
327 int i;
328
329 boot_device_init();
330
331 for (i = 0; i < ARRAY_SIZE(locators); i++) {
332 const struct cbfs_locator *ops;
333
334 ops = locators[i];
335
336 if (ops->locate == NULL)
337 continue;
338
339 if (ops->locate(props))
340 continue;
341
342 LOG("'%s' located CBFS at [%zx:%zx)\n",
343 ops->name, props->offset, props->offset + props->size);
344
345 return 0;
346 }
347
348 return -1;
349}
350
351void cbfs_prepare_program_locate(void)
352{
353 int i;
354
355 boot_device_init();
356
357 for (i = 0; i < ARRAY_SIZE(locators); i++) {
358 if (locators[i]->prepare == NULL)
359 continue;
360 locators[i]->prepare();
361 }
362}