xref: /aosp_15_r20/external/coreboot/src/commonlib/fsp_relocate.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <commonlib/endian.h>
5 #include <commonlib/fsp.h>
6 #include <inttypes.h>
7 #include <commonlib/helpers.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <string.h>
11 #include <vendorcode/intel/fsp/fsp_header.h>
12 
13 #define FSP_DBG_LVL BIOS_NEVER
14 #define MASK_24BITS  0x00FFFFFF
15 
16 /*
17  * UEFI defines everything as little endian. However, this piece of code
18  * can be integrated in a userland tool. That tool could be on a big endian
19  * machine so one needs to access the fields within UEFI structures using
20  * endian-aware accesses.
21  */
22 
23 /* Return 0 if equal. Non-zero if not equal. */
guid_compare(const EFI_GUID * le_guid,const EFI_GUID * native_guid)24 static int guid_compare(const EFI_GUID *le_guid, const EFI_GUID *native_guid)
25 {
26 	if (read_le32(&le_guid->Data1) != native_guid->Data1)
27 		return 1;
28 	if (read_le16(&le_guid->Data2) != native_guid->Data2)
29 		return 1;
30 	if (read_le16(&le_guid->Data3) != native_guid->Data3)
31 		return 1;
32 	return memcmp(le_guid->Data4, native_guid->Data4,
33 			ARRAY_SIZE(le_guid->Data4));
34 }
35 
36 static const EFI_GUID ffs2_guid = EFI_FIRMWARE_FILE_SYSTEM2_GUID;
37 static const EFI_GUID fih_guid = FSP_INFO_HEADER_GUID;
38 
39 struct fsp_patch_table {
40 	uint32_t signature;
41 	uint16_t header_length;
42 	uint8_t header_revision;
43 	uint8_t reserved;
44 	uint32_t patch_entry_num;
45 	uint32_t patch_entries[];
46 } __packed;
47 
48 #define FSPP_SIG 0x50505346
49 
relative_offset(void * base,ssize_t offset)50 static void *relative_offset(void *base, ssize_t offset)
51 {
52 	uintptr_t loc;
53 
54 	loc = (uintptr_t)base;
55 	loc += offset;
56 
57 	return (void *)loc;
58 }
59 
csh_size(const EFI_COMMON_SECTION_HEADER * csh)60 static size_t csh_size(const EFI_COMMON_SECTION_HEADER *csh)
61 {
62 	size_t size;
63 
64 	/* Unpack the array into a type that can be used. */
65 	size = 0;
66 	size |= read_le8(&csh->Size[0]) << 0;
67 	size |= read_le8(&csh->Size[1]) << 8;
68 	size |= read_le8(&csh->Size[2]) << 16;
69 
70 	return size;
71 }
72 
file_section_offset(const EFI_FFS_FILE_HEADER * ffsfh)73 static size_t file_section_offset(const EFI_FFS_FILE_HEADER *ffsfh)
74 {
75 	if (IS_FFS_FILE2(ffsfh))
76 		return sizeof(EFI_FFS_FILE_HEADER2);
77 	else
78 		return sizeof(EFI_FFS_FILE_HEADER);
79 }
80 
section_data_offset(const EFI_COMMON_SECTION_HEADER * csh)81 static size_t section_data_offset(const EFI_COMMON_SECTION_HEADER *csh)
82 {
83 	if (csh_size(csh) == MASK_24BITS)
84 		return sizeof(EFI_COMMON_SECTION_HEADER2);
85 	else
86 		return sizeof(EFI_COMMON_SECTION_HEADER);
87 }
88 
fspp_reloc(void * fsp,size_t fsp_size,uint32_t e)89 static uint32_t *fspp_reloc(void *fsp, size_t fsp_size, uint32_t e)
90 {
91 	size_t offset;
92 
93 	/* Offsets live in bits 23:0. */
94 	offset = e & MASK_24BITS;
95 
96 	/* If bit 31 is set then the offset is considered a negative value
97 	 * relative to the end of the image using 16MiB as the offset's
98 	 * reference. */
99 	if (e & (1 << 31))
100 		offset = fsp_size - (16 * MiB - offset);
101 
102 	/* Determine if offset falls within fsp_size for a 32 bit relocation. */
103 	if (offset > fsp_size - sizeof(uint32_t))
104 		return NULL;
105 
106 	return relative_offset(fsp, offset);
107 }
108 
reloc_type(uint16_t reloc_entry)109 static int reloc_type(uint16_t reloc_entry)
110 {
111 	/* Reloc type in upper 4 bits */
112 	return reloc_entry >> 12;
113 }
114 
reloc_offset(uint16_t reloc_entry)115 static size_t reloc_offset(uint16_t reloc_entry)
116 {
117 	/* Offsets are in low 12 bits. */
118 	return reloc_entry & ((1 << 12) - 1);
119 }
120 
fsp_get_info_hdr(void * fsp,size_t fih_offset)121 static FSP_INFO_HEADER *fsp_get_info_hdr(void *fsp, size_t fih_offset)
122 {
123 	EFI_FFS_FILE_HEADER *ffsfh;
124 	EFI_COMMON_SECTION_HEADER *csh;
125 	FSP_INFO_HEADER *fih;
126 
127 	printk(FSP_DBG_LVL, "FSP_INFO_HEADER offset is %zx\n", fih_offset);
128 
129 	if (fih_offset == 0) {
130 		printk(BIOS_ERR, "FSP_INFO_HEADER offset is 0.\n");
131 		return NULL;
132 	}
133 
134 	/* FSP_INFO_HEADER is located at first file in FV within first RAW section. */
135 	ffsfh = relative_offset(fsp, fih_offset);
136 	fih_offset += file_section_offset(ffsfh);
137 	csh = relative_offset(fsp, fih_offset);
138 	fih_offset += section_data_offset(csh);
139 	fih = relative_offset(fsp, fih_offset);
140 
141 	if (guid_compare(&ffsfh->Name, &fih_guid)) {
142 		printk(BIOS_ERR, "Bad FIH GUID.\n");
143 		return NULL;
144 	}
145 
146 	if (read_le8(&csh->Type) != EFI_SECTION_RAW) {
147 		printk(BIOS_ERR, "FIH file should have raw section: %x\n",
148 			read_le8(&csh->Type));
149 		return NULL;
150 	}
151 
152 	if (read_le32(&fih->Signature) != FSP_SIG) {
153 		printk(BIOS_ERR, "Unexpected FIH signature: %08x\n",
154 			read_le32(&fih->Signature));
155 		return NULL;
156 	}
157 
158 	return fih;
159 }
160 
pe_relocate(uintptr_t new_addr,void * pe,void * fsp,size_t fih_off)161 static int pe_relocate(uintptr_t new_addr, void *pe, void *fsp, size_t fih_off)
162 {
163 	EFI_IMAGE_OPTIONAL_HEADER_UNION *peih;
164 	EFI_IMAGE_DOS_HEADER *doshdr;
165 	EFI_IMAGE_OPTIONAL_HEADER32 *ophdr;
166 	EFI_IMAGE_OPTIONAL_HEADER64 *ophdr64;
167 	FSP_INFO_HEADER *fih;
168 	uint32_t  roffset, rsize;
169 	uint32_t  offset;
170 	uint8_t *pe_base = pe;
171 	uint64_t image_base;
172 	uint64_t img_base_off;
173 	uint64_t delta;
174 
175 	doshdr = pe;
176 	if (read_le16(&doshdr->e_magic) != EFI_IMAGE_DOS_SIGNATURE) {
177 		printk(BIOS_ERR, "Invalid DOS Header/magic\n");
178 		return -1;
179 	}
180 
181 	peih = relative_offset(pe, doshdr->e_lfanew);
182 
183 	if (read_le32(&peih->Pe32.Signature) != EFI_IMAGE_NT_SIGNATURE) {
184 		printk(BIOS_ERR, "Invalid PE32 header\n");
185 		return -1;
186 	}
187 
188 	ophdr = &peih->Pe32.OptionalHeader;
189 	ophdr64 = &peih->Pe32Plus.OptionalHeader;
190 
191 	if (read_le16(&ophdr->Magic) == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
192 		ophdr64 = NULL;
193 	} else if (read_le16(&ophdr64->Magic) == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
194 		ophdr = NULL;
195 	} else {
196 		printk(BIOS_ERR, "No support for non-PE32/PE32+ images\n");
197 		return -1;
198 	}
199 
200 	fih = fsp_get_info_hdr(fsp, fih_off);
201 	if (fih == NULL) {
202 		printk(BIOS_ERR, "No Image base found for FSP PE32\n");
203 		return -1;
204 	}
205 	image_base = read_le32(&fih->ImageBase);
206 	printk(FSP_DBG_LVL, "FSP InfoHdr Image Base is %" PRIX64"\n", image_base);
207 
208 	delta = new_addr - image_base;
209 
210 	img_base_off = ophdr ? read_le32(&ophdr->ImageBase) : read_le64(&ophdr64->ImageBase);
211 	printk(FSP_DBG_LVL, "lfanew 0x%x, delta-0x%" PRIX64 ", FSP Base 0x%" PRIX64 ", NT32ImageBase 0x%" PRIX64 ", offset 0x%" PRIX64 "\n",
212 			read_le32(&doshdr->e_lfanew),
213 			delta, image_base, img_base_off,
214 			(uint64_t)((uint8_t *)(uintptr_t)img_base_off - pe_base));
215 
216 	printk(FSP_DBG_LVL, "relocating PE32%s image at addr - 0x%" PRIxPTR "\n", ophdr ? "" : "+", new_addr);
217 	if (ophdr) {
218 		rsize = read_le32(&ophdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
219 		roffset = read_le32(&ophdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
220 	} else {
221 		rsize = read_le32(&ophdr64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
222 		roffset = read_le32(&ophdr64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
223 	}
224 
225 	printk(FSP_DBG_LVL, "relocation table at offset-%x,size=%x\n", roffset, rsize);
226 
227 	offset = roffset;
228 	while (offset < (roffset + rsize)) {
229 		uint32_t vaddr;
230 		uint32_t rlen, rnum;
231 		uint16_t *rdata;
232 		uint32_t i;
233 		EFI_IMAGE_DATA_DIRECTORY *relocd;
234 
235 		relocd = (void *)&pe_base[offset];
236 		offset += sizeof(*relocd);
237 		// Read relocation type, offset pairs
238 		rlen = read_le32(&relocd->Size) - sizeof(*relocd);
239 		rnum = rlen / sizeof(uint16_t);
240 		vaddr = read_le32(&relocd->VirtualAddress);
241 		rdata = (uint16_t *)&pe_base[offset];
242 		printk(FSP_DBG_LVL, "\t%d Relocs for RVA %x\n", rnum, vaddr);
243 
244 		for (i = 0; i < rnum; i++) {
245 			uint16_t roff = reloc_offset(rdata[i]);
246 			uint16_t rtype = reloc_type(rdata[i]);
247 			uint32_t aoff = vaddr + roff;
248 			uint64_t val;
249 			printk(FSP_DBG_LVL, "\t\treloc type %x offset %x aoff %x, base-0x%" PRIX64 "\n",
250 					rtype, roff, aoff, img_base_off);
251 			switch (rtype) {
252 			case EFI_IMAGE_REL_BASED_ABSOLUTE:
253 				continue;
254 			case EFI_IMAGE_REL_BASED_HIGHLOW:
255 				val = read_le32(&pe_base[aoff]);
256 				printk(FSP_DBG_LVL, "Adjusting %p %" PRIX64 " -> %" PRIX64 "\n",
257 					&pe_base[aoff], val, val + delta);
258 				write_le32(&pe_base[aoff], val + delta);
259 				break;
260 			case EFI_IMAGE_REL_BASED_DIR64:
261 				val = read_le64(&pe_base[aoff]);
262 				printk(FSP_DBG_LVL, "Adjusting %p %"  PRIX64 " -> %" PRIX64 "\n",
263 					&pe_base[aoff], val, val + delta);
264 				write_le64(&pe_base[aoff], val + delta);
265 				break;
266 			default:
267 				printk(BIOS_ERR, "Unsupported relocation type %d\n",
268 						rtype);
269 				return -1;
270 			}
271 		}
272 		offset += sizeof(*rdata) * rnum;
273 	}
274 	printk(FSP_DBG_LVL, "Adjust Image Base %" PRIX64 "->%" PRIX64 "\n",
275 			img_base_off, img_base_off + delta);
276 	img_base_off += delta;
277 	if (ophdr)
278 		write_le32(&ophdr->ImageBase, img_base_off);
279 	else
280 		write_le64(&ophdr64->ImageBase, img_base_off);
281 
282 	return 0;
283 }
284 
te_relocate(uintptr_t new_addr,void * te)285 static int te_relocate(uintptr_t new_addr, void *te)
286 {
287 	EFI_TE_IMAGE_HEADER *teih;
288 	EFI_IMAGE_DATA_DIRECTORY *relocd;
289 	EFI_IMAGE_BASE_RELOCATION *relocb;
290 	uintptr_t image_base;
291 	size_t fixup_offset;
292 	size_t num_relocs;
293 	uint16_t *reloc;
294 	size_t relocd_offset;
295 	uint8_t *te_base;
296 	uint32_t adj;
297 
298 	teih = te;
299 
300 	if (read_le16(&teih->Signature) != EFI_TE_IMAGE_HEADER_SIGNATURE) {
301 		printk(BIOS_ERR, "TE Signature mismatch: %x vs %x\n",
302 			read_le16(&teih->Signature),
303 			EFI_TE_IMAGE_HEADER_SIGNATURE);
304 		return -1;
305 	}
306 
307 	/*
308 	 * A TE image is created by converting a PE file. Because of this
309 	 * the offsets within the headers are off. In order to calculate
310 	 * the correct relative offsets one needs to subtract fixup_offset
311 	 * from the encoded offsets. Similarly, the linked address of the
312 	 * program is found by adding the fixup_offset to the ImageBase.
313 	 */
314 	fixup_offset = read_le16(&teih->StrippedSize);
315 	fixup_offset -= sizeof(EFI_TE_IMAGE_HEADER);
316 	/* Keep track of a base that is correctly adjusted so that offsets
317 	 * can be used directly. */
318 	te_base = te;
319 	te_base -= fixup_offset;
320 
321 	image_base = read_le64(&teih->ImageBase);
322 	adj = new_addr - (image_base + fixup_offset);
323 
324 	printk(FSP_DBG_LVL, "TE Image %p -> %p adjust value: %x\n",
325 		(void *)image_base, (void *)new_addr, adj);
326 
327 	/* Adjust ImageBase for consistency. */
328 	write_le64(&teih->ImageBase, (uint32_t)(image_base + adj));
329 
330 	relocd = &teih->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC];
331 
332 	relocd_offset = 0;
333 	/* Though the field name is VirtualAddress it's actually relative to
334 	 * the beginning of the image which is linked at ImageBase. */
335 	relocb = relative_offset(te,
336 			read_le32(&relocd->VirtualAddress) - fixup_offset);
337 	while (relocd_offset < read_le32(&relocd->Size)) {
338 		size_t rva_offset = read_le32(&relocb->VirtualAddress);
339 
340 		printk(FSP_DBG_LVL, "Relocs for RVA offset %zx\n", rva_offset);
341 		num_relocs = read_le32(&relocb->SizeOfBlock) - sizeof(*relocb);
342 		num_relocs /= sizeof(uint16_t);
343 		reloc = relative_offset(relocb, sizeof(*relocb));
344 
345 		printk(FSP_DBG_LVL, "Num relocs in block: %zx\n", num_relocs);
346 
347 		while (num_relocs > 0) {
348 			uint16_t reloc_val = read_le16(reloc);
349 			int type = reloc_type(reloc_val);
350 			size_t offset = reloc_offset(reloc_val);
351 
352 			printk(FSP_DBG_LVL, "reloc type %x offset %zx\n",
353 				type, offset);
354 
355 			if (type == EFI_IMAGE_REL_BASED_HIGHLOW ||
356 					type == EFI_IMAGE_REL_BASED_DIR64) {
357 				uint32_t *reloc_addr;
358 				uint32_t val;
359 
360 				offset += rva_offset;
361 				reloc_addr = (void *)&te_base[offset];
362 				val = read_le32(reloc_addr);
363 
364 				printk(FSP_DBG_LVL, "Adjusting %p %x -> %x\n",
365 					reloc_addr, val, val + adj);
366 				write_le32(reloc_addr, val + adj);
367 			} else if (type != EFI_IMAGE_REL_BASED_ABSOLUTE) {
368 				printk(BIOS_ERR, "Unknown reloc type: %x\n",
369 					type);
370 				return -1;
371 			}
372 			num_relocs--;
373 			reloc++;
374 		}
375 
376 		/* Track consumption of relocation directory contents. */
377 		relocd_offset += read_le32(&relocb->SizeOfBlock);
378 		/* Get next relocation block to process. */
379 		relocb = relative_offset(relocb,
380 					read_le32(&relocb->SizeOfBlock));
381 	}
382 
383 	return 0;
384 }
385 
section_data_size(const EFI_COMMON_SECTION_HEADER * csh)386 static size_t section_data_size(const EFI_COMMON_SECTION_HEADER *csh)
387 {
388 	size_t section_size;
389 
390 	if (csh_size(csh) == MASK_24BITS)
391 		section_size = read_le32(&SECTION2_SIZE(csh));
392 	else
393 		section_size = csh_size(csh);
394 
395 	return section_size - section_data_offset(csh);
396 }
397 
ffs_file_size(const EFI_FFS_FILE_HEADER * ffsfh)398 static size_t ffs_file_size(const EFI_FFS_FILE_HEADER *ffsfh)
399 {
400 	size_t size;
401 
402 	if (IS_FFS_FILE2(ffsfh)) {
403 		/*
404 		 * this cast is needed with UEFI 2.6 headers in order
405 		 * to read the UINT32 value that FFS_FILE2_SIZE converts
406 		 * the return into
407 		 */
408 		uint32_t file2_size = FFS_FILE2_SIZE(ffsfh);
409 		size = read_le32(&file2_size);
410 	} else {
411 		size = read_le8(&ffsfh->Size[0]) << 0;
412 		size |= read_le8(&ffsfh->Size[1]) << 8;
413 		size |= read_le8(&ffsfh->Size[2]) << 16;
414 	}
415 	return size;
416 }
417 
relocate_patch_table(void * fsp,size_t size,size_t offset,ssize_t adjustment)418 static int relocate_patch_table(void *fsp, size_t size, size_t offset,
419 				ssize_t adjustment)
420 {
421 	struct fsp_patch_table *table;
422 	size_t num;
423 	size_t num_entries;
424 
425 	table = relative_offset(fsp, offset);
426 
427 	if ((offset + sizeof(*table) > size) ||
428 	    (read_le16(&table->header_length) + offset) > size) {
429 		printk(BIOS_ERR, "FSPP not entirely contained in region.\n");
430 		return -1;
431 	}
432 
433 	num_entries = read_le32(&table->patch_entry_num);
434 	printk(FSP_DBG_LVL, "FSPP relocs: %zx\n", num_entries);
435 
436 	for (num = 0; num < num_entries; num++) {
437 		uint32_t *reloc;
438 		uint32_t reloc_val;
439 
440 		reloc = fspp_reloc(fsp, size,
441 				read_le32(&table->patch_entries[num]));
442 
443 		if (reloc == NULL) {
444 			printk(BIOS_ERR, "Ignoring FSPP entry: %x\n",
445 				read_le32(&table->patch_entries[num]));
446 			continue;
447 		}
448 
449 		reloc_val = read_le32(reloc);
450 		printk(FSP_DBG_LVL, "Adjusting %p %x -> %x\n",
451 			reloc, reloc_val,
452 			(unsigned int)(reloc_val + adjustment));
453 
454 		write_le32(reloc, reloc_val + adjustment);
455 	}
456 
457 	return 0;
458 }
459 
relocate_remaining_items(void * fsp,size_t size,uintptr_t new_addr,size_t fih_offset)460 static ssize_t relocate_remaining_items(void *fsp, size_t size,
461 					uintptr_t new_addr, size_t fih_offset)
462 {
463 	EFI_FFS_FILE_HEADER *ffsfh;
464 	EFI_COMMON_SECTION_HEADER *csh;
465 	FSP_INFO_HEADER *fih;
466 	ssize_t adjustment;
467 	size_t offset;
468 
469 	printk(FSP_DBG_LVL, "FSP_INFO_HEADER offset is %zx\n", fih_offset);
470 
471 	if (fih_offset == 0) {
472 		printk(BIOS_ERR, "FSP_INFO_HEADER offset is 0.\n");
473 		return -1;
474 	}
475 
476 	/* FSP_INFO_HEADER at first file in FV within first RAW section. */
477 	ffsfh = relative_offset(fsp, fih_offset);
478 	fih_offset += file_section_offset(ffsfh);
479 	csh = relative_offset(fsp, fih_offset);
480 	fih_offset += section_data_offset(csh);
481 	fih = relative_offset(fsp, fih_offset);
482 
483 	if (guid_compare(&ffsfh->Name, &fih_guid)) {
484 		printk(BIOS_ERR, "Bad FIH GUID.\n");
485 		return -1;
486 	}
487 
488 	if (read_le8(&csh->Type) != EFI_SECTION_RAW) {
489 		printk(BIOS_ERR, "FIH file should have raw section: %x\n",
490 			read_le8(&csh->Type));
491 		return -1;
492 	}
493 
494 	if (read_le32(&fih->Signature) != FSP_SIG) {
495 		printk(BIOS_ERR, "Unexpected FIH signature: %08x\n",
496 			read_le32(&fih->Signature));
497 	}
498 
499 	adjustment = (intptr_t)new_addr - read_le32(&fih->ImageBase);
500 
501 	/* Update ImageBase to reflect FSP's new home. */
502 	write_le32(&fih->ImageBase, adjustment + read_le32(&fih->ImageBase));
503 	printk(FSP_DBG_LVL, "Updated FSP InfoHdr Image Base to %x\n",
504 			read_le32(&fih->ImageBase));
505 
506 	/* Need to find patch table and adjust each entry. The tables
507 	 * following FSP_INFO_HEADER have a 32-bit signature and header
508 	 * length. The patch table is denoted as having a 'FSPP' signature;
509 	 * the table format doesn't follow the other tables. */
510 	offset = fih_offset + read_le32(&fih->HeaderLength);
511 	while (offset + 2 * sizeof(uint32_t) <= size) {
512 		uint32_t *table_headers;
513 
514 		table_headers = relative_offset(fsp, offset);
515 
516 		printk(FSP_DBG_LVL, "Checking offset %zx for 'FSPP'\n",
517 			offset);
518 
519 		if (read_le32(&table_headers[0]) != FSPP_SIG) {
520 			offset += read_le32(&table_headers[1]);
521 			continue;
522 		}
523 
524 		if (relocate_patch_table(fsp, size, offset, adjustment)) {
525 			printk(BIOS_ERR, "FSPP relocation failed.\n");
526 			return -1;
527 		}
528 
529 		return fih_offset;
530 	}
531 
532 	printk(BIOS_ERR, "Could not find the FSP patch table.\n");
533 	return -1;
534 }
535 
relocate_fvh(uintptr_t new_addr,void * fsp,size_t fsp_size,size_t fvh_offset,size_t * fih_offset)536 static ssize_t relocate_fvh(uintptr_t new_addr, void *fsp, size_t fsp_size,
537 				size_t fvh_offset, size_t *fih_offset)
538 {
539 	EFI_FIRMWARE_VOLUME_HEADER *fvh;
540 	EFI_FFS_FILE_HEADER *ffsfh;
541 	EFI_COMMON_SECTION_HEADER *csh;
542 	size_t offset;
543 	size_t file_offset;
544 	size_t size;
545 	size_t fv_length;
546 
547 	offset = fvh_offset;
548 	fvh = relative_offset(fsp, offset);
549 
550 	if (read_le32(&fvh->Signature) != EFI_FVH_SIGNATURE)
551 		return -1;
552 
553 	fv_length = read_le64(&fvh->FvLength);
554 
555 	printk(FSP_DBG_LVL, "FVH length: %zx Offset: %zx Mapping length: %zx\n",
556 		fv_length, offset, fsp_size);
557 
558 	if (fv_length + offset > fsp_size)
559 		return -1;
560 
561 	/* Parse only this FV. However, the algorithm uses offsets into the
562 	 * entire FSP region so make size include the starting offset. */
563 	size = fv_length + offset;
564 
565 	if (guid_compare(&fvh->FileSystemGuid, &ffs2_guid)) {
566 		printk(BIOS_ERR, "FVH not an FFS2 type.\n");
567 		return -1;
568 	}
569 
570 	if (read_le16(&fvh->ExtHeaderOffset) != 0) {
571 		EFI_FIRMWARE_VOLUME_EXT_HEADER *fveh;
572 
573 		offset += read_le16(&fvh->ExtHeaderOffset);
574 		fveh = relative_offset(fsp, offset);
575 		printk(FSP_DBG_LVL, "Extended Header Offset: %zx Size: %zx\n",
576 			(size_t)read_le16(&fvh->ExtHeaderOffset),
577 			(size_t)read_le32(&fveh->ExtHeaderSize));
578 		offset += read_le32(&fveh->ExtHeaderSize);
579 		/* FFS files are 8 byte aligned after extended header. */
580 		offset = ALIGN_UP(offset, 8);
581 	} else {
582 		offset += read_le16(&fvh->HeaderLength);
583 	}
584 
585 	file_offset = offset;
586 	while (file_offset + sizeof(*ffsfh) < size) {
587 		offset = file_offset;
588 		printk(FSP_DBG_LVL, "file offset: %zx\n", file_offset);
589 
590 		/* First file and section should be FSP info header. */
591 		if (*fih_offset == 0)
592 			*fih_offset = file_offset;
593 
594 		ffsfh = relative_offset(fsp, file_offset);
595 
596 		printk(FSP_DBG_LVL, "file type = %x\n", read_le8(&ffsfh->Type));
597 		printk(FSP_DBG_LVL, "file attribs = %x\n",
598 			read_le8(&ffsfh->Attributes));
599 
600 		/* Exit FV relocation when empty space found */
601 		if (read_le8(&ffsfh->Type) == EFI_FV_FILETYPE_FFS_MAX)
602 			break;
603 
604 		/* Next file on 8 byte alignment. */
605 		file_offset += ffs_file_size(ffsfh);
606 		file_offset = ALIGN_UP(file_offset, 8);
607 
608 		/* Padding files have no section information. */
609 		if (read_le8(&ffsfh->Type) == EFI_FV_FILETYPE_FFS_PAD)
610 			continue;
611 
612 		offset += file_section_offset(ffsfh);
613 
614 		while (offset + sizeof(*csh) < file_offset) {
615 			size_t data_size;
616 			size_t data_offset;
617 			void *section_data;
618 			size_t section_offset;
619 			uintptr_t section_addr;
620 
621 			csh = relative_offset(fsp, offset);
622 
623 			printk(FSP_DBG_LVL, "section offset: %zx\n", offset);
624 			printk(FSP_DBG_LVL, "section type: %x\n",
625 				read_le8(&csh->Type));
626 
627 			data_size = section_data_size(csh);
628 			data_offset = section_data_offset(csh);
629 
630 			if (data_size + data_offset + offset > file_offset) {
631 				printk(BIOS_ERR, "Section exceeds FV size.\n");
632 				return -1;
633 			}
634 
635 			/*
636 			 * The entire FSP image can be thought of as one
637 			 * program with a single link address even though there
638 			 * are multiple TEs linked separately. The reason is
639 			 * that each TE is linked for XIP. So in order to
640 			 * relocate the TE properly we need to form the
641 			 * relocated address based on the TE offset within
642 			 * FSP proper.
643 			 */
644 			section_offset = offset + data_offset;
645 			section_addr = new_addr + section_offset;
646 			section_data = relative_offset(fsp, section_offset);
647 
648 			if (read_le8(&csh->Type) == EFI_SECTION_TE) {
649 				printk(FSP_DBG_LVL, "TE image at offset %zx\n",
650 					section_offset);
651 				te_relocate(section_addr, section_data);
652 			} else if (read_le8(&csh->Type) == EFI_SECTION_PE32) {
653 				printk(FSP_DBG_LVL, "PE32 image at offset %zx\n",
654 					section_offset);
655 				pe_relocate(new_addr, section_data, fsp, *fih_offset);
656 			}
657 
658 			offset += data_size + data_offset;
659 			/* Sections are aligned to 4 bytes. */
660 			offset = ALIGN_UP(offset, 4);
661 		}
662 	}
663 
664 	/* Return amount of buffer parsed: FV size. */
665 	return fv_length;
666 }
667 
fsp_component_relocate(uintptr_t new_addr,void * fsp,size_t size)668 ssize_t fsp_component_relocate(uintptr_t new_addr, void *fsp, size_t size)
669 {
670 	size_t offset;
671 	size_t fih_offset;
672 
673 	offset = 0;
674 	fih_offset = 0;
675 	while (offset < size) {
676 		ssize_t nparsed;
677 
678 		/* Relocate each FV within the FSP region. */
679 		nparsed = relocate_fvh(new_addr, fsp, size, offset, &fih_offset);
680 
681 		/* FV should be larger than 0 or failed to parse. */
682 		if (nparsed <= 0) {
683 			printk(BIOS_ERR, "FV @ offset %zx relocation failed\n",
684 				offset);
685 			return -1;
686 		}
687 
688 		offset += nparsed;
689 	}
690 
691 	return relocate_remaining_items(fsp, size, new_addr, fih_offset);
692 }
693 
fsp1_1_relocate(uintptr_t new_addr,void * fsp,size_t size)694 ssize_t fsp1_1_relocate(uintptr_t new_addr, void *fsp, size_t size)
695 {
696 	return fsp_component_relocate(new_addr, fsp, size);
697 }
698