blob: d57a18c4e11d65cedcd51ad3fd53a27ec7612566 [file] [log] [blame]
Aaron Durbina5be7fa2015-09-10 22:52:27 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc.
18 */
19
20#include <console/console.h>
21#include <endian.h>
22#include <fsp/api.h>
23#include <fsp/util.h>
24#include <stdlib.h>
25#include <stdint.h>
26#include <string.h>
27
28#define FSP_DBG_LVL BIOS_NEVER
29
30/*
31 * UEFI defines everything as little endian. However, this piece of code
32 * can be integrated in a userland tool. That tool could be on a big endian
33 * machine so one needs to access the fields within UEFI structures using
34 * endian-aware accesses.
35 */
36
37/* Return 0 if equal. Non-zero if not equal. */
38static int guid_compare(const EFI_GUID *le_guid, const EFI_GUID *native_guid)
39{
40 if (le32toh(le_guid->Data1) != native_guid->Data1)
41 return 1;
42 if (le16toh(le_guid->Data2) != native_guid->Data2)
43 return 1;
44 if (le16toh(le_guid->Data3) != native_guid->Data3)
45 return 1;
46 return memcmp(le_guid->Data4, native_guid->Data4,
47 ARRAY_SIZE(le_guid->Data4));
48}
49
50/* Provide this for symmetry when accessing UEFI fields. */
51static inline uint8_t le8toh(uint8_t byte)
52{
53 return byte;
54}
55
56static const EFI_GUID ffs2_guid = EFI_FIRMWARE_FILE_SYSTEM2_GUID;
57static const EFI_GUID fih_guid = FSP_INFO_HEADER_GUID;
58
59struct fsp_patch_table {
60 uint32_t signature;
61 uint16_t header_length;
62 uint8_t header_revision;
63 uint8_t reserved;
64 uint32_t patch_entry_num;
65 uint32_t patch_entries[0];
66} __attribute__((packed));
67
68#define FSPP_SIG 0x50505346
69
70static void *relative_offset(void *base, ssize_t offset)
71{
72 uintptr_t loc;
73
74 loc = (uintptr_t)base;
75 loc += offset;
76
77 return (void *)loc;
78}
79
80static uint32_t *fspp_reloc(void *fsp, size_t fsp_size, uint32_t e)
81{
82 size_t offset;
83
84 /* Offsets live in bits 23:0. */
85 offset = e & 0xffffff;
86
87 /* If bit 31 is set then the offset is considered a negative value
88 * relative to the end of the image using 16MiB as the offset's
89 * reference. */
90 if (e & (1 << 31))
91 offset = fsp_size - (16 * MiB - offset);
92
93 /* Determine if offset falls within fsp_size for a 32 bit relocation. */
94 if (offset > fsp_size - sizeof(uint32_t))
95 return NULL;
96
97 return relative_offset(fsp, offset);
98}
99
100static int reloc_type(uint16_t reloc_entry)
101{
102 /* Reloc type in upper 4 bits */
103 return reloc_entry >> 12;
104}
105
106static size_t reloc_offset(uint16_t reloc_entry)
107{
108 /* Offsets are in low 12 bits. */
109 return reloc_entry & ((1 << 12) - 1);
110}
111
112static int te_relocate(uintptr_t new_addr, void *te, size_t size)
113{
114 EFI_TE_IMAGE_HEADER *teih;
115 EFI_IMAGE_DATA_DIRECTORY *relocd;
116 EFI_IMAGE_BASE_RELOCATION *relocb;
117 uintptr_t image_base;
118 size_t fixup_offset;
119 size_t num_relocs;
120 uint16_t *reloc;
121 size_t relocd_offset;
122 uint8_t *te_base;
123 uint32_t adj;
124
125 teih = te;
126
127 if (le16toh(teih->Signature) != EFI_TE_IMAGE_HEADER_SIGNATURE) {
128 printk(BIOS_ERR, "TE Signature mismatch: %x vs %x\n",
129 le16toh(teih->Signature),
130 EFI_TE_IMAGE_HEADER_SIGNATURE);
131 return -1;
132 }
133
134 /*
135 * A TE image is created by converting a PE file. Because of this
136 * the offsets within the headers are off. In order to calculate
137 * the correct releative offets one needs to subtract fixup_offset
138 * from the encoded offets. Similarly, the linked address of the
139 * program is found by adding the fixup_offset to the ImageBase.
140 */
141 fixup_offset = le16toh(teih->StrippedSize);
142 fixup_offset -= sizeof(EFI_TE_IMAGE_HEADER);
143 /* Keep track of a base that is correctly adjusted so that offsets
144 * can be used directly. */
145 te_base = te;
146 te_base -= fixup_offset;
147
148 image_base = le64toh(teih->ImageBase);
149 adj = new_addr - (image_base + fixup_offset);
150
151 printk(FSP_DBG_LVL, "TE Image %p -> %p adjust value: %x\n",
152 (void *)image_base, (void *)new_addr, adj);
153
154 /* Adjust ImageBase for consistency. */
155 teih->ImageBase = htole32(image_base + adj);
156
157 relocd = &teih->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC];
158
159 relocd_offset = 0;
160 /* Though the field name is VirtualAddress it's actually relative to
161 * the beginning of the image which is linked at ImageBase. */
162 relocb = relative_offset(te,
163 le32toh(relocd->VirtualAddress) - fixup_offset);
164 while (relocd_offset < relocd->Size) {
165 size_t rva_offset = le32toh(relocb->VirtualAddress);
166
167 printk(FSP_DBG_LVL, "Relocs for RVA offset %zx\n", rva_offset);
168 num_relocs = le32toh(relocb->SizeOfBlock) - sizeof(*relocb);
169 num_relocs /= sizeof(uint16_t);
170 reloc = relative_offset(relocb, sizeof(*relocb));
171
172 printk(FSP_DBG_LVL, "Num relocs in block: %zx\n", num_relocs);
173
174 while (num_relocs > 0) {
175 uint16_t reloc_val = le16toh(*reloc);
176 int type = reloc_type(reloc_val);
177 size_t offset = reloc_offset(reloc_val);
178
179 printk(FSP_DBG_LVL, "reloc type %x offset %zx\n",
180 type, offset);
181
182 if (type == EFI_IMAGE_REL_BASED_HIGHLOW) {
183 uint32_t *reloc_addr;
184 uint32_t val;
185
186 offset += rva_offset;
187 reloc_addr = (void *)&te_base[offset];
188 val = le32toh(*reloc_addr);
189
190 printk(FSP_DBG_LVL, "Adjusting %p %x -> %x\n",
191 reloc_addr, val, val + adj);
192 *reloc_addr = htole32(val + adj);
193 } else if (type != EFI_IMAGE_REL_BASED_ABSOLUTE) {
194 printk(BIOS_ERR, "Unknown reloc type: %x\n",
195 type);
196 return -1;
197 }
198 num_relocs--;
199 reloc++;
200 }
201
202 /* Track consumption of relocation directory contents. */
203 relocd_offset += le32toh(relocb->SizeOfBlock);
204 /* Get next relocation block to process. */
205 relocb = relative_offset(relocb, le32toh(relocb->SizeOfBlock));
206 }
207
208 return 0;
209}
210
211static size_t csh_size(const EFI_COMMON_SECTION_HEADER *csh)
212{
213 size_t size;
214
215 /* Unpack the array into a type that can be used. */
216 size = 0;
217 size |= le8toh(csh->Size[0]) << 0;
218 size |= le8toh(csh->Size[1]) << 8;
219 size |= le8toh(csh->Size[2]) << 16;
220
221 return size;
222}
223
224static size_t section_data_offset(const EFI_COMMON_SECTION_HEADER *csh)
225{
226 if (csh_size(csh) == 0x00ffffff)
227 return sizeof(EFI_COMMON_SECTION_HEADER2);
228 else
229 return sizeof(EFI_COMMON_SECTION_HEADER);
230}
231
232static size_t section_data_size(const EFI_COMMON_SECTION_HEADER *csh)
233{
234 size_t section_size;
235
236 if (csh_size(csh) == 0x00ffffff)
237 section_size = le32toh(SECTION2_SIZE(csh));
238 else
239 section_size = csh_size(csh);
240
241 return section_size - section_data_offset(csh);
242}
243
244static size_t file_section_offset(const EFI_FFS_FILE_HEADER *ffsfh)
245{
246 if (IS_FFS_FILE2(ffsfh))
247 return sizeof(EFI_FFS_FILE_HEADER2);
248 else
249 return sizeof(EFI_FFS_FILE_HEADER);
250}
251
252static size_t ffs_file_size(const EFI_FFS_FILE_HEADER *ffsfh)
253{
254 size_t size;
255
256 if (IS_FFS_FILE2(ffsfh))
257 size = le32toh(FFS_FILE2_SIZE(ffsfh));
258 else {
259 size = le8toh(ffsfh->Size[0]) << 0;
260 size |= le8toh(ffsfh->Size[1]) << 8;
261 size |= le8toh(ffsfh->Size[2]) << 16;
262 }
263 return size;
264}
265
266static int relocate_patch_table(void *fsp, size_t size, size_t offset,
267 ssize_t adjustment)
268{
269 struct fsp_patch_table *table;
270 size_t num;
271 size_t num_entries;
272
273 table = relative_offset(fsp, offset);
274
275 if ((offset + sizeof(*table) > size) ||
276 (le16toh(table->header_length) + offset) > size) {
277 printk(BIOS_ERR, "FSPP not entirely contained in region.\n");
278 return -1;
279 }
280
281 num_entries = le32toh(table->patch_entry_num);
282 printk(FSP_DBG_LVL, "FSPP relocs: %zx\n", num_entries);
283
284 for (num = 0; num < table->patch_entry_num; num++) {
285 uint32_t *reloc;
286 uint32_t reloc_val;
287
288 reloc = fspp_reloc(fsp, size,
289 le32toh(table->patch_entries[num]));
290
291 if (reloc == NULL) {
292 printk(BIOS_ERR, "Ignoring FSPP entry: %x\n",
293 le32toh(table->patch_entries[num]));
294 continue;
295 }
296
297 reloc_val = le32toh(*reloc);
298 printk(FSP_DBG_LVL, "Adjusting %p %x -> %x\n",
299 reloc, reloc_val,
300 (unsigned int)(reloc_val + adjustment));
301
302 *reloc = htole32(reloc_val + adjustment);
303 }
304
305 return 0;
306}
307
308static ssize_t relocate_remaining_items(void *fsp, size_t size,
309 uintptr_t new_addr, size_t fih_offset)
310{
311 EFI_FFS_FILE_HEADER *ffsfh;
312 EFI_COMMON_SECTION_HEADER *csh;
313 FSP_INFO_HEADER *fih;
314 ssize_t adjustment;
315 size_t offset;
316
317 printk(FSP_DBG_LVL, "FSP_INFO_HEADER offset is %zx\n", fih_offset);
318
319 if (fih_offset == 0) {
320 printk(BIOS_ERR, "FSP_INFO_HEADER offset is 0.\n");
321 return -1;
322 }
323
324 /* FSP_INFO_HEADER at first file in FV within first RAW section. */
325 ffsfh = relative_offset(fsp, fih_offset);
326 fih_offset += file_section_offset(ffsfh);
327 csh = relative_offset(fsp, fih_offset);
328 fih_offset += section_data_offset(csh);
329 fih = relative_offset(fsp, fih_offset);
330
331 if (guid_compare(&ffsfh->Name, &fih_guid)) {
332 printk(BIOS_ERR, "Bad FIH GUID.\n");
333 return -1;
334 }
335
336 if (le8toh(csh->Type) != EFI_SECTION_RAW) {
337 printk(BIOS_ERR, "FIH file should have raw section: %x\n",
338 csh->Type);
339 return -1;
340 }
341
342 if (le32toh(fih->Signature) != FSP_SIG) {
343 printk(BIOS_ERR, "Unexpected FIH signature: %08x\n",
344 le32toh(fih->Signature));
345 return -1;
346 }
347
348 adjustment = (intptr_t)new_addr - le32toh(fih->ImageBase);
349
350 /* Update ImageBase to reflect FSP's new home. */
351 fih->ImageBase = htole32(adjustment + le32toh(fih->ImageBase));
352
353 /* Need to find patch table and adjust each entry. The tables
354 * following FSP_INFO_HEADER have a 32-bit signature and header
355 * length. The patch table is denoted as having a 'FSPP' signature;
356 * the table format doesn't follow the other tables. */
357 offset = fih_offset + le32toh(fih->HeaderLength);
358 while (offset + 2 * sizeof(uint32_t) <= size) {
359 uint32_t *table_headers;
360
361 table_headers = relative_offset(fsp, offset);
362
363 printk(FSP_DBG_LVL, "Checking offset %zx for 'FSPP'\n",
364 offset);
365
366 if (le32toh(table_headers[0]) != FSPP_SIG) {
367 offset += le32toh(table_headers[1]);
368 continue;
369 }
370
371 if (relocate_patch_table(fsp, size, offset, adjustment)) {
372 printk(BIOS_ERR, "FSPP relocation failed.\n");
373 return -1;
374 }
375
376 return fih_offset;
377 }
378
379 printk(BIOS_ERR, "Could not find the FSP patch table.\n");
380 return -1;
381}
382
383static ssize_t relocate_fvh(uintptr_t new_addr, void *fsp, size_t fsp_size,
384 size_t fvh_offset, size_t *fih_offset)
385{
386 EFI_FIRMWARE_VOLUME_HEADER *fvh;
387 EFI_FFS_FILE_HEADER *ffsfh;
388 EFI_COMMON_SECTION_HEADER *csh;
389 size_t offset;
390 size_t file_offset;
391 size_t size;
392 size_t fv_length;
393
394 offset = fvh_offset;
395 fvh = relative_offset(fsp, offset);
396
397 if (le32toh(fvh->Signature) != EFI_FVH_SIGNATURE)
398 return -1;
399
400 fv_length = le64toh(fvh->FvLength);
401
402 printk(FSP_DBG_LVL, "FVH length: %zx Offset: %zx Mapping length: %zx\n",
403 fv_length, offset, fsp_size);
404
405 if (fvh->FvLength + offset > fsp_size)
406 return -1;
407
408 /* Parse only this FV. However, the algorithm uses offsets into the
409 * entire FSP region so make size include the starting offset. */
410 size = fv_length + offset;
411
412 if (guid_compare(&fvh->FileSystemGuid, &ffs2_guid)) {
413 printk(BIOS_ERR, "FVH not an FFS2 type.\n");
414 return -1;
415 }
416
417 if (le16toh(fvh->ExtHeaderOffset) != 0) {
418 EFI_FIRMWARE_VOLUME_EXT_HEADER *fveh;
419
420 offset += le16toh(fvh->ExtHeaderOffset);
421 fveh = relative_offset(fsp, offset);
422 printk(FSP_DBG_LVL, "Extended Header Offset: %zx Size: %zx\n",
423 (size_t)le16toh(fvh->ExtHeaderOffset),
424 (size_t)le32toh(fveh->ExtHeaderSize));
425 offset += le32toh(fveh->ExtHeaderSize);
426 /* FFS files are 8 byte aligned after extended header. */
427 offset = ALIGN_UP(offset, 8);
428 } else {
429 offset += le16toh(fvh->HeaderLength);
430 }
431
432 file_offset = offset;
433 while (file_offset + sizeof(*ffsfh) < size) {
434 offset = file_offset;
435 printk(FSP_DBG_LVL, "file offset: %zx\n", file_offset);
436
437 /* First file and section should be FSP info header. */
438 if (fih_offset != NULL && *fih_offset == 0)
439 *fih_offset = file_offset;
440
441 ffsfh = relative_offset(fsp, file_offset);
442
443 printk(FSP_DBG_LVL, "file type = %x\n", le8toh(ffsfh->Type));
444 printk(FSP_DBG_LVL, "file attribs = %x\n",
445 le8toh(ffsfh->Attributes));
446
447 /* Exit FV relocation when empty space found */
448 if (le8toh(ffsfh->Type) == EFI_FV_FILETYPE_FFS_MAX)
449 break;
450
451 /* Next file on 8 byte alignment. */
452 file_offset += ffs_file_size(ffsfh);
453 file_offset = ALIGN_UP(file_offset, 8);
454
455 /* Padding files have no section information. */
456 if (le8toh(ffsfh->Type) == EFI_FV_FILETYPE_FFS_PAD)
457 continue;
458
459 offset += file_section_offset(ffsfh);
460
461 while (offset + sizeof(*csh) < file_offset) {
462 size_t data_size;
463 size_t data_offset;
464
465 csh = relative_offset(fsp, offset);
466
467 printk(FSP_DBG_LVL, "section offset: %zx\n", offset);
468 printk(FSP_DBG_LVL, "section type: %x\n",
469 le8toh(csh->Type));
470
471 data_size = section_data_size(csh);
472 data_offset = section_data_offset(csh);
473
474 if (data_size + data_offset + offset > file_offset) {
475 printk(BIOS_ERR, "Section exceeds FV size.\n");
476 return -1;
477 }
478
479 /*
480 * The entire FSP 1.1 image can be thought of as one
481 * program with a single link address even though there
482 * are multiple TEs linked separately. The reason is
483 * that each TE is linked for XIP. So in order to
484 * relocate the TE properly we need to form the
485 * relocated address based on the TE offset within
486 * FSP proper.
487 */
488 if (le8toh(csh->Type) == EFI_SECTION_TE) {
489 void *te;
490 size_t te_offset = offset + data_offset;
491 uintptr_t te_addr = new_addr + te_offset;
492
493 printk(FSP_DBG_LVL, "TE image at offset %zx\n",
494 te_offset);
495 te = relative_offset(fsp, te_offset);
496 te_relocate(te_addr, te, data_size);
497 }
498
499 offset += data_size + data_offset;
500 /* Sections are aligned to 4 bytes. */
501 offset = ALIGN_UP(offset, 4);
502 }
503 }
504
505 /* Return amount of buffer parsed: FV size. */
506 return fv_length;
507}
508
509ssize_t fsp1_1_relocate(uintptr_t new_addr, void *fsp, size_t size)
510{
511 size_t offset;
512 size_t fih_offset;
513
514 offset = 0;
515 fih_offset = 0;
516 while (offset < size) {
517 ssize_t nparsed;
518
519 /* Relocate each FV within the FSP region. The FSP_INFO_HEADER
520 * should only be located in the first FV. */
521 if (offset == 0)
522 nparsed = relocate_fvh(new_addr, fsp, size, offset,
523 &fih_offset);
524 else
525 nparsed = relocate_fvh(new_addr, fsp, size, offset,
526 NULL);
527
528 /* FV should be larger than 0 or failed to parse. */
529 if (nparsed <= 0) {
530 printk(BIOS_ERR, "FV @ offset %zx relocation failed\n",
531 offset);
532 return -1;
533 }
534
535 offset += nparsed;
536 }
537
538 return relocate_remaining_items(fsp, size, new_addr, fih_offset);
539}