blob: d68aa39cc543fcab9a031026f8a03c9f2204e48f [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>
22#include <endian.h>
23#include <lib.h>
24#include <symbols.h>
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +080025
Aaron Durbin899d13d2015-05-15 23:39:23 -050026#define ERROR(x...) printk(BIOS_ERR, "CBFS: " x)
27#define LOG(x...) printk(BIOS_INFO, "CBFS: " x)
28#if IS_ENABLED(CONFIG_DEBUG_CBFS)
29#define DEBUG(x...) printk(BIOS_SPEW, "CBFS: " x)
30#else
31#define DEBUG(x...)
32#endif
Peter Stuge483b7bb2009-04-14 07:40:01 +000033
Aaron Durbin37a5d152015-09-17 16:09:30 -050034int cbfs_boot_locate(struct cbfsf *fh, const char *name, uint32_t *type)
Aaron Durbin899d13d2015-05-15 23:39:23 -050035{
Aaron Durbin899d13d2015-05-15 23:39:23 -050036 struct region_device rdev;
37 const struct region_device *boot_dev;
38 struct cbfs_props props;
39
Aaron Durbin899d13d2015-05-15 23:39:23 -050040 if (cbfs_boot_region_properties(&props))
41 return -1;
42
43 /* All boot CBFS operations are performed using the RO devie. */
44 boot_dev = boot_device_ro();
45
46 if (boot_dev == NULL)
47 return -1;
48
49 if (rdev_chain(&rdev, boot_dev, props.offset, props.size))
50 return -1;
51
Aaron Durbin37a5d152015-09-17 16:09:30 -050052 return cbfs_locate(fh, &rdev, name, type);
Aaron Durbin899d13d2015-05-15 23:39:23 -050053}
54
55void *cbfs_boot_map_with_leak(const char *name, uint32_t type, size_t *size)
56{
Aaron Durbin37a5d152015-09-17 16:09:30 -050057 struct cbfsf fh;
Aaron Durbin899d13d2015-05-15 23:39:23 -050058 size_t fsize;
59
60 if (cbfs_boot_locate(&fh, name, &type))
61 return NULL;
62
Aaron Durbin37a5d152015-09-17 16:09:30 -050063 fsize = region_device_sz(&fh.data);
Aaron Durbin899d13d2015-05-15 23:39:23 -050064
65 if (size != NULL)
66 *size = fsize;
67
Aaron Durbin37a5d152015-09-17 16:09:30 -050068 return rdev_mmap(&fh.data, 0, fsize);
Aaron Durbin899d13d2015-05-15 23:39:23 -050069}
70
Aaron Durbin37a5d152015-09-17 16:09:30 -050071int cbfs_locate(struct cbfsf *fh, const struct region_device *cbfs,
Aaron Durbin899d13d2015-05-15 23:39:23 -050072 const char *name, uint32_t *type)
73{
Aaron Durbin37a5d152015-09-17 16:09:30 -050074 size_t offset = 0;
Aaron Durbin899d13d2015-05-15 23:39:23 -050075
76 LOG("Locating '%s'\n", name);
77
78 /* Try to scan the entire cbfs region looking for file name. */
79 while (1) {
80 struct cbfs_file file;
81 const size_t fsz = sizeof(file);
82 char *fname;
83 int name_match;
84 size_t datasz;
85
86 DEBUG("Checking offset %zx\n", offset);
87
88 /* Can't read file. Nothing else to do but bail out. */
Aaron Durbin37a5d152015-09-17 16:09:30 -050089 if (rdev_readat(cbfs, &file, offset, fsz) != fsz)
Aaron Durbin899d13d2015-05-15 23:39:23 -050090 break;
91
92 if (memcmp(file.magic, CBFS_FILE_MAGIC, sizeof(file.magic))) {
93 offset++;
Patrick Georgi4d3e4c42015-07-14 22:28:27 +020094 offset = ALIGN_UP(offset, CBFS_ALIGNMENT);
Aaron Durbin899d13d2015-05-15 23:39:23 -050095 continue;
96 }
97
98 file.len = ntohl(file.len);
99 file.type = ntohl(file.type);
100 file.offset = ntohl(file.offset);
101
102 /* See if names match. */
Aaron Durbin37a5d152015-09-17 16:09:30 -0500103 fname = rdev_mmap(cbfs, offset + fsz, file.offset - fsz);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500104
105 if (fname == NULL)
106 break;
107
108 name_match = !strcmp(fname, name);
Aaron Durbin37a5d152015-09-17 16:09:30 -0500109 rdev_munmap(cbfs, fname);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500110
111 if (!name_match) {
112 DEBUG(" Unmatched '%s' at %zx\n", fname, offset);
113 offset += file.offset + file.len;
Patrick Georgi4d3e4c42015-07-14 22:28:27 +0200114 offset = ALIGN_UP(offset, CBFS_ALIGNMENT);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500115 continue;
116 }
117
118 if (type != NULL && *type != file.type) {
119 DEBUG(" Unmatched type %x at %zx\n", file.type, offset);
120 offset += file.offset + file.len;
Patrick Georgi4d3e4c42015-07-14 22:28:27 +0200121 offset = ALIGN_UP(offset, CBFS_ALIGNMENT);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500122 continue;
123 }
124
125 LOG("Found @ offset %zx size %x\n", offset, file.len);
Aaron Durbin37a5d152015-09-17 16:09:30 -0500126 /* File and type match. Keep track of both the metadata and
127 * the data for the file. */
128 if (rdev_chain(&fh->metadata, cbfs, offset, file.offset))
129 break;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500130 offset += file.offset;
131 datasz = file.len;
Aaron Durbin37a5d152015-09-17 16:09:30 -0500132 if (rdev_chain(&fh->data, cbfs, offset, datasz))
Aaron Durbin899d13d2015-05-15 23:39:23 -0500133 break;
134
135 /* Success. */
136 return 0;
137 }
138
139 LOG("'%s' not found.\n", name);
140 return -1;
141}
142
143static size_t inflate(void *src, void *dst)
144{
145 if (ENV_BOOTBLOCK || ENV_VERSTAGE)
146 return 0;
147 if (ENV_ROMSTAGE && !IS_ENABLED(CONFIG_COMPRESS_RAMSTAGE))
148 return 0;
149 return ulzma(src, dst);
150}
151
Stefan Reinauer800379f2010-03-01 08:34:19 +0000152static inline int tohex4(unsigned int c)
Patrick Georgib203c2f2009-08-20 14:48:03 +0000153{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800154 return (c <= 9) ? (c + '0') : (c - 10 + 'a');
Patrick Georgib203c2f2009-08-20 14:48:03 +0000155}
156
157static void tohex16(unsigned int val, char* dest)
158{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800159 dest[0] = tohex4(val>>12);
160 dest[1] = tohex4((val>>8) & 0xf);
161 dest[2] = tohex4((val>>4) & 0xf);
162 dest[3] = tohex4(val & 0xf);
Patrick Georgib203c2f2009-08-20 14:48:03 +0000163}
164
Aaron Durbin899d13d2015-05-15 23:39:23 -0500165void *cbfs_boot_map_optionrom(uint16_t vendor, uint16_t device)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000166{
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800167 char name[17] = "pciXXXX,XXXX.rom";
Peter Stuge483b7bb2009-04-14 07:40:01 +0000168
Patrick Georgib203c2f2009-08-20 14:48:03 +0000169 tohex16(vendor, name+3);
170 tohex16(device, name+8);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000171
Aaron Durbin899d13d2015-05-15 23:39:23 -0500172 return cbfs_boot_map_with_leak(name, CBFS_TYPE_OPTIONROM, NULL);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000173}
174
Aaron Durbin899d13d2015-05-15 23:39:23 -0500175void *cbfs_boot_load_stage_by_name(const char *name)
Peter Stuge483b7bb2009-04-14 07:40:01 +0000176{
Aaron Durbin37a5d152015-09-17 16:09:30 -0500177 struct cbfsf fh;
Aaron Durbin7e7a4df2015-12-08 14:34:35 -0600178 struct prog stage = PROG_INIT(PROG_UNKNOWN, name);
Aaron Durbin899d13d2015-05-15 23:39:23 -0500179 uint32_t type = CBFS_TYPE_STAGE;
Aaron Durbin3948e532015-03-20 13:00:20 -0500180
Aaron Durbin37a5d152015-09-17 16:09:30 -0500181 if (cbfs_boot_locate(&fh, name, &type))
Aaron Durbin899d13d2015-05-15 23:39:23 -0500182 return NULL;
Aaron Durbin3948e532015-03-20 13:00:20 -0500183
Aaron Durbin37a5d152015-09-17 16:09:30 -0500184 /* Chain data portion in the prog. */
185 cbfs_file_data(prog_rdev(&stage), &fh);
186
Aaron Durbin899d13d2015-05-15 23:39:23 -0500187 if (cbfs_prog_stage_load(&stage))
188 return NULL;
189
190 return prog_entry(&stage);
Peter Stuge483b7bb2009-04-14 07:40:01 +0000191}
192
Aaron Durbin899d13d2015-05-15 23:39:23 -0500193int cbfs_prog_stage_load(struct prog *pstage)
194{
195 struct cbfs_stage stage;
196 uint8_t *load;
197 void *entry;
198 size_t fsize;
199 size_t foffset;
Aaron Durbin37a5d152015-09-17 16:09:30 -0500200 const struct region_device *fh = prog_rdev(pstage);
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800201
Aaron Durbin899d13d2015-05-15 23:39:23 -0500202 if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage))
Julius Wernerb29bd27b2015-12-03 11:29:12 -0800203 return -1;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500204
205 fsize = region_device_sz(fh);
206 fsize -= sizeof(stage);
207 foffset = 0;
208 foffset += sizeof(stage);
209
210 assert(fsize == stage.len);
211
212 /* Note: cbfs_stage fields are currently in the endianness of the
213 * running processor. */
214 load = (void *)(uintptr_t)stage.load;
215 entry = (void *)(uintptr_t)stage.entry;
216
Aaron Durbined253c82015-10-07 17:22:42 -0500217 /* Hacky way to not load programs over read only media. The stages
218 * that would hit this path initialize themselves. */
219 if (ENV_VERSTAGE && IS_ENABLED(CONFIG_ARCH_X86) &&
220 IS_ENABLED(CONFIG_SPI_FLASH_MEMORY_MAPPED)) {
221 void *mapping = rdev_mmap(fh, foffset, fsize);
222 rdev_munmap(fh, mapping);
223 if (mapping == load)
224 goto out;
225 }
226
Aaron Durbin899d13d2015-05-15 23:39:23 -0500227 if (stage.compression == CBFS_COMPRESS_NONE) {
228 if (rdev_readat(fh, load, foffset, fsize) != fsize)
229 return -1;
230 } else if (stage.compression == CBFS_COMPRESS_LZMA) {
231 void *map = rdev_mmap(fh, foffset, fsize);
232
233 if (map == NULL)
234 return -1;
235
236 fsize = inflate(map, load);
237
238 rdev_munmap(fh, map);
239
240 if (!fsize)
241 return -1;
242 } else
243 return -1;
244
245 /* Clear area not covered by file. */
246 memset(&load[fsize], 0, stage.memlen - fsize);
247
248 arch_segment_loaded((uintptr_t)load, stage.memlen, SEG_FINAL);
Aaron Durbined253c82015-10-07 17:22:42 -0500249
250out:
Aaron Durbin899d13d2015-05-15 23:39:23 -0500251 prog_set_area(pstage, load, stage.memlen);
252 prog_set_entry(pstage, entry, NULL);
253
254 return 0;
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800255}
Aaron Durbin6d720f32015-12-08 17:00:23 -0600256
257static int cbfs_master_header_props(struct cbfs_props *props)
258{
259 struct cbfs_header header;
260 const struct region_device *bdev;
261 int32_t rel_offset;
262 size_t offset;
263
264 bdev = boot_device_ro();
265
266 if (bdev == NULL)
267 return -1;
268
269 /* Find location of header using signed 32-bit offset from
270 * end of CBFS region. */
271 offset = CONFIG_CBFS_SIZE - sizeof(int32_t);
272 if (rdev_readat(bdev, &rel_offset, offset, sizeof(int32_t)) < 0)
273 return -1;
274
275 offset = CONFIG_CBFS_SIZE + rel_offset;
276 if (rdev_readat(bdev, &header, offset, sizeof(header)) < 0)
277 return -1;
278
279 header.magic = ntohl(header.magic);
280 header.romsize = ntohl(header.romsize);
281 header.offset = ntohl(header.offset);
282
283 if (header.magic != CBFS_HEADER_MAGIC)
284 return -1;
285
286 props->offset = header.offset;
287 props->size = header.romsize;
288 props->size -= props->offset;
289
290 printk(BIOS_SPEW, "CBFS @ %zx size %zx\n", props->offset, props->size);
291
292 return 0;
293}
294
295/* This struct is marked as weak to allow a particular platform to
296 * override the master header logic. This implementation should work for most
297 * devices. */
298const struct cbfs_locator __attribute__((weak)) cbfs_master_header_locator = {
299 .name = "Master Header Locator",
300 .locate = cbfs_master_header_props,
301};
302
303extern const struct cbfs_locator vboot_locator;
304
305static const struct cbfs_locator *locators[] = {
306#if CONFIG_VBOOT_VERIFY_FIRMWARE
307 &vboot_locator,
308#endif
309 &cbfs_master_header_locator,
310};
311
312int cbfs_boot_region_properties(struct cbfs_props *props)
313{
314 int i;
315
316 boot_device_init();
317
318 for (i = 0; i < ARRAY_SIZE(locators); i++) {
319 const struct cbfs_locator *ops;
320
321 ops = locators[i];
322
323 if (ops->locate == NULL)
324 continue;
325
326 if (ops->locate(props))
327 continue;
328
329 LOG("'%s' located CBFS at [%zx:%zx)\n",
330 ops->name, props->offset, props->offset + props->size);
331
332 return 0;
333 }
334
335 return -1;
336}
337
338void cbfs_prepare_program_locate(void)
339{
340 int i;
341
342 boot_device_init();
343
344 for (i = 0; i < ARRAY_SIZE(locators); i++) {
345 if (locators[i]->prepare == NULL)
346 continue;
347 locators[i]->prepare();
348 }
349}