These fields are documented in the Alder Lake-S Client Platform SPI Programming Guide, but they are not presented in the Skylake-LP Client Platform SPI Programming Guide Change-Id: I624fe5cb28aa3cb207bc48aa8d31b2a71b70bcf2 Signed-off-by: Alexander Goncharov <chat@joursoir.net> Reviewed-on: https://review.coreboot.org/c/coreboot/+/83282 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Eric Lai <ericllai@google.com> Reviewed-by: Subrata Banik <subratabanik@google.com>
		
			
				
	
	
		
			2661 lines
		
	
	
		
			75 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2661 lines
		
	
	
		
			75 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* ifdtool - dump Intel Firmware Descriptor information */
 | |
| /* SPDX-License-Identifier: GPL-2.0-only */
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <getopt.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <commonlib/helpers.h>
 | |
| #include <fmap.h>
 | |
| #include "ifdtool.h"
 | |
| 
 | |
| #ifndef O_BINARY
 | |
| #define O_BINARY 0
 | |
| #endif
 | |
| 
 | |
| /**
 | |
|  * PTR_IN_RANGE - examine whether a pointer falls in [base, base + limit)
 | |
|  * @param ptr:    the non-void* pointer to a single arbitrary-sized object.
 | |
|  * @param base:   base address represented with char* type.
 | |
|  * @param limit:  upper limit of the legal address.
 | |
|  *
 | |
|  */
 | |
| #define PTR_IN_RANGE(ptr, base, limit)			\
 | |
| 	((const char *)(ptr) >= (base) &&		\
 | |
| 	 (const char *)&(ptr)[1] <= (base) + (limit))
 | |
| 
 | |
| /**
 | |
|  * PLATFORM_HAS_GBE_REGION - some platforms do not support the PCH GbE LAN region
 | |
|  */
 | |
| #define PLATFORM_HAS_GBE_REGION (platform != PLATFORM_DNV)
 | |
| 
 | |
| /*
 | |
|  * PLATFORM_HAS_EC_REGION - some platforms do not support the EC region
 | |
|  */
 | |
| #define PLATFORM_HAS_EC_REGION (ifd_version >= IFD_VERSION_2 && platform != PLATFORM_DNV)
 | |
| 
 | |
| /*
 | |
|  * PLATFORM_HAS_10GBE_X_REGION - some platforms have 1 or more 10GbE LAN regions
 | |
|  */
 | |
| #define PLATFORM_HAS_10GBE_0_REGION (platform == PLATFORM_DNV)
 | |
| #define PLATFORM_HAS_10GBE_1_REGION (platform == PLATFORM_DNV)
 | |
| 
 | |
| union gprd {
 | |
| 	struct bit_field {
 | |
| 		/*
 | |
| 		 * Start Address: bit 0-14 of the GPRD represents the
 | |
| 		 * protected region start address, where bit 0-11 of
 | |
| 		 * the start address are assumed to be zero.
 | |
| 		 */
 | |
| 		uint32_t start : 15;
 | |
| 
 | |
| 		/* Specifies read protection is enabled */
 | |
| 		uint32_t read_protect_en : 1;
 | |
| 
 | |
| 		/*
 | |
| 		 * End Address: bit 16-30 of the GPRD represents the
 | |
| 		 * protected region end address, where bit 0-11 of
 | |
| 		 * the end address are assumed to be 0xfff.
 | |
| 		 */
 | |
| 		uint32_t end : 15;
 | |
| 
 | |
| 		/* Specifies write protection is enabled */
 | |
| 		uint32_t write_protect_en : 1;
 | |
| 	} __packed data;
 | |
| 
 | |
| 	uint32_t value;
 | |
| };
 | |
| 
 | |
| static int max_regions_from_fdbar(const struct fdbar *fdb);
 | |
| 
 | |
| static int ifd_version;
 | |
| static int chipset;
 | |
| static unsigned int max_regions = 0;
 | |
| static int selected_chip = 0;
 | |
| static int platform = -1;
 | |
| 
 | |
| static const struct region_name region_names[MAX_REGIONS] = {
 | |
| 	{ "Flash Descriptor", "fd", "flashregion_0_flashdescriptor.bin", "SI_DESC" },
 | |
| 	{ "BIOS", "bios", "flashregion_1_bios.bin", "SI_BIOS" },
 | |
| 	{ "Intel ME", "me", "flashregion_2_intel_me.bin", "SI_ME" },
 | |
| 	{ "GbE", "gbe", "flashregion_3_gbe.bin", "SI_GBE" },
 | |
| 	{ "Platform Data", "pd", "flashregion_4_platform_data.bin", "SI_PDR" },
 | |
| 	{ "Device Exp1", "devexp", "flashregion_5_device_exp.bin", "SI_DEVICEEXT" },
 | |
| 	{ "Secondary BIOS", "bios2", "flashregion_6_bios2.bin", "SI_BIOS2" },
 | |
| 	{ "Reserved", "res7", "flashregion_7_reserved.bin", NULL },
 | |
| 	{ "EC", "ec", "flashregion_8_ec.bin", "SI_EC" },
 | |
| 	{ "Device Exp2", "devexp2", "flashregion_9_device_exp.bin", "SI_DEVICEEXT2" },
 | |
| 	{ "IE", "ie", "flashregion_10_ie.bin", "SI_IE" },
 | |
| 	{ "10GbE_0", "10gbe_0", "flashregion_11_10gbe0.bin", "SI_10GBE0" },
 | |
| 	{ "10GbE_1", "10gbe_1", "flashregion_12_10gbe1.bin", "SI_10GBE1" },
 | |
| 	{ "Reserved", "res13", "flashregion_13_reserved.bin", NULL },
 | |
| 	{ "Reserved", "res14", "flashregion_14_reserved.bin", NULL },
 | |
| 	{ "PTT", "ptt", "flashregion_15_ptt.bin", "SI_PTT" },
 | |
| };
 | |
| 
 | |
| /* port from flashrom */
 | |
| static const char *const ich_chipset_names[] = {
 | |
| 	"Unknown ICH",
 | |
| 	"ICH8",
 | |
| 	"ICH9",
 | |
| 	"ICH10",
 | |
| 	"Unknown PCH",
 | |
| 	"5 series Ibex Peak",
 | |
| 	"6 series Cougar Point",
 | |
| 	"7 series Panther Point",
 | |
| 	"8 series Lynx Point",
 | |
| 	"Baytrail",
 | |
| 	"8 series Lynx Point LP",
 | |
| 	"8 series Wellsburg",
 | |
| 	"9 series Wildcat Point",
 | |
| 	"9 series Wildcat Point LP",
 | |
| 	"Apollo Lake: N3xxx, J3xxx",
 | |
| 	"Gemini Lake: N5xxx, J5xxx, N4xxx, J4xxx",
 | |
| 	"Jasper Lake: N6xxx, N51xx, N45xx",
 | |
| 	"Elkhart Lake: x6000 series Atom",
 | |
| 	"100/200 series Sunrise Point",
 | |
| 	"300 series Cannon Point",
 | |
| 	"400 series Ice Point",
 | |
| 	"500 series Tiger Point/ 600 series Alder Point",
 | |
| 	"800 series Meteor Lake",
 | |
| 	"C620 series Lewisburg",
 | |
| 	"Denverton: C39xx",
 | |
| 	NULL
 | |
| };
 | |
| 
 | |
| static struct fdbar *find_fd(char *image, int size)
 | |
| {
 | |
| 	int i, found = 0;
 | |
| 
 | |
| 	/* Scan for FD signature */
 | |
| 	for (i = 0; i < (size - 4); i += 4) {
 | |
| 		if (*(uint32_t *)(image + i) == 0x0FF0A55A) {
 | |
| 			found = 1;
 | |
| 			break; // signature found.
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!found) {
 | |
| 		printf("No Flash Descriptor found in this image\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	struct fdbar *fdb = (struct fdbar *)(image + i);
 | |
| 	return PTR_IN_RANGE(fdb, image, size) ? fdb : NULL;
 | |
| }
 | |
| 
 | |
| static char *find_flumap(char *image, int size)
 | |
| {
 | |
| 	/* The upper map is located in the word before the 256B-long OEM section
 | |
| 	 * at the end of the 4kB-long flash descriptor. In the official
 | |
| 	 * documentation this is defined as FDBAR + 0xEFC. However, starting
 | |
| 	 * with B-Step of Ibex Peak (5 series) the signature (and thus FDBAR)
 | |
| 	 * has moved 16 bytes back to offset 0x10 of the image. Although
 | |
| 	 * official documentation still maintains the offset relative to FDBAR
 | |
| 	 * this is wrong and a simple fixed offset from the start of the image
 | |
| 	 * works.
 | |
| 	 */
 | |
| 	char *flumap = image + 4096 - 256 - 4;
 | |
| 	return PTR_IN_RANGE(flumap, image, size) ? flumap : NULL;
 | |
| }
 | |
| 
 | |
| static struct fcba *find_fcba(char *image, int size)
 | |
| {
 | |
| 	struct fdbar *fdb = find_fd(image, size);
 | |
| 	if (!fdb)
 | |
| 		return NULL;
 | |
| 	struct fcba *fcba = (struct fcba *)(image + ((fdb->flmap0 & 0xff) << 4));
 | |
| 	return PTR_IN_RANGE(fcba, image, size) ? fcba : NULL;
 | |
| }
 | |
| 
 | |
| static struct fmba *find_fmba(char *image, int size)
 | |
| {
 | |
| 	struct fdbar *fdb = find_fd(image, size);
 | |
| 	if (!fdb)
 | |
| 		return NULL;
 | |
| 	struct fmba *fmba = (struct fmba *)(image + ((fdb->flmap1 & 0xff) << 4));
 | |
| 	return PTR_IN_RANGE(fmba, image, size) ? fmba : NULL;
 | |
| }
 | |
| 
 | |
| static struct frba *find_frba(char *image, int size)
 | |
| {
 | |
| 	struct fdbar *fdb = find_fd(image, size);
 | |
| 	if (!fdb)
 | |
| 		return NULL;
 | |
| 	struct frba *frba =
 | |
| 		(struct frba *) (image + (((fdb->flmap0 >> 16) & 0xff) << 4));
 | |
| 	return PTR_IN_RANGE(frba, image, size) ? frba : NULL;
 | |
| }
 | |
| 
 | |
| static struct fpsba *find_fpsba(char *image, int size)
 | |
| {
 | |
| 	struct fdbar *fdb = find_fd(image, size);
 | |
| 	if (!fdb)
 | |
| 		return NULL;
 | |
| 	struct fpsba *fpsba =
 | |
| 		(struct fpsba *) (image + (((fdb->flmap1 >> 16) & 0xff) << 4));
 | |
| 
 | |
| 	int SSL = ((fdb->flmap1 >> 24) & 0xff) * sizeof(uint32_t);
 | |
| 	if ((((char *)fpsba) + SSL) >= (image + size))
 | |
| 		return NULL;
 | |
| 	return fpsba;
 | |
| }
 | |
| 
 | |
| static struct fmsba *find_fmsba(char *image, int size)
 | |
| {
 | |
| 	struct fdbar *fdb = find_fd(image, size);
 | |
| 	if (!fdb)
 | |
| 		return NULL;
 | |
| 	struct fmsba *fmsba = (struct fmsba *)(image + ((fdb->flmap2 & 0xff) << 4));
 | |
| 	return PTR_IN_RANGE(fmsba, image, size) ? fmsba : NULL;
 | |
| }
 | |
| 
 | |
| /* port from flashrom */
 | |
| static enum ich_chipset ifd1_guess_chipset(char *image, int size)
 | |
| {
 | |
| 	const struct fdbar *fdb = find_fd(image, size);
 | |
| 	if (!fdb)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	uint32_t iccriba = (fdb->flmap2 >> 16) & 0xff;
 | |
| 	uint32_t msl = (fdb->flmap2 >> 8) & 0xff;
 | |
| 	uint32_t isl = (fdb->flmap1 >> 24);
 | |
| 
 | |
| 	/* Rest for IFD1 chipset type */
 | |
| 	if (iccriba == 0x00) {
 | |
| 		if (msl == 0 && isl <= 2)
 | |
| 			return CHIPSET_ICH8;
 | |
| 		else if (isl <= 2)
 | |
| 			return CHIPSET_ICH9;
 | |
| 		else if (isl <= 10)
 | |
| 			return CHIPSET_ICH10;
 | |
| 		else if (isl <= 16)
 | |
| 			return CHIPSET_5_SERIES_IBEX_PEAK;
 | |
| 		printf("Peculiar firmware descriptor, assuming Ibex Peak compatibility.\n");
 | |
| 		return CHIPSET_5_SERIES_IBEX_PEAK;
 | |
| 	} else if (iccriba < 0x31 && (fdb->flmap2 & 0xff) < 0x30) {
 | |
| 		if (msl == 0 && isl <= 17)
 | |
| 			return CHIPSET_BAYTRAIL;
 | |
| 		else if (msl <= 1 && isl <= 18)
 | |
| 			return CHIPSET_6_SERIES_COUGAR_POINT;
 | |
| 		else if (msl <= 1 && isl <= 21)
 | |
| 			return CHIPSET_8_SERIES_LYNX_POINT;
 | |
| 		printf("Peculiar firmware descriptor, assuming Wildcat Point compatibility.\n");
 | |
| 		return CHIPSET_9_SERIES_WILDCAT_POINT;
 | |
| 	}
 | |
| 	return CHIPSET_PCH_UNKNOWN;
 | |
| }
 | |
| 
 | |
| static enum ich_chipset ifd2_platform_to_chipset(const int pindex)
 | |
| {
 | |
| 	switch (pindex) {
 | |
| 	case PLATFORM_APL:
 | |
| 		return CHIPSET_N_J_SERIES_APOLLO_LAKE;
 | |
| 	case PLATFORM_GLK:
 | |
| 		return CHIPSET_N_J_SERIES_GEMINI_LAKE;
 | |
| 	case PLATFORM_JSL:
 | |
| 		return CHIPSET_N_SERIES_JASPER_LAKE;
 | |
| 	case PLATFORM_EHL:
 | |
| 		return CHIPSET_x6000_SERIES_ELKHART_LAKE;
 | |
| 	case PLATFORM_SKLKBL:
 | |
| 		return CHIPSET_100_200_SERIES_SUNRISE_POINT;
 | |
| 	case PLATFORM_CNL:
 | |
| 		return CHIPSET_300_SERIES_CANNON_POINT;
 | |
| 	case PLATFORM_TGL:
 | |
| 	case PLATFORM_ADL:
 | |
| 	case PLATFORM_IFD2:
 | |
| 		return CHIPSET_500_600_SERIES_TIGER_ALDER_POINT;
 | |
| 	case PLATFORM_MTL:
 | |
| 		return CHIPSET_800_SERIES_METEOR_LAKE;
 | |
| 	case PLATFORM_PTL:
 | |
| 		return CHIPSET_900_SERIES_PANTHER_LAKE;
 | |
| 	case PLATFORM_ICL:
 | |
| 		return CHIPSET_400_SERIES_ICE_POINT;
 | |
| 	case PLATFORM_LBG:
 | |
| 		return CHIPSET_C620_SERIES_LEWISBURG;
 | |
| 	case PLATFORM_DNV:
 | |
| 		return CHIPSET_DENVERTON;
 | |
| 	case PLATFORM_WBG:
 | |
| 		return CHIPSET_8_SERIES_WELLSBURG;
 | |
| 	default:
 | |
| 		return CHIPSET_PCH_UNKNOWN;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Some newer platforms have re-defined the FCBA field that was used to
 | |
|  * distinguish IFD v1 v/s v2. Define a list of platforms that we know do not
 | |
|  * have the required FCBA field, but are IFD v2 and return true if current
 | |
|  * platform is one of them.
 | |
|  */
 | |
| static int is_platform_ifd_2(void)
 | |
| {
 | |
| 	static const int ifd_2_platforms[] = {
 | |
| 		PLATFORM_APL,
 | |
| 		PLATFORM_GLK,
 | |
| 		PLATFORM_CNL,
 | |
| 		PLATFORM_LBG,
 | |
| 		PLATFORM_DNV,
 | |
| 		PLATFORM_ICL,
 | |
| 		PLATFORM_TGL,
 | |
| 		PLATFORM_JSL,
 | |
| 		PLATFORM_EHL,
 | |
| 		PLATFORM_ADL,
 | |
| 		PLATFORM_SKLKBL,
 | |
| 		PLATFORM_IFD2,
 | |
| 		PLATFORM_MTL,
 | |
| 		PLATFORM_PTL,
 | |
| 		PLATFORM_WBG,
 | |
| 	};
 | |
| 	unsigned int i;
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(ifd_2_platforms); i++) {
 | |
| 		if (platform == ifd_2_platforms[i])
 | |
| 			return 1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void check_ifd_version(char *image, int size)
 | |
| {
 | |
| 	const struct fdbar *fdb = find_fd(image, size);
 | |
| 
 | |
| 	if (is_platform_ifd_2()) {
 | |
| 		chipset = ifd2_platform_to_chipset(platform);
 | |
| 		if (chipset == CHIPSET_8_SERIES_WELLSBURG)
 | |
| 			ifd_version = IFD_VERSION_1_5;
 | |
| 		else
 | |
| 			ifd_version = IFD_VERSION_2;
 | |
| 		max_regions = MIN(max_regions_from_fdbar(fdb), MAX_REGIONS);
 | |
| 	} else {
 | |
| 		ifd_version = IFD_VERSION_1;
 | |
| 		chipset = ifd1_guess_chipset(image, size);
 | |
| 		max_regions = MIN(max_regions_from_fdbar(fdb), MAX_REGIONS_OLD);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static struct region get_region(const struct frba *frba, unsigned int region_type)
 | |
| {
 | |
| 	int base_mask;
 | |
| 	int limit_mask;
 | |
| 	uint32_t flreg;
 | |
| 	struct region region;
 | |
| 
 | |
| 	if (ifd_version >= IFD_VERSION_2)
 | |
| 		base_mask = 0x7fff;
 | |
| 	else
 | |
| 		base_mask = 0xfff;
 | |
| 
 | |
| 	limit_mask = base_mask << 16;
 | |
| 
 | |
| 	if (region_type >= max_regions) {
 | |
| 		fprintf(stderr, "Invalid region type %d.\n", region_type);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	flreg = frba->flreg[region_type];
 | |
| 	region.base = (flreg & base_mask) << 12;
 | |
| 	region.limit = ((flreg & limit_mask) >> 4) | 0xfff;
 | |
| 	region.size = region.limit - region.base + 1;
 | |
| 	region.type = region_type;
 | |
| 
 | |
| 	if (region.size < 0)
 | |
| 		region.size = 0;
 | |
| 
 | |
| 	return region;
 | |
| }
 | |
| 
 | |
| static void set_region(struct frba *frba, unsigned int region_type,
 | |
| 		       const struct region *region)
 | |
| {
 | |
| 	if (region_type >= max_regions) {
 | |
| 		fprintf(stderr, "Invalid region type %u.\n", region_type);
 | |
| 		exit (EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	frba->flreg[region_type] =
 | |
| 		(((region->limit >> 12) & 0x7fff) << 16) |
 | |
| 		((region->base >> 12) & 0x7fff);
 | |
| }
 | |
| 
 | |
| static const char *region_name(unsigned int region_type)
 | |
| {
 | |
| 	if (region_type >= max_regions) {
 | |
| 		fprintf(stderr, "Invalid region type.\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	return region_names[region_type].pretty;
 | |
| }
 | |
| 
 | |
| static int region_num(const char *name)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 
 | |
| 	for (i = 0; i < max_regions; i++) {
 | |
| 		if (strcasecmp(name, region_names[i].pretty) == 0)
 | |
| 			return i;
 | |
| 		if (strcasecmp(name, region_names[i].terse) == 0)
 | |
| 			return i;
 | |
| 	}
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static void dump_region(unsigned int num, const struct frba *frba)
 | |
| {
 | |
| 	struct region region = get_region(frba, num);
 | |
| 	printf("  Flash Region %d (%s): %08x - %08x %s\n",
 | |
| 		num, region_name(num), region.base, region.limit,
 | |
| 		region.size < 1 ? "(unused)" : "");
 | |
| }
 | |
| 
 | |
| static int sort_compare(const void *a, const void *b)
 | |
| {
 | |
| 	return *(size_t *)a - *(size_t *)b;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * IFDv1 always has 8 regions, while IFDv2 always has 16 regions.
 | |
|  *
 | |
|  * It's platform specific which regions are used or are reserved.
 | |
|  * The 'SPI programming guide' as the name says is a guide only,
 | |
|  * not a specification what the hardware actually does.
 | |
|  * The best to do is not to rely on the guide, but detect how many
 | |
|  * regions are present in the IFD and expose them all.
 | |
|  *
 | |
|  * Very early IFDv2 chipsets, sometimes unofficially referred to as
 | |
|  * IFDv1.5 platforms, only have 8 regions. To not corrupt the IFD when
 | |
|  * operating on an IFDv1.5 detect how much space is actually present
 | |
|  * in the IFD.
 | |
|  */
 | |
| static int max_regions_from_fdbar(const struct fdbar *fdb)
 | |
| {
 | |
| 	const size_t fcba = (fdb->flmap0 & 0xff) << 4;
 | |
| 	const size_t fmba = (fdb->flmap1 & 0xff) << 4;
 | |
| 	const size_t frba = ((fdb->flmap0 >> 16) & 0xff) << 4;
 | |
| 	const size_t fpsba = ((fdb->flmap1 >> 16) & 0xff) << 4;
 | |
| 	const size_t flumap = 4096 - 256 - 4;
 | |
| 	size_t sorted[5] = {fcba, fmba, frba, fpsba, flumap};
 | |
| 
 | |
| 	qsort(sorted, ARRAY_SIZE(sorted), sizeof(size_t), sort_compare);
 | |
| 
 | |
| 	for (size_t i = 0; i < 4; i++) {
 | |
| 		/*
 | |
| 		 * Find FRBA in the sorted array and determine the size of the
 | |
| 		 * region by the start of the next region. Every region requires
 | |
| 		 * 4 bytes of space.
 | |
| 		 */
 | |
| 		if (sorted[i] == frba)
 | |
| 			return MIN((sorted[i + 1] - sorted[i]) / 4, MAX_REGIONS);
 | |
| 	}
 | |
| 	/* Never reaches this point */
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void dump_frba(const struct frba *frba)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	struct region region;
 | |
| 	printf("Found Region Section\n");
 | |
| 	for (i = 0; i < max_regions; i++) {
 | |
| 		region = get_region(frba, i);
 | |
| 		/* Skip unused & reserved Flash Region */
 | |
| 		if (region.size < 1 && !strcmp(region_name(i), "Reserved"))
 | |
| 			continue;
 | |
| 
 | |
| 		printf("FLREG%u:    0x%08x\n", i, frba->flreg[i]);
 | |
| 		dump_region(i, frba);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void dump_flashrom_layout(char *image, int size, const char *layout_fname)
 | |
| {
 | |
| 	const struct frba *frba = find_frba(image, size);
 | |
| 	if (!frba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	int layout_fd = open(layout_fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
 | |
| 	if (layout_fd == -1) {
 | |
| 		perror("Could not open file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	for (unsigned int i = 0; i < max_regions; i++) {
 | |
| 		struct region region = get_region(frba, i);
 | |
| 
 | |
| 		/* A region limit of 0 is an indicator of an unused region
 | |
| 		 * A region base of 7FFFh is an indicator of a reserved region
 | |
| 		 */
 | |
| 		if (region.limit == 0 || region.base == 0x07FFF000)
 | |
| 			continue;
 | |
| 
 | |
| 		char buf[LAYOUT_LINELEN];
 | |
| 		snprintf(buf, LAYOUT_LINELEN, "%08x:%08x %s\n", region.base, region.limit, region_names[i].terse);
 | |
| 		if (write(layout_fd, buf, strlen(buf)) < 0) {
 | |
| 			perror("Could not write to file");
 | |
| 			exit(EXIT_FAILURE);
 | |
| 		}
 | |
| 	}
 | |
| 	close(layout_fd);
 | |
| 	printf("Wrote layout to %s\n", layout_fname);
 | |
| }
 | |
| 
 | |
| static void _decode_spi_frequency(unsigned int freq)
 | |
| {
 | |
| 	switch (freq) {
 | |
| 	case SPI_FREQUENCY_20MHZ:
 | |
| 		printf("20MHz");
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_33MHZ:
 | |
| 		printf("33MHz");
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_48MHZ:
 | |
| 		printf("48MHz");
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_50MHZ_30MHZ:
 | |
| 		switch (ifd_version) {
 | |
| 		case IFD_VERSION_1:
 | |
| 		case IFD_VERSION_1_5:
 | |
| 			printf("50MHz");
 | |
| 			break;
 | |
| 		case IFD_VERSION_2:
 | |
| 			printf("30MHz");
 | |
| 			break;
 | |
| 		}
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_17MHZ:
 | |
| 		printf("17MHz");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("unknown<%x>MHz", freq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void _decode_spi_frequency_500_series(unsigned int freq)
 | |
| {
 | |
| 	switch (freq) {
 | |
| 	case SPI_FREQUENCY_100MHZ:
 | |
| 		printf("100MHz");
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_50MHZ:
 | |
| 		printf("50MHz");
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_500SERIES_33MHZ:
 | |
| 		printf("33MHz");
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_25MHZ:
 | |
| 		printf("25MHz");
 | |
| 		break;
 | |
| 	case SPI_FREQUENCY_14MHZ:
 | |
| 		printf("14MHz");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("unknown<%x>MHz", freq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void decode_spi_frequency(unsigned int freq)
 | |
| {
 | |
| 	switch (chipset) {
 | |
| 	case CHIPSET_500_600_SERIES_TIGER_ALDER_POINT:
 | |
| 	case CHIPSET_800_SERIES_METEOR_LAKE:
 | |
| 	case CHIPSET_900_SERIES_PANTHER_LAKE:
 | |
| 		_decode_spi_frequency_500_series(freq);
 | |
| 		break;
 | |
| 	default:
 | |
| 		_decode_spi_frequency(freq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void _decode_espi_frequency(unsigned int freq)
 | |
| {
 | |
| 	switch (freq) {
 | |
| 	case ESPI_FREQUENCY_20MHZ:
 | |
| 		printf("20MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_24MHZ:
 | |
| 		printf("24MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_30MHZ:
 | |
| 		printf("30MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_48MHZ:
 | |
| 		printf("48MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_60MHZ:
 | |
| 		printf("60MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_17MHZ:
 | |
| 		printf("17MHz");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("unknown<%x>MHz", freq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void _decode_espi_frequency_500_series(unsigned int freq)
 | |
| {
 | |
| 	switch (freq) {
 | |
| 	case ESPI_FREQUENCY_500SERIES_20MHZ:
 | |
| 		printf("20MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_500SERIES_24MHZ:
 | |
| 		printf("24MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_500SERIES_25MHZ:
 | |
| 		printf("25MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_500SERIES_48MHZ:
 | |
| 		printf("48MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_500SERIES_60MHZ:
 | |
| 		printf("60MHz");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("unknown<%x>MHz", freq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void _decode_espi_frequency_800_series(unsigned int freq)
 | |
| {
 | |
| 	switch (freq) {
 | |
| 	case ESPI_FREQUENCY_800SERIES_20MHZ:
 | |
| 		printf("20MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_800SERIES_25MHZ:
 | |
| 		printf("25MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_800SERIES_33MHZ:
 | |
| 		printf("33MHz");
 | |
| 		break;
 | |
| 	case ESPI_FREQUENCY_800SERIES_50MHZ:
 | |
| 		printf("50MHz");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("unknown<%x>MHz", freq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void decode_espi_frequency(unsigned int freq)
 | |
| {
 | |
| 	switch (chipset) {
 | |
| 	case CHIPSET_500_600_SERIES_TIGER_ALDER_POINT:
 | |
| 		_decode_espi_frequency_500_series(freq);
 | |
| 		break;
 | |
| 	case CHIPSET_800_SERIES_METEOR_LAKE:
 | |
| 	case CHIPSET_900_SERIES_PANTHER_LAKE:
 | |
| 		_decode_espi_frequency_800_series(freq);
 | |
| 		break;
 | |
| 	default:
 | |
| 		_decode_espi_frequency(freq);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void decode_component_density(unsigned int density)
 | |
| {
 | |
| 	switch (density) {
 | |
| 	case COMPONENT_DENSITY_512KB:
 | |
| 		printf("512KB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_1MB:
 | |
| 		printf("1MB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_2MB:
 | |
| 		printf("2MB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_4MB:
 | |
| 		printf("4MB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_8MB:
 | |
| 		printf("8MB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_16MB:
 | |
| 		printf("16MB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_32MB:
 | |
| 		printf("32MB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_64MB:
 | |
| 		printf("64MB");
 | |
| 		break;
 | |
| 	case COMPONENT_DENSITY_UNUSED:
 | |
| 		printf("UNUSED");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("unknown<%x>MB", density);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int is_platform_with_pch(void)
 | |
| {
 | |
| 	if (chipset >= CHIPSET_5_SERIES_IBEX_PEAK)
 | |
| 		return 1;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* FLMAP0 register bit 24 onwards are reserved from SPT PCH */
 | |
| static int is_platform_with_100x_series_pch(void)
 | |
| {
 | |
| 	if (chipset >= CHIPSET_100_200_SERIES_SUNRISE_POINT &&
 | |
| 			chipset <= CHIPSET_900_SERIES_PANTHER_LAKE)
 | |
| 		return 1;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void dump_fcba(const struct fcba *fcba, const struct fpsba *fpsba)
 | |
| {
 | |
| 	unsigned int freq;
 | |
| 
 | |
| 	printf("\nFound Component Section\n");
 | |
| 	printf("FLCOMP     0x%08x\n", fcba->flcomp);
 | |
| 	printf("  Dual Output Fast Read Support:       %ssupported\n",
 | |
| 		(fcba->flcomp & (1 << 30)) ? "" : "not ");
 | |
| 	printf("  Read ID/Read Status Clock Frequency: ");
 | |
| 	decode_spi_frequency((fcba->flcomp >> 27) & 7);
 | |
| 	printf("\n  Write/Erase Clock Frequency:         ");
 | |
| 	decode_spi_frequency((fcba->flcomp >> 24) & 7);
 | |
| 	printf("\n  Fast Read Clock Frequency:           ");
 | |
| 	decode_spi_frequency((fcba->flcomp >> 21) & 7);
 | |
| 	printf("\n  Fast Read Support:                   %ssupported",
 | |
| 		(fcba->flcomp & (1 << 20)) ? "" : "not ");
 | |
| 	if (is_platform_with_100x_series_pch() &&
 | |
| 			chipset != CHIPSET_100_200_SERIES_SUNRISE_POINT) {
 | |
| 		printf("\n  Read eSPI/EC Bus Frequency:          ");
 | |
| 		if (chipset == CHIPSET_500_600_SERIES_TIGER_ALDER_POINT)
 | |
| 			freq = (fpsba->pchstrp[22] & 0x38) >> 3;
 | |
| 		else if (chipset == CHIPSET_800_SERIES_METEOR_LAKE)
 | |
| 			freq = (fpsba->pchstrp[65] & 0x38) >> 3;
 | |
| 		else if (chipset == CHIPSET_900_SERIES_PANTHER_LAKE)
 | |
| 			freq = (fpsba->pchstrp[119] & 0x38) >> 3;
 | |
| 		else
 | |
| 			freq = (fcba->flcomp >> 17) & 7;
 | |
| 		decode_espi_frequency(freq);
 | |
| 
 | |
| 		printf("\n  Quad I/O Read:                       %s",
 | |
| 			(fcba->flcomp & (1 << 15)) ? "enabled" : "disabled");
 | |
| 		printf("\n  Quad Output Read:                    %s",
 | |
| 			(fcba->flcomp & (1 << 14)) ? "enabled" : "disabled");
 | |
| 		printf("\n  Dual I/O Read:                       %s",
 | |
| 			(fcba->flcomp & (1 << 13)) ? "enabled" : "disabled");
 | |
| 		printf("\n  Dual Output Read:                    %s",
 | |
| 			(fcba->flcomp & (1 << 12)) ? "enabled" : "disabled");
 | |
| 	} else {
 | |
| 		printf("\n  Read Clock Frequency:                ");
 | |
| 		decode_spi_frequency((fcba->flcomp >> 17) & 7);
 | |
| 	}
 | |
| 
 | |
| 	switch (ifd_version) {
 | |
| 	case IFD_VERSION_1:
 | |
| 		printf("\n  Component 2 Density:                 ");
 | |
| 		decode_component_density((fcba->flcomp >> 3) & 7);
 | |
| 		printf("\n  Component 1 Density:                 ");
 | |
| 		decode_component_density(fcba->flcomp & 7);
 | |
| 		break;
 | |
| 	case IFD_VERSION_1_5:
 | |
| 	case IFD_VERSION_2:
 | |
| 		printf("\n  Component 2 Density:                 ");
 | |
| 		decode_component_density((fcba->flcomp >> 4) & 0xf);
 | |
| 		printf("\n  Component 1 Density:                 ");
 | |
| 		decode_component_density(fcba->flcomp & 0xf);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	printf("\n");
 | |
| 	printf("FLILL      0x%08x\n", fcba->flill);
 | |
| 	printf("  Invalid Instruction 3: 0x%02x\n",
 | |
| 		(fcba->flill >> 24) & 0xff);
 | |
| 	printf("  Invalid Instruction 2: 0x%02x\n",
 | |
| 		(fcba->flill >> 16) & 0xff);
 | |
| 	printf("  Invalid Instruction 1: 0x%02x\n",
 | |
| 		(fcba->flill >> 8) & 0xff);
 | |
| 	printf("  Invalid Instruction 0: 0x%02x\n",
 | |
| 		fcba->flill & 0xff);
 | |
| 	if (is_platform_with_100x_series_pch()) {
 | |
| 		printf("FLILL1     0x%08x\n", fcba->flpb);
 | |
| 		printf("  Invalid Instruction 7: 0x%02x\n",
 | |
| 			(fcba->flpb >> 24) & 0xff);
 | |
| 		printf("  Invalid Instruction 6: 0x%02x\n",
 | |
| 			(fcba->flpb >> 16) & 0xff);
 | |
| 		printf("  Invalid Instruction 5: 0x%02x\n",
 | |
| 			(fcba->flpb >> 8) & 0xff);
 | |
| 		printf("  Invalid Instruction 4: 0x%02x\n",
 | |
| 			fcba->flpb & 0xff);
 | |
| 	} else {
 | |
| 		printf("FLPB       0x%08x\n", fcba->flpb);
 | |
| 		printf("  Flash Partition Boundary Address: 0x%06x\n\n",
 | |
| 				(fcba->flpb & 0xfff) << 12);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void dump_fpsba(const struct fdbar *fdb, const struct fpsba *fpsba)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	/* SoC Straps, aka PSL, aka ISL */
 | |
| 	unsigned int SS = (fdb->flmap1 >> 24) & 0xff;
 | |
| 
 | |
| 	printf("Found PCH Strap Section\n");
 | |
| 	for (i = 0; i < SS; i++)
 | |
| 		printf("PCHSTRP%-3u: 0x%08x\n", i, fpsba->pchstrp[i]);
 | |
| 
 | |
| 	if (ifd_version >= IFD_VERSION_2) {
 | |
| 		printf("HAP bit is %sset\n",
 | |
| 		       fpsba->pchstrp[0] & (1 << 16) ? "" : "not ");
 | |
| 	} else if (chipset >= CHIPSET_ICH8 && chipset <= CHIPSET_ICH10) {
 | |
| 		printf("ICH_MeDisable bit is %sset\n",
 | |
| 		       fpsba->pchstrp[0] & 1 ? "" : "not ");
 | |
| 	} else {
 | |
| 		printf("AltMeDisable bit is %sset\n",
 | |
| 		       fpsba->pchstrp[10] & (1 << 7) ? "" : "not ");
 | |
| 	}
 | |
| 
 | |
| 	printf("\n");
 | |
| }
 | |
| 
 | |
| static void decode_flmstr(uint32_t flmstr)
 | |
| {
 | |
| 	int wr_shift, rd_shift;
 | |
| 	if (ifd_version >= IFD_VERSION_2) {
 | |
| 		wr_shift = FLMSTR_WR_SHIFT_V2;
 | |
| 		rd_shift = FLMSTR_RD_SHIFT_V2;
 | |
| 	} else {
 | |
| 		wr_shift = FLMSTR_WR_SHIFT_V1;
 | |
| 		rd_shift = FLMSTR_RD_SHIFT_V1;
 | |
| 	}
 | |
| 
 | |
| 	/* EC region access only available on v2+ */
 | |
| 	if (PLATFORM_HAS_EC_REGION)
 | |
| 		printf("  EC Region Write Access:            %s\n",
 | |
| 			(flmstr & (1 << (wr_shift + 8))) ?
 | |
| 			"enabled" : "disabled");
 | |
| 	printf("  Platform Data Region Write Access: %s\n",
 | |
| 			(flmstr & (1 << (wr_shift + 4))) ? "enabled" : "disabled");
 | |
| 	if (PLATFORM_HAS_GBE_REGION) {
 | |
| 		printf("  GbE Region Write Access:           %s\n",
 | |
| 			(flmstr & (1 << (wr_shift + 3))) ? "enabled" : "disabled");
 | |
| 	}
 | |
| 	printf("  Intel ME Region Write Access:      %s\n",
 | |
| 		(flmstr & (1 << (wr_shift + 2))) ? "enabled" : "disabled");
 | |
| 	printf("  Host CPU/BIOS Region Write Access: %s\n",
 | |
| 		(flmstr & (1 << (wr_shift + 1))) ? "enabled" : "disabled");
 | |
| 	printf("  Flash Descriptor Write Access:     %s\n",
 | |
| 		(flmstr & (1 << wr_shift)) ? "enabled" : "disabled");
 | |
| 	if (PLATFORM_HAS_10GBE_0_REGION) {
 | |
| 		printf("  10GbE_0 Write Access:               %s\n",
 | |
| 			(flmstr & (1 << (wr_shift + 11))) ? "enabled" : "disabled");
 | |
| 	}
 | |
| 	if (PLATFORM_HAS_10GBE_1_REGION) {
 | |
| 		printf("  10GbE_1 Write Access:               %s\n",
 | |
| 			(flmstr & (1 << 4)) ? "enabled" : "disabled");
 | |
| 	}
 | |
| 
 | |
| 	if (PLATFORM_HAS_EC_REGION)
 | |
| 		printf("  EC Region Read Access:             %s\n",
 | |
| 			(flmstr & (1 << (rd_shift + 8))) ?
 | |
| 			"enabled" : "disabled");
 | |
| 	printf("  Platform Data Region Read Access:  %s\n",
 | |
| 		(flmstr & (1 << (rd_shift + 4))) ? "enabled" : "disabled");
 | |
| 	if (PLATFORM_HAS_GBE_REGION) {
 | |
| 		printf("  GbE Region Read Access:            %s\n",
 | |
| 			(flmstr & (1 << (rd_shift + 3))) ? "enabled" : "disabled");
 | |
| 	}
 | |
| 	printf("  Intel ME Region Read Access:       %s\n",
 | |
| 		(flmstr & (1 << (rd_shift + 2))) ? "enabled" : "disabled");
 | |
| 	printf("  Host CPU/BIOS Region Read Access:  %s\n",
 | |
| 		(flmstr & (1 << (rd_shift + 1))) ? "enabled" : "disabled");
 | |
| 	printf("  Flash Descriptor Read Access:      %s\n",
 | |
| 		(flmstr & (1 << rd_shift)) ? "enabled" : "disabled");
 | |
| 	if (PLATFORM_HAS_10GBE_0_REGION) {
 | |
| 		printf("  10GbE_0 Read Access:                %s\n",
 | |
| 			(flmstr & (1 << (rd_shift + 11))) ? "enabled" : "disabled");
 | |
| 	}
 | |
| 	if (PLATFORM_HAS_10GBE_1_REGION) {
 | |
| 		printf("  10GbE_1 Read Access:                %s\n",
 | |
| 			(flmstr & (1 << 0)) ? "enabled" : "disabled");
 | |
| 	}
 | |
| 
 | |
| 	/* Requestor ID doesn't exist for ifd 2 */
 | |
| 	if (ifd_version < IFD_VERSION_2)
 | |
| 		printf("  Requester ID:                      0x%04x\n\n",
 | |
| 			flmstr & 0xffff);
 | |
| }
 | |
| 
 | |
| static void dump_fmba(const struct fmba *fmba)
 | |
| {
 | |
| 	printf("Found Master Section\n");
 | |
| 	printf("FLMSTR1:   0x%08x (Host CPU/BIOS)\n", fmba->flmstr1);
 | |
| 	decode_flmstr(fmba->flmstr1);
 | |
| 	printf("FLMSTR2:   0x%08x (Intel ME)\n", fmba->flmstr2);
 | |
| 	decode_flmstr(fmba->flmstr2);
 | |
| 	if (PLATFORM_HAS_GBE_REGION) {
 | |
| 		printf("FLMSTR3:   0x%08x (GbE)\n", fmba->flmstr3);
 | |
| 		decode_flmstr(fmba->flmstr3);
 | |
| 		if (ifd_version >= IFD_VERSION_2) {
 | |
| 			printf("FLMSTR5:   0x%08x (EC)\n", fmba->flmstr5);
 | |
| 			decode_flmstr(fmba->flmstr5);
 | |
| 		}
 | |
| 	} else {
 | |
| 		printf("FLMSTR6:  0x%08x (IE)\n", fmba->flmstr6);
 | |
| 		decode_flmstr(fmba->flmstr6);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void dump_fmsba(const struct fmsba *fmsba)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	printf("Found Processor Strap Section\n");
 | |
| 	for (i = 0; i < ARRAY_SIZE(fmsba->data); i++)
 | |
| 		printf("????:      0x%08x\n", fmsba->data[i]);
 | |
| 
 | |
| 	if (chipset >= CHIPSET_ICH8 && chipset <= CHIPSET_ICH10) {
 | |
| 		printf("MCH_MeDisable bit is %sset\n",
 | |
| 			fmsba->data[0] & 1 ? "" : "not ");
 | |
| 		printf("MCH_AltMeDisable bit is %sset\n",
 | |
| 			fmsba->data[0] & (1 << 7) ? "" : "not ");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void dump_jid(uint32_t jid)
 | |
| {
 | |
| 	printf("    SPI Component Vendor ID:            0x%02x\n",
 | |
| 		jid & 0xff);
 | |
| 	printf("    SPI Component Device ID 0:          0x%02x\n",
 | |
| 		(jid >> 8) & 0xff);
 | |
| 	printf("    SPI Component Device ID 1:          0x%02x\n",
 | |
| 		(jid >> 16) & 0xff);
 | |
| }
 | |
| 
 | |
| static void dump_vscc(uint32_t vscc)
 | |
| {
 | |
| 	printf("    Lower Erase Opcode:                 0x%02x\n",
 | |
| 		vscc >> 24);
 | |
| 	printf("    Lower Write Enable on Write Status: 0x%02x\n",
 | |
| 		vscc & (1 << 20) ? 0x06 : 0x50);
 | |
| 	printf("    Lower Write Status Required:        %s\n",
 | |
| 		vscc & (1 << 19) ? "Yes" : "No");
 | |
| 	printf("    Lower Write Granularity:            %d bytes\n",
 | |
| 		vscc & (1 << 18) ? 64 : 1);
 | |
| 	printf("    Lower Block / Sector Erase Size:    ");
 | |
| 	switch ((vscc >> 16) & 0x3) {
 | |
| 	case 0:
 | |
| 		printf("256 Byte\n");
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		printf("4KB\n");
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		printf("8KB\n");
 | |
| 		break;
 | |
| 	case 3:
 | |
| 		printf("64KB\n");
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	printf("    Upper Erase Opcode:                 0x%02x\n",
 | |
| 		(vscc >> 8) & 0xff);
 | |
| 	printf("    Upper Write Enable on Write Status: 0x%02x\n",
 | |
| 		vscc & (1 << 4) ? 0x06 : 0x50);
 | |
| 	printf("    Upper Write Status Required:        %s\n",
 | |
| 		vscc & (1 << 3) ? "Yes" : "No");
 | |
| 	printf("    Upper Write Granularity:            %d bytes\n",
 | |
| 		vscc & (1 << 2) ? 64 : 1);
 | |
| 	printf("    Upper Block / Sector Erase Size:    ");
 | |
| 	switch (vscc & 0x3) {
 | |
| 	case 0:
 | |
| 		printf("256 Byte\n");
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		printf("4KB\n");
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		printf("8KB\n");
 | |
| 		break;
 | |
| 	case 3:
 | |
| 		printf("64KB\n");
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void dump_vtba(const struct vtba *vtba, int vtl)
 | |
| {
 | |
| 	int i;
 | |
| 	int max_len = sizeof(struct vtba) / sizeof(struct vscc);
 | |
| 	int num = (vtl >> 1) < max_len ? (vtl >> 1) : max_len;
 | |
| 
 | |
| 	printf("ME VSCC table:\n");
 | |
| 	for (i = 0; i < num; i++) {
 | |
| 		printf("  JID%d:  0x%08x\n", i, vtba->entry[i].jid);
 | |
| 		dump_jid(vtba->entry[i].jid);
 | |
| 		printf("  VSCC%d: 0x%08x\n", i, vtba->entry[i].vscc);
 | |
| 		dump_vscc(vtba->entry[i].vscc);
 | |
| 	}
 | |
| 	printf("\n");
 | |
| }
 | |
| 
 | |
| static void dump_oem(const uint8_t *oem)
 | |
| {
 | |
| 	int i, j;
 | |
| 	printf("OEM Section:\n");
 | |
| 	for (i = 0; i < 4; i++) {
 | |
| 		printf("%02x:", i << 4);
 | |
| 		for (j = 0; j < 16; j++)
 | |
| 			printf(" %02x", oem[(i << 4) + j]);
 | |
| 		printf("\n");
 | |
| 	}
 | |
| 	printf("\n");
 | |
| }
 | |
| 
 | |
| static void dump_fd(char *image, int size)
 | |
| {
 | |
| 	const struct fdbar *fdb = find_fd(image, size);
 | |
| 	if (!fdb)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	printf("%s", is_platform_with_pch() ? "PCH" : "ICH");
 | |
| 	printf(" Revision: %s\n", ich_chipset_names[chipset]);
 | |
| 	printf("FLMAP0:    0x%08x\n", fdb->flmap0);
 | |
| 	if (!is_platform_with_100x_series_pch())
 | |
| 		printf("  NR:      %d\n", (fdb->flmap0 >> 24) & 7);
 | |
| 	printf("  FRBA:    0x%x\n", ((fdb->flmap0 >> 16) & 0xff) << 4);
 | |
| 	printf("  NC:      %d\n", ((fdb->flmap0 >> 8) & 3) + 1);
 | |
| 	printf("  FCBA:    0x%x\n", ((fdb->flmap0) & 0xff) << 4);
 | |
| 
 | |
| 	printf("FLMAP1:    0x%08x\n", fdb->flmap1);
 | |
| 	printf("  %s:     ", is_platform_with_100x_series_pch() ? "PSL" : "ISL");
 | |
| 	printf("0x%02x\n", (fdb->flmap1 >> 24) & 0xff);
 | |
| 	printf("  FPSBA:   0x%x\n", ((fdb->flmap1 >> 16) & 0xff) << 4);
 | |
| 	printf("  NM:      %d\n", (fdb->flmap1 >> 8) & 3);
 | |
| 	printf("  FMBA:    0x%x\n", ((fdb->flmap1) & 0xff) << 4);
 | |
| 
 | |
| 	if (!is_platform_with_100x_series_pch()) {
 | |
| 		printf("FLMAP2:    0x%08x\n", fdb->flmap2);
 | |
| 		printf("  PSL:     0x%04x\n", (fdb->flmap2 >> 8) & 0xffff);
 | |
| 		printf("  FMSBA:   0x%x\n", ((fdb->flmap2) & 0xff) << 4);
 | |
| 	}
 | |
| 
 | |
| 	if (chipset == CHIPSET_500_600_SERIES_TIGER_ALDER_POINT ||
 | |
| 		 chipset == CHIPSET_800_SERIES_METEOR_LAKE ||
 | |
| 		 chipset == CHIPSET_900_SERIES_PANTHER_LAKE) {
 | |
| 		printf("FLMAP3:    0x%08x\n", fdb->flmap3);
 | |
| 		printf("  Minor Revision ID:     0x%04x\n", (fdb->flmap3 >> 14) & 0x7f);
 | |
| 		printf("  Major Revision ID:     0x%04x\n", (fdb->flmap3 >> 21) & 0x7ff);
 | |
| 	}
 | |
| 
 | |
| 	char *flumap = find_flumap(image, size);
 | |
| 	uint32_t flumap1 = *(uint32_t *)flumap;
 | |
| 	printf("FLUMAP1:   0x%08x\n", flumap1);
 | |
| 	printf("  Intel ME VSCC Table Length (VTL):        %d\n",
 | |
| 		(flumap1 >> 8) & 0xff);
 | |
| 	printf("  Intel ME VSCC Table Base Address (VTBA): 0x%06x\n\n",
 | |
| 		(flumap1 & 0xff) << 4);
 | |
| 	dump_vtba((struct vtba *)
 | |
| 			(image + ((flumap1 & 0xff) << 4)),
 | |
| 			(flumap1 >> 8) & 0xff);
 | |
| 	dump_oem((const uint8_t *)image + 0xf00);
 | |
| 
 | |
| 	const struct frba *frba = find_frba(image, size);
 | |
| 	const struct fcba *fcba = find_fcba(image, size);
 | |
| 	const struct fpsba *fpsba = find_fpsba(image, size);
 | |
| 	const struct fmba *fmba = find_fmba(image, size);
 | |
| 	const struct fmsba *fmsba = find_fmsba(image, size);
 | |
| 
 | |
| 	if (frba && fcba && fpsba && fmba && fmsba) {
 | |
| 		dump_frba(frba);
 | |
| 		dump_fcba(fcba, fpsba);
 | |
| 		dump_fpsba(fdb, fpsba);
 | |
| 		dump_fmba(fmba);
 | |
| 		dump_fmsba(fmsba);
 | |
| 	} else {
 | |
| 		printf("FD is corrupted!\n");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Takes an image containing an IFD and creates a Flashmap .fmd file template.
 | |
|  * This flashmap will contain all IFD regions except the BIOS region.
 | |
|  * The BIOS region is created by coreboot itself and 'should' match the IFD region
 | |
|  * anyway (CONFIG_VALIDATE_INTEL_DESCRIPTOR should make sure). coreboot built system will use
 | |
|  * this template to generate the final Flashmap file.
 | |
|  */
 | |
| static void create_fmap_template(char *image, int size, const char *layout_fname)
 | |
| {
 | |
| 	const struct frba *frba = find_frba(image, size);
 | |
| 	if (!frba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	int layout_fd = open(layout_fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
 | |
| 	if (layout_fd == -1) {
 | |
| 		perror("Could not open file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	char *bbuf = "FLASH@##ROM_BASE## ##ROM_SIZE## {\n";
 | |
| 	if (write(layout_fd, bbuf, strlen(bbuf)) < 0) {
 | |
| 		perror("Could not write to file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	/* fmaptool requires regions in .fmd to be sorted.
 | |
| 	 * => We need to sort the regions by base address before writing them in .fmd File
 | |
| 	 */
 | |
| 	int count_regions = 0;
 | |
| 	struct region sorted_regions[MAX_REGIONS] = { 0 };
 | |
| 	for (unsigned int i = 0; i < max_regions; i++) {
 | |
| 		struct region region = get_region(frba, i);
 | |
| 
 | |
| 		/* A region limit of 0 is an indicator of an unused region
 | |
| 		 * A region base of 7FFFh is an indicator of a reserved region
 | |
| 		 */
 | |
| 		if (region.limit == 0 || region.base == 0x07FFF000)
 | |
| 			continue;
 | |
| 
 | |
| 		/* Is there an FMAP equivalent? IFD reserved regions are usually thrown out
 | |
| 		 * of the FMAP here
 | |
| 		 */
 | |
| 		if (!region_names[region.type].fmapname) {
 | |
| 			printf("Skip IFD region: %s\n", region_names[region.type].pretty);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Here we decide to use the coreboot generated FMAP BIOS region, instead of
 | |
| 		 * the one specified in the IFD. The case when IFD and FMAP BIOS region do not
 | |
| 		 * match cannot be caught here, therefore one should still validate IFD and
 | |
| 		 * FMAP via CONFIG_VALIDATE_INTEL_DESCRIPTOR
 | |
| 		 */
 | |
| 		if (i == REGION_BIOS)
 | |
| 			continue;
 | |
| 
 | |
| 		sorted_regions[count_regions] = region;
 | |
| 		// basically insertion sort
 | |
| 		for (int i = count_regions - 1; i >= 0; i--) {
 | |
| 			if (sorted_regions[i].base > sorted_regions[i + 1].base) {
 | |
| 				struct region tmp = sorted_regions[i];
 | |
| 				sorted_regions[i] = sorted_regions[i + 1];
 | |
| 				sorted_regions[i + 1] = tmp;
 | |
| 			}
 | |
| 		}
 | |
| 		count_regions++;
 | |
| 	}
 | |
| 
 | |
| 	// Now write regions sorted by base address in the fmap file
 | |
| 	for (int i = 0; i < count_regions; i++) {
 | |
| 		struct region region = sorted_regions[i];
 | |
| 		char buf[LAYOUT_LINELEN];
 | |
| 		snprintf(buf, LAYOUT_LINELEN, "\t%s@0x%X 0x%X\n", region_names[region.type].fmapname, region.base, region.size);
 | |
| 		if (write(layout_fd, buf, strlen(buf)) < 0) {
 | |
| 			perror("Could not write to file");
 | |
| 			exit(EXIT_FAILURE);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	char *ebuf = "\tSI_BIOS@##BIOS_BASE## ##BIOS_SIZE## {\n"
 | |
| 		     "\t\t##CONSOLE_ENTRY##\n"
 | |
| 		     "\t\t##MRC_CACHE_ENTRY##\n"
 | |
| 		     "\t\t##SMMSTORE_ENTRY##\n"
 | |
| 		     "\t\t##SPD_CACHE_ENTRY##\n"
 | |
| 		     "\t\t##VPD_ENTRY##\n"
 | |
| 		     "\t\tFMAP@##FMAP_BASE## ##FMAP_SIZE##\n"
 | |
| 		     "\t\tCOREBOOT(CBFS)@##CBFS_BASE## ##CBFS_SIZE##\n"
 | |
| 		     "\t}\n"
 | |
| 		     "}\n";
 | |
| 	if (write(layout_fd, ebuf, strlen(ebuf)) < 0) {
 | |
| 		perror("Could not write to file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	close(layout_fd);
 | |
| 	printf("Wrote layout to %s\n", layout_fname);
 | |
| }
 | |
| 
 | |
| static void write_regions(char *image, int size)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	const struct frba *frba = find_frba(image, size);
 | |
| 
 | |
| 	if (!frba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	for (i = 0; i < max_regions; i++) {
 | |
| 		struct region region = get_region(frba, i);
 | |
| 		dump_region(i, frba);
 | |
| 		if (region.size > 0) {
 | |
| 			int region_fd;
 | |
| 			region_fd = open(region_names[i].filename,
 | |
| 					 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
 | |
| 					 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 | |
| 			if (region_fd < 0) {
 | |
| 				perror("Error while trying to open file");
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			if (write(region_fd, image + region.base, region.size) != region.size)
 | |
| 				perror("Error while writing");
 | |
| 			close(region_fd);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void validate_layout(char *image, int size)
 | |
| {
 | |
| 	uint i, errors = 0;
 | |
| 	struct fmap *fmap;
 | |
| 	long int fmap_loc = fmap_find((uint8_t *)image, size);
 | |
| 	const struct frba *frba = find_frba(image, size);
 | |
| 
 | |
| 	if (fmap_loc < 0 || !frba) {
 | |
| 		printf("Could not find FMAP (%p) or Intel Flash Descriptor (%p)\n",
 | |
| 			(void *)fmap_loc, frba);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	fmap = (struct fmap *)(image + fmap_loc);
 | |
| 
 | |
| 	int matches = 0;
 | |
| 	for (i = 0; i < max_regions; i++) {
 | |
| 		struct region region = get_region(frba, i);
 | |
| 		if (region.size == 0)
 | |
| 			continue;
 | |
| 
 | |
| 		const struct fmap_area *area = fmap_find_area(fmap, region_names[i].fmapname);
 | |
| 		if (!area)
 | |
| 			continue;
 | |
| 
 | |
| 		matches++; // found a match between FMAP and IFD region
 | |
| 
 | |
| 		if ((uint)region.base != area->offset || (uint)region.size != area->size) {
 | |
| 			printf("Region mismatch between %s and %s\n", region_names[i].terse, area->name);
 | |
| 			printf(" Descriptor region %s:\n", region_names[i].terse);
 | |
| 			printf("  offset: 0x%08x\n", region.base);
 | |
| 			printf("  length: 0x%08x\n", region.size);
 | |
| 			printf(" FMAP area %s:\n", area->name);
 | |
| 			printf("  offset: 0x%08x\n", area->offset);
 | |
| 			printf("  length: 0x%08x\n", area->size);
 | |
| 			errors++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!matches) {
 | |
| 		// At least a BIOS region should be present in both IFD and FMAP
 | |
| 		fprintf(stderr, "Warning: Not a single IFD region found in FMAP\n");
 | |
| 	}
 | |
| 
 | |
| 	if (errors > 0)
 | |
| 		exit(EXIT_FAILURE);
 | |
| }
 | |
| 
 | |
| static void write_image(const char *filename, char *image, int size)
 | |
| {
 | |
| 	int new_fd;
 | |
| 	printf("Writing new image to %s\n", filename);
 | |
| 
 | |
| 	// Now write out new image
 | |
| 	new_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
 | |
| 	if (new_fd < 0) {
 | |
| 		perror("Error while trying to open file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	if (write(new_fd, image, size) != size)
 | |
| 		perror("Error while writing");
 | |
| 	close(new_fd);
 | |
| }
 | |
| 
 | |
| static void set_spi_frequency(const char *filename, char *image, int size,
 | |
| 			      enum spi_frequency freq)
 | |
| {
 | |
| 	struct fcba *fcba = find_fcba(image, size);
 | |
| 	if (!fcba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	/* clear bits 21-29 */
 | |
| 	fcba->flcomp &= ~0x3fe00000;
 | |
| 	/* Read ID and Read Status Clock Frequency */
 | |
| 	fcba->flcomp |= freq << 27;
 | |
| 	/* Write and Erase Clock Frequency */
 | |
| 	fcba->flcomp |= freq << 24;
 | |
| 	/* Fast Read Clock Frequency */
 | |
| 	fcba->flcomp |= freq << 21;
 | |
| 
 | |
| 	write_image(filename, image, size);
 | |
| }
 | |
| 
 | |
| static void set_em100_mode(const char *filename, char *image, int size)
 | |
| {
 | |
| 	struct fcba *fcba = find_fcba(image, size);
 | |
| 	if (!fcba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	int freq;
 | |
| 
 | |
| 	switch (ifd_version) {
 | |
| 	case IFD_VERSION_1:
 | |
| 	case IFD_VERSION_1_5:
 | |
| 		freq = SPI_FREQUENCY_20MHZ;
 | |
| 		break;
 | |
| 	case IFD_VERSION_2:
 | |
| 		freq = SPI_FREQUENCY_17MHZ;
 | |
| 		break;
 | |
| 	default:
 | |
| 		freq = SPI_FREQUENCY_17MHZ;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	fcba->flcomp &= ~(1 << 30);
 | |
| 	set_spi_frequency(filename, image, size, freq);
 | |
| }
 | |
| 
 | |
| static void set_chipdensity(const char *filename, char *image, int size,
 | |
| 			    unsigned int density)
 | |
| {
 | |
| 	struct fcba *fcba = find_fcba(image, size);
 | |
| 	uint8_t mask, chip2_offset;
 | |
| 	if (!fcba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	printf("Setting chip density to ");
 | |
| 	decode_component_density(density);
 | |
| 	printf("\n");
 | |
| 
 | |
| 	switch (ifd_version) {
 | |
| 	case IFD_VERSION_1:
 | |
| 		/* fail if selected density is not supported by this version */
 | |
| 		if ( (density == COMPONENT_DENSITY_32MB) ||
 | |
| 		     (density == COMPONENT_DENSITY_64MB) ||
 | |
| 		     (density == COMPONENT_DENSITY_UNUSED) ) {
 | |
| 			printf("error: Selected density not supported in IFD version 1.\n");
 | |
| 			exit(EXIT_FAILURE);
 | |
| 		}
 | |
| 		mask = 0x7;
 | |
| 		chip2_offset = 3;
 | |
| 		break;
 | |
| 	case IFD_VERSION_1_5:
 | |
| 	case IFD_VERSION_2:
 | |
| 		mask = 0xf;
 | |
| 		chip2_offset = 4;
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("error: Unknown IFD version\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* clear chip density for corresponding chip */
 | |
| 	switch (selected_chip) {
 | |
| 	case 1:
 | |
| 		fcba->flcomp &= ~mask;
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		fcba->flcomp &= ~(mask << chip2_offset);
 | |
| 		break;
 | |
| 	default: /*both chips*/
 | |
| 		fcba->flcomp &= ~(mask | (mask << chip2_offset));
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* set the new density */
 | |
| 	if (selected_chip == 1 || selected_chip == 0)
 | |
| 		fcba->flcomp |= (density); /* first chip */
 | |
| 	if (selected_chip == 2 || selected_chip == 0)
 | |
| 		fcba->flcomp |= (density << chip2_offset); /* second chip */
 | |
| 
 | |
| 	write_image(filename, image, size);
 | |
| }
 | |
| 
 | |
| static int check_region(const struct frba *frba, unsigned int region_type)
 | |
| {
 | |
| 	struct region region;
 | |
| 
 | |
| 	if (!frba)
 | |
| 		return 0;
 | |
| 
 | |
| 	region = get_region(frba, region_type);
 | |
| 	return !!((region.base < region.limit) && (region.size > 0));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Platforms from CNL onwards support up to 16 flash regions, not 12. The
 | |
|  * permissions for regions [15:12] are stored in extended region read/write
 | |
|  * access fields in the FLMSTR registers.
 | |
|  *
 | |
|  * FLMSTR with extended regions:
 | |
|  *   31:20 Region Write Access
 | |
|  *   19:8  Region Read Access
 | |
|  *    7:4  Extended Region Write Access
 | |
|  *    3:0  Extended Region Read Access
 | |
|  *
 | |
|  * FLMSTR without extended regions:
 | |
|  *   31:20 Region Write Access
 | |
|  *   19:8  Region Read Access
 | |
|  *    7:0  Reserved
 | |
|  */
 | |
| static bool platform_has_extended_regions(void)
 | |
| {
 | |
| 	switch (platform) {
 | |
| 	case PLATFORM_CNL:
 | |
| 	case PLATFORM_JSL:
 | |
| 	case PLATFORM_TGL:
 | |
| 	case PLATFORM_ADL:
 | |
| 	case PLATFORM_MTL:
 | |
| 	case PLATFORM_PTL:
 | |
| 		return true;
 | |
| 	default:
 | |
| 		return false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void lock_descriptor(const char *filename, char *image, int size)
 | |
| {
 | |
| 	int wr_shift, rd_shift;
 | |
| 	struct fmba *fmba = find_fmba(image, size);
 | |
| 	const struct frba *frba = find_frba(image, size);
 | |
| 	if (!fmba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	if (ifd_version >= IFD_VERSION_2) {
 | |
| 		wr_shift = FLMSTR_WR_SHIFT_V2;
 | |
| 		rd_shift = FLMSTR_RD_SHIFT_V2;
 | |
| 
 | |
| 		/*
 | |
| 		 * Clear all read/write access bits. See comment on
 | |
| 		 * platform_has_extended_regions() for bitfields.
 | |
| 		 */
 | |
| 		if (platform_has_extended_regions()) {
 | |
| 			fmba->flmstr1 = 0;
 | |
| 			fmba->flmstr2 = 0;
 | |
| 			fmba->flmstr3 = 0;
 | |
| 			fmba->flmstr5 = 0;
 | |
| 		} else {
 | |
| 			fmba->flmstr1 &= 0xff;
 | |
| 			fmba->flmstr2 &= 0xff;
 | |
| 			fmba->flmstr3 &= 0xff;
 | |
| 			fmba->flmstr5 &= 0xff;
 | |
| 		}
 | |
| 	} else {
 | |
| 		wr_shift = FLMSTR_WR_SHIFT_V1;
 | |
| 		rd_shift = FLMSTR_RD_SHIFT_V1;
 | |
| 
 | |
| 		fmba->flmstr1 = 0;
 | |
| 		fmba->flmstr2 = 0;
 | |
| 		/* Requestor ID */
 | |
| 		fmba->flmstr3 = 0x118;
 | |
| 	}
 | |
| 
 | |
| 	switch (platform) {
 | |
| 	case PLATFORM_APL:
 | |
| 	case PLATFORM_GLK:
 | |
| 		/* CPU/BIOS can read descriptor and BIOS */
 | |
| 		fmba->flmstr1 |= 0x3 << rd_shift;
 | |
| 		/* CPU/BIOS can write BIOS */
 | |
| 		fmba->flmstr1 |= 0x2 << wr_shift;
 | |
| 		/* TXE can read descriptor, BIOS and Device Expansion */
 | |
| 		fmba->flmstr2 |= 0x23 << rd_shift;
 | |
| 		/* TXE can only write Device Expansion */
 | |
| 		fmba->flmstr2 |= 0x20 << wr_shift;
 | |
| 		break;
 | |
| 	case PLATFORM_CNL:
 | |
| 	case PLATFORM_ICL:
 | |
| 	case PLATFORM_SKLKBL:
 | |
| 	case PLATFORM_TGL:
 | |
| 	case PLATFORM_JSL:
 | |
| 	case PLATFORM_EHL:
 | |
| 	case PLATFORM_ADL:
 | |
| 	case PLATFORM_IFD2:
 | |
| 	case PLATFORM_MTL:
 | |
| 	case PLATFORM_PTL:
 | |
| 		/* CPU/BIOS can read descriptor and BIOS. */
 | |
| 		fmba->flmstr1 |= (1 << REGION_DESC) << rd_shift;
 | |
| 		fmba->flmstr1 |= (1 << REGION_BIOS) << rd_shift;
 | |
| 		/* CPU/BIOS can write BIOS. */
 | |
| 		fmba->flmstr1 |= (1 << REGION_BIOS) << wr_shift;
 | |
| 		/* ME can read descriptor and ME. */
 | |
| 		fmba->flmstr2 |= (1 << REGION_DESC) << rd_shift;
 | |
| 		fmba->flmstr2 |= (1 << REGION_ME) << rd_shift;
 | |
| 		/* ME can write ME. */
 | |
| 		fmba->flmstr2 |= (1 << REGION_ME) << wr_shift;
 | |
| 		if (check_region(frba, REGION_GBE)) {
 | |
| 			/* BIOS can read/write GbE. */
 | |
| 			fmba->flmstr1 |= (1 << REGION_GBE) << rd_shift;
 | |
| 			fmba->flmstr1 |= (1 << REGION_GBE) << wr_shift;
 | |
| 			/* ME can read GbE. */
 | |
| 			fmba->flmstr2 |= (1 << REGION_GBE) << rd_shift;
 | |
| 			/* GbE can read descriptor and read/write GbE.. */
 | |
| 			fmba->flmstr3 |= (1 << REGION_DESC) << rd_shift;
 | |
| 			fmba->flmstr3 |= (1 << REGION_GBE) << rd_shift;
 | |
| 			fmba->flmstr3 |= (1 << REGION_GBE) << wr_shift;
 | |
| 		}
 | |
| 		if (check_region(frba, REGION_PDR)) {
 | |
| 			/* BIOS can read/write PDR. */
 | |
| 			fmba->flmstr1 |= (1 << REGION_PDR) << rd_shift;
 | |
| 			fmba->flmstr1 |= (1 << REGION_PDR) << wr_shift;
 | |
| 		}
 | |
| 		if (check_region(frba, REGION_EC)) {
 | |
| 			/* BIOS can read EC. */
 | |
| 			fmba->flmstr1 |= (1 << REGION_EC) << rd_shift;
 | |
| 			/* EC can read descriptor and read/write EC. */
 | |
| 			fmba->flmstr5 |= (1 << REGION_DESC) << rd_shift;
 | |
| 			fmba->flmstr5 |= (1 << REGION_EC) << rd_shift;
 | |
| 			fmba->flmstr5 |= (1 << REGION_EC) << wr_shift;
 | |
| 		}
 | |
| 		if (check_region(frba, REGION_DEV_EXP2)) {
 | |
| 			/* BIOS can read SPI device expansion 2 region. */
 | |
| 			fmba->flmstr1 |= (1 << REGION_DEV_EXP2) << rd_shift;
 | |
| 		}
 | |
| 		break;
 | |
| 	case PLATFORM_DNV:
 | |
| 	case PLATFORM_WBG:
 | |
| 		/* CPU/BIOS can read descriptor and BIOS. */
 | |
| 		fmba->flmstr1 |= (1 << REGION_DESC) << rd_shift;
 | |
| 		fmba->flmstr1 |= (1 << REGION_BIOS) << rd_shift;
 | |
| 		/* CPU/BIOS can write BIOS. */
 | |
| 		fmba->flmstr1 |= (1 << REGION_BIOS) << wr_shift;
 | |
| 		/* ME can read descriptor and ME. */
 | |
| 		fmba->flmstr2 |= (1 << REGION_DESC) << rd_shift;
 | |
| 		fmba->flmstr2 |= (1 << REGION_ME) << rd_shift;
 | |
| 		/* ME can write ME. */
 | |
| 		fmba->flmstr2 |= (1 << REGION_ME) << wr_shift;
 | |
| 		break;
 | |
| 	default:
 | |
| 		/* CPU/BIOS can read descriptor and BIOS. */
 | |
| 		fmba->flmstr1 |= (1 << REGION_DESC) << rd_shift;
 | |
| 		fmba->flmstr1 |= (1 << REGION_BIOS) << rd_shift;
 | |
| 		/* CPU/BIOS can write BIOS. */
 | |
| 		fmba->flmstr1 |= (1 << REGION_BIOS) << wr_shift;
 | |
| 		/* ME can read descriptor and ME. */
 | |
| 		fmba->flmstr2 |= (1 << REGION_DESC) << rd_shift;
 | |
| 		fmba->flmstr2 |= (1 << REGION_ME) << rd_shift;
 | |
| 		/* ME can write ME. */
 | |
| 		fmba->flmstr2 |= (1 << REGION_ME) << wr_shift;
 | |
| 		if (check_region(frba, REGION_GBE)) {
 | |
| 			/* BIOS can read GbE. */
 | |
| 			fmba->flmstr1 |= (1 << REGION_GBE) << rd_shift;
 | |
| 			/* BIOS can write GbE. */
 | |
| 			fmba->flmstr1 |= (1 << REGION_GBE) << wr_shift;
 | |
| 			/* ME can read GbE. */
 | |
| 			fmba->flmstr2 |= (1 << REGION_GBE) << rd_shift;
 | |
| 			/* ME can write GbE. */
 | |
| 			fmba->flmstr2 |= (1 << REGION_GBE) << wr_shift;
 | |
| 			/* GbE can write GbE. */
 | |
| 			fmba->flmstr3 |= (1 << REGION_GBE) << rd_shift;
 | |
| 			/* GbE can read GbE. */
 | |
| 			fmba->flmstr3 |= (1 << REGION_GBE) << wr_shift;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	write_image(filename, image, size);
 | |
| }
 | |
| 
 | |
| static void enable_cpu_read_me(const char *filename, char *image, int size)
 | |
| {
 | |
| 	int rd_shift;
 | |
| 	struct fmba *fmba = find_fmba(image, size);
 | |
| 
 | |
| 	if (!fmba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	if (ifd_version >= IFD_VERSION_2)
 | |
| 		rd_shift = FLMSTR_RD_SHIFT_V2;
 | |
| 	else
 | |
| 		rd_shift = FLMSTR_RD_SHIFT_V1;
 | |
| 
 | |
| 	/* CPU/BIOS can read ME. */
 | |
| 	fmba->flmstr1 |= (1 << REGION_ME) << rd_shift;
 | |
| 
 | |
| 	write_image(filename, image, size);
 | |
| }
 | |
| 
 | |
| static void unlock_descriptor(const char *filename, char *image, int size)
 | |
| {
 | |
| 	struct fmba *fmba = find_fmba(image, size);
 | |
| 	if (!fmba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	if (ifd_version >= IFD_VERSION_2) {
 | |
| 		/*
 | |
| 		 * Set all read/write access bits. See comment on
 | |
| 		 * platform_has_extended_regions() for bitfields.
 | |
| 		 */
 | |
| 		if (platform_has_extended_regions()) {
 | |
| 			fmba->flmstr1 = 0xffffffff;
 | |
| 			fmba->flmstr2 = 0xffffffff;
 | |
| 			fmba->flmstr3 = 0xffffffff;
 | |
| 			fmba->flmstr5 = 0xffffffff;
 | |
| 		} else {
 | |
| 			fmba->flmstr1 = 0xffffff00 | (fmba->flmstr1 & 0xff);
 | |
| 			fmba->flmstr2 = 0xffffff00 | (fmba->flmstr2 & 0xff);
 | |
| 			fmba->flmstr3 = 0xffffff00 | (fmba->flmstr3 & 0xff);
 | |
| 			fmba->flmstr5 = 0xffffff00 | (fmba->flmstr5 & 0xff);
 | |
| 		}
 | |
| 	} else {
 | |
| 		fmba->flmstr1 = 0xffff0000;
 | |
| 		fmba->flmstr2 = 0xffff0000;
 | |
| 		/* Keep chipset specific Requester ID */
 | |
| 		fmba->flmstr3 = 0x08080000 | (fmba->flmstr3 & 0xffff);
 | |
| 	}
 | |
| 
 | |
| 	write_image(filename, image, size);
 | |
| }
 | |
| 
 | |
| static void print_gpr0_range(union gprd reg)
 | |
| {
 | |
| 	printf("--------- GPR0 Protected Range --------------\n");
 | |
| 	printf("Start address = 0x%08x\n", reg.data.start << 12);
 | |
| 	printf("End address = 0x%08x\n", (reg.data.end << 12) | 0xfff);
 | |
| }
 | |
| 
 | |
| static uint8_t get_cse_data_partition_offset(void)
 | |
| {
 | |
| 	uint8_t data_offset = 0xff;
 | |
| 
 | |
| 	switch (platform) {
 | |
| 	case PLATFORM_CNL:
 | |
| 	case PLATFORM_JSL:
 | |
| 		data_offset = 0x10;
 | |
| 		break;
 | |
| 	case PLATFORM_TGL:
 | |
| 	case PLATFORM_ADL:
 | |
| 	case PLATFORM_MTL:
 | |
| 	case PLATFORM_PTL:
 | |
| 		data_offset = 0x18;
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return data_offset;
 | |
| }
 | |
| 
 | |
| static uint32_t get_gpr0_offset(void)
 | |
| {
 | |
| 	/* Offset expressed as number of 32-bit fields from FPSBA */
 | |
| 	uint32_t gpr0_offset = 0xffffffff;
 | |
| 
 | |
| 	switch (platform) {
 | |
| 	case PLATFORM_CNL:
 | |
| 		gpr0_offset = 0x10;
 | |
| 		break;
 | |
| 	case PLATFORM_JSL:
 | |
| 		gpr0_offset = 0x12;
 | |
| 		break;
 | |
| 	case PLATFORM_TGL:
 | |
| 	case PLATFORM_ADL:
 | |
| 		gpr0_offset = 0x15;
 | |
| 		break;
 | |
| 	case PLATFORM_MTL:
 | |
| 		gpr0_offset = 0x40;
 | |
| 		break;
 | |
| 	case PLATFORM_PTL:
 | |
| 		gpr0_offset = 0x76;
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return gpr0_offset;
 | |
| }
 | |
| 
 | |
| static void disable_gpr0(const char *filename, char *image, int size)
 | |
| {
 | |
| 	struct fpsba *fpsba = find_fpsba(image, size);
 | |
| 	if (!fpsba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	uint32_t gpr0_offset = get_gpr0_offset();
 | |
| 	if (gpr0_offset == 0xffffffff) {
 | |
| 		fprintf(stderr, "Disabling GPR0 not supported on this platform\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	union gprd reg;
 | |
| 	/* If bit 31 is set then GPR0 protection is enable */
 | |
| 	reg.value = fpsba->pchstrp[gpr0_offset];
 | |
| 	if (!reg.data.write_protect_en) {
 | |
| 		printf("GPR0 protection is already disabled\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	printf("Value at GPRD offset (%d) is 0x%08x\n", gpr0_offset, reg.value);
 | |
| 	print_gpr0_range(reg);
 | |
| 	/* 0 means GPR0 protection is disabled */
 | |
| 	fpsba->pchstrp[gpr0_offset] = 0;
 | |
| 	write_image(filename, image, size);
 | |
| 	printf("GPR0 protection is now disabled\n");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Helper function to parse the FPT to retrieve the FITC start offset and size.
 | |
|  * FITC is a sub-partition table inside CSE data partition known as FPT.
 | |
|  *
 | |
|  * CSE Region
 | |
|  *   |-----> CSE Data Partition Offset
 | |
|  *   |              |------->  FPT Entry
 | |
|  *   |              |              |-> Sub Partition 1
 | |
|  *   |              |              |-> Sub Partition 2
 | |
|  *   |              |              |-> FITC
 | |
|  *   |              |              |     | -> FITC Offset
 | |
|  *   |              |              |     | -> FITC Length
 | |
|  */
 | |
| static int parse_fitc_table(struct cse_fpt *fpt, uint32_t *offset,
 | |
| 		 size_t *size)
 | |
| {
 | |
| 	size_t num_part_header = fpt->count;
 | |
| 	/* Move to the next structure which is FPT sub-partition entries */
 | |
| 	struct cse_fpt_sub_part *fpt_sub_part = (struct cse_fpt_sub_part *)(fpt + 1);
 | |
| 	for (size_t index = 0; index < num_part_header; index++) {
 | |
| 		if (!strncmp(fpt_sub_part->signature, "FITC", 4)) {
 | |
| 			*offset = fpt_sub_part->offset;
 | |
| 			*size = fpt_sub_part->length;
 | |
| 			return 0;
 | |
| 		}
 | |
| 		fpt_sub_part++;
 | |
| 	}
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Formula to calculate the GPR0 protection range as below:
 | |
|  * Start: CSE Region Base Offset
 | |
|  * End: Till the end of FITC sub-partition
 | |
|  */
 | |
| static int calculate_gpr0_range(char *image, int size,
 | |
| 		 uint32_t *gpr0_start, uint32_t *gpr0_end)
 | |
| {
 | |
| 	struct frba *frba = find_frba(image, size);
 | |
| 	if (!frba)
 | |
| 		return -1;
 | |
| 
 | |
| 	struct region region = get_region(frba, REGION_ME);
 | |
| 	if (region.size <= 0) {
 | |
| 		fprintf(stderr, "Region %s is disabled in target\n",
 | |
| 				region_name(REGION_ME));
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* CSE Region Start */
 | |
| 	uint32_t cse_region_start = region.base;
 | |
| 	/* Get CSE Data Partition Offset */
 | |
| 	uint8_t cse_data_offset = get_cse_data_partition_offset();
 | |
| 	if (cse_data_offset == 0xff) {
 | |
| 		fprintf(stderr, "Unsupported platform\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	uint32_t data_part_offset = *((uint32_t *)(image + cse_region_start + cse_data_offset));
 | |
| 	/* Start reading the CSE Data Partition Table, also known as FPT */
 | |
| 	uint32_t data_part_start = data_part_offset + cse_region_start;
 | |
| 
 | |
| 	uint32_t fitc_region_start = 0;
 | |
| 	size_t fitc_region_size = 0;
 | |
| 	/*
 | |
| 	 * FPT holds entry for own FPT data structure also bunch of sub-partitions.
 | |
| 	 * `FITC` is one of such sub-partition entry.
 | |
| 	 */
 | |
| 	if (parse_fitc_table(((struct cse_fpt *)(image + data_part_start)),
 | |
| 			 &fitc_region_start, &fitc_region_size) < 0) {
 | |
| 		fprintf(stderr, "Unable to find FITC entry\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * GPR0 protection is configured to the following range:
 | |
| 	 * start: CSE region base offset
 | |
| 	 * end: Till the end of FITC sub-partition (i.e. CSE region + data partition offset +
 | |
| 	 *       FITC sub partition offset + FITC sub partition size)
 | |
| 	 */
 | |
| 	*gpr0_start = cse_region_start;
 | |
| 	*gpr0_end = (cse_region_start + data_part_offset +
 | |
| 				 fitc_region_start + fitc_region_size) - 1;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static union gprd get_enabled_gprd(char *image, int size)
 | |
| {
 | |
| 	union gprd enabled_gprd_reg;
 | |
| 	uint32_t gpr0_range_start, gpr0_range_end;
 | |
| 	enabled_gprd_reg.value = 0;
 | |
| 	if (calculate_gpr0_range(image, size, &gpr0_range_start, &gpr0_range_end))
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	enabled_gprd_reg.data.start = (gpr0_range_start >> 12) & 0x7fff;
 | |
| 	enabled_gprd_reg.data.end = (gpr0_range_end >> 12) & 0x7fff;
 | |
| 	enabled_gprd_reg.data.read_protect_en = 0;
 | |
| 	enabled_gprd_reg.data.write_protect_en = 1;
 | |
| 
 | |
| 	return enabled_gprd_reg;
 | |
| }
 | |
| 
 | |
| static void enable_gpr0(const char *filename, char *image, int size)
 | |
| {
 | |
| 	struct fpsba *fpsba = find_fpsba(image, size);
 | |
| 	if (!fpsba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	uint32_t gpr0_offset = get_gpr0_offset();
 | |
| 	if (gpr0_offset == 0xffffffff) {
 | |
| 		fprintf(stderr, "Enabling GPR0 not supported on this platform\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	union gprd reg;
 | |
| 	/* If bit 31 is set then GPR0 protection is enable */
 | |
| 	reg.value = fpsba->pchstrp[gpr0_offset];
 | |
| 	if (reg.data.write_protect_en) {
 | |
| 		printf("GPR0 protection is already enabled\n");
 | |
| 		print_gpr0_range(reg);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	union gprd enabled_gprd = get_enabled_gprd(image, size);
 | |
| 
 | |
| 	fpsba->pchstrp[gpr0_offset] = enabled_gprd.value;
 | |
| 	printf("Value at GPRD offset (%d) is 0x%08x\n", gpr0_offset, enabled_gprd.value);
 | |
| 	print_gpr0_range(enabled_gprd);
 | |
| 	write_image(filename, image, size);
 | |
| 	printf("GPR0 protection is now enabled\n");
 | |
| }
 | |
| 
 | |
| static void is_gpr0_protected(char *image, int size)
 | |
| {
 | |
| 	struct fpsba *fpsba = find_fpsba(image, size);
 | |
| 	if (!fpsba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	uint32_t gpr0_offset = get_gpr0_offset();
 | |
| 	if (gpr0_offset == 0xffffffff) {
 | |
| 		fprintf(stderr, "Checking GPR0 not supported on this platform\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	union gprd reg;
 | |
| 	union gprd enabled_gprd = get_enabled_gprd(image, size);
 | |
| 	reg.value = fpsba->pchstrp[gpr0_offset];
 | |
| 
 | |
| 	if (fpsba->pchstrp[gpr0_offset] == enabled_gprd.value)
 | |
| 		printf("GPR0 status: Enabled\n\n");
 | |
| 	else if (fpsba->pchstrp[gpr0_offset] == 0)
 | |
| 		printf("GPR0 status: Disabled\n\n");
 | |
| 	else
 | |
| 		printf("ERROR: GPR0 setting is not expected\n\n");
 | |
| 
 | |
| 	printf("Value at GPRD offset (%d) is 0x%08x\n", gpr0_offset, fpsba->pchstrp[gpr0_offset]);
 | |
| 	print_gpr0_range(reg);
 | |
| }
 | |
| 
 | |
| static void set_pchstrap(struct fpsba *fpsba, const struct fdbar *fdb, const int strap,
 | |
| 			const unsigned int value)
 | |
| {
 | |
| 	if (!fpsba || !fdb) {
 | |
| 		fprintf(stderr, "Internal error\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	/* SoC Strap, aka PSL, aka ISL */
 | |
| 	int SS = (fdb->flmap1 >> 24) & 0xff;
 | |
| 	if (strap >= SS) {
 | |
| 		fprintf(stderr, "Strap index %d out of range (max: %d)\n", strap, SS);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	fpsba->pchstrp[strap] = value;
 | |
| }
 | |
| 
 | |
| /* Set the AltMeDisable (or HAP for >= IFD_VERSION_2) */
 | |
| static void fpsba_set_altmedisable(struct fpsba *fpsba, struct fmsba *fmsba, bool altmedisable)
 | |
| {
 | |
| 	if (ifd_version >= IFD_VERSION_2) {
 | |
| 		printf("%sting the HAP bit to %s Intel ME...\n",
 | |
| 			altmedisable ? "Set" : "Unset",
 | |
| 			altmedisable ? "disable" : "enable");
 | |
| 		if (altmedisable)
 | |
| 			fpsba->pchstrp[0] |= (1 << 16);
 | |
| 		else
 | |
| 			fpsba->pchstrp[0] &= ~(1 << 16);
 | |
| 	} else {
 | |
| 		if (chipset >= CHIPSET_ICH8 && chipset <= CHIPSET_ICH10) {
 | |
| 			printf("%sting the ICH_MeDisable, MCH_MeDisable, "
 | |
| 			       "and MCH_AltMeDisable to %s Intel ME...\n",
 | |
| 			       altmedisable ? "Set" : "Unset",
 | |
| 			       altmedisable ? "disable" : "enable");
 | |
| 			if (altmedisable) {
 | |
| 				/* MCH_MeDisable */
 | |
| 				fmsba->data[0] |= 1;
 | |
| 				/* MCH_AltMeDisable */
 | |
| 				fmsba->data[0] |= (1 << 7);
 | |
| 				/* ICH_MeDisable */
 | |
| 				fpsba->pchstrp[0] |= 1;
 | |
| 			} else {
 | |
| 				fmsba->data[0] &= ~1;
 | |
| 				fmsba->data[0] &= ~(1 << 7);
 | |
| 				fpsba->pchstrp[0] &= ~1;
 | |
| 			}
 | |
| 		} else {
 | |
| 			printf("%sting the AltMeDisable to %s Intel ME...\n",
 | |
| 				altmedisable ? "Set" : "Unset",
 | |
| 				altmedisable ? "disable" : "enable");
 | |
| 			if (altmedisable)
 | |
| 				fpsba->pchstrp[10] |= (1 << 7);
 | |
| 			else
 | |
| 				fpsba->pchstrp[10] &= ~(1 << 7);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void inject_region(const char *filename, char *image, int size,
 | |
| 			  unsigned int region_type, const char *region_fname)
 | |
| {
 | |
| 	struct frba *frba = find_frba(image, size);
 | |
| 	if (!frba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	struct region region = get_region(frba, region_type);
 | |
| 	if (region.size <= 0xfff) {
 | |
| 		fprintf(stderr, "Region %s is disabled in target. Not injecting.\n",
 | |
| 				region_name(region_type));
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	int region_fd = open(region_fname, O_RDONLY | O_BINARY);
 | |
| 	if (region_fd == -1) {
 | |
| 		perror("Could not open file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	struct stat buf;
 | |
| 	if (fstat(region_fd, &buf) == -1) {
 | |
| 		perror("Could not stat file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	int region_size = buf.st_size;
 | |
| 
 | |
| 	printf("File %s is %d bytes\n", region_fname, region_size);
 | |
| 
 | |
| 	if (region_size > region.size) {
 | |
| 		fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x)"
 | |
| 				" bytes. Not injecting.\n",
 | |
| 				region_name(region_type), region.size,
 | |
| 				region.size, region_size, region_size);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	int offset = 0;
 | |
| 	if ((region_type == 1) && (region_size < region.size)) {
 | |
| 		fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x)"
 | |
| 				" bytes. Padding before injecting.\n",
 | |
| 				region_name(region_type), region.size,
 | |
| 				region.size, region_size, region_size);
 | |
| 		offset = region.size - region_size;
 | |
| 		memset(image + region.base, 0xff, offset);
 | |
| 	}
 | |
| 
 | |
| 	if (size < region.base + offset + region_size) {
 | |
| 		fprintf(stderr, "Output file is too small. (%d < %d)\n",
 | |
| 			size, region.base + offset + region_size);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	if (read(region_fd, image + region.base + offset, region_size) != region_size) {
 | |
| 		perror("Could not read file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	close(region_fd);
 | |
| 
 | |
| 	printf("Adding %s as the %s section of %s\n",
 | |
| 	       region_fname, region_name(region_type), filename);
 | |
| 	write_image(filename, image, size);
 | |
| }
 | |
| 
 | |
| static unsigned int next_pow2(unsigned int x)
 | |
| {
 | |
| 	unsigned int y = 1;
 | |
| 	if (x == 0)
 | |
| 		return 0;
 | |
| 	while (y <= x)
 | |
| 		y = y << 1;
 | |
| 
 | |
| 	return y;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Determine if two memory regions overlap.
 | |
|  *
 | |
|  * @param r1, r2 Memory regions to compare.
 | |
|  * @return 0 if the two regions are separate
 | |
|  * @return 1 if the two regions overlap
 | |
|  */
 | |
| static int regions_collide(const struct region *r1, const struct region *r2)
 | |
| {
 | |
| 	if ((r1->size == 0) || (r2->size == 0))
 | |
| 		return 0;
 | |
| 
 | |
| 	/* r1 should be either completely below or completely above r2 */
 | |
| 	return !(r1->limit < r2->base || r1->base > r2->limit);
 | |
| }
 | |
| 
 | |
| static void new_layout(const char *filename, char *image, int size,
 | |
| 		const char *layout_fname)
 | |
| {
 | |
| 	FILE *romlayout;
 | |
| 	char tempstr[256];
 | |
| 	char layout_region_name[256];
 | |
| 	unsigned int i, j;
 | |
| 	int region_number;
 | |
| 	struct region current_regions[MAX_REGIONS];
 | |
| 	struct region new_regions[MAX_REGIONS];
 | |
| 	int new_extent = 0;
 | |
| 	char *new_image;
 | |
| 
 | |
| 	/* load current descriptor map and regions */
 | |
| 	struct frba *frba = find_frba(image, size);
 | |
| 	if (!frba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	for (i = 0; i < max_regions; i++) {
 | |
| 		current_regions[i] = get_region(frba, i);
 | |
| 		new_regions[i] = get_region(frba, i);
 | |
| 	}
 | |
| 
 | |
| 	/* read new layout */
 | |
| 	romlayout = fopen(layout_fname, "r");
 | |
| 
 | |
| 	if (!romlayout) {
 | |
| 		perror("Could not read layout file.\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	while (!feof(romlayout)) {
 | |
| 		char *tstr1, *tstr2;
 | |
| 
 | |
| 		if (2 != fscanf(romlayout, "%255s %255s\n", tempstr,
 | |
| 					layout_region_name))
 | |
| 			continue;
 | |
| 
 | |
| 		region_number = region_num(layout_region_name);
 | |
| 		if (region_number < 0)
 | |
| 			continue;
 | |
| 
 | |
| 		tstr1 = strtok(tempstr, ":");
 | |
| 		tstr2 = strtok(NULL, ":");
 | |
| 		if (!tstr1 || !tstr2) {
 | |
| 			fprintf(stderr, "Could not parse layout file.\n");
 | |
| 			exit(EXIT_FAILURE);
 | |
| 		}
 | |
| 		new_regions[region_number].base = strtol(tstr1,
 | |
| 				(char **)NULL, 16);
 | |
| 		new_regions[region_number].limit = strtol(tstr2,
 | |
| 				(char **)NULL, 16);
 | |
| 		new_regions[region_number].size =
 | |
| 			new_regions[region_number].limit -
 | |
| 			new_regions[region_number].base + 1;
 | |
| 
 | |
| 		if (new_regions[region_number].size < 0)
 | |
| 			new_regions[region_number].size = 0;
 | |
| 	}
 | |
| 	fclose(romlayout);
 | |
| 
 | |
| 	/* check new layout */
 | |
| 	for (i = 0; i < max_regions; i++) {
 | |
| 		if (new_regions[i].size == 0)
 | |
| 			continue;
 | |
| 
 | |
| 		if (new_regions[i].size < current_regions[i].size) {
 | |
| 			printf("DANGER: Region %s is shrinking.\n",
 | |
| 					region_name(i));
 | |
| 			printf("    The region will be truncated to fit.\n");
 | |
| 			printf("    This may result in an unusable image.\n");
 | |
| 		}
 | |
| 
 | |
| 		for (j = i + 1; j < max_regions; j++) {
 | |
| 			if (regions_collide(&new_regions[i], &new_regions[j])) {
 | |
| 				fprintf(stderr, "Regions would overlap.\n");
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* detect if the image size should grow */
 | |
| 		if (new_extent < new_regions[i].limit)
 | |
| 			new_extent = new_regions[i].limit;
 | |
| 	}
 | |
| 
 | |
| 	/* check if the image is actually a Flash Descriptor region */
 | |
| 	if (size == new_regions[0].size) {
 | |
| 		printf("The image is a single Flash Descriptor:\n");
 | |
| 		printf("    Only the descriptor will be modified\n");
 | |
| 		new_extent = size;
 | |
| 	} else {
 | |
| 		new_extent = next_pow2(new_extent - 1);
 | |
| 		if (new_extent != size) {
 | |
| 			printf("The image has changed in size.\n");
 | |
| 			printf("The old image is %d bytes.\n", size);
 | |
| 			printf("The new image is %d bytes.\n", new_extent);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* copy regions to a new image */
 | |
| 	new_image = malloc(new_extent);
 | |
| 	memset(new_image, 0xff, new_extent);
 | |
| 	for (i = 0; i < max_regions; i++) {
 | |
| 		int copy_size = new_regions[i].size;
 | |
| 		int offset_current = 0, offset_new = 0;
 | |
| 		const struct region *current = ¤t_regions[i];
 | |
| 		const struct region *new = &new_regions[i];
 | |
| 
 | |
| 		if (new->size == 0)
 | |
| 			continue;
 | |
| 
 | |
| 		if (new->size > current->size) {
 | |
| 			/* copy from the end of the current region */
 | |
| 			copy_size = current->size;
 | |
| 			if (i == REGION_BIOS)
 | |
| 				offset_new = new->size - current->size;
 | |
| 		}
 | |
| 
 | |
| 		if ((i == REGION_BIOS) && (new->size < current->size)) {
 | |
| 			/* copy BIOS region to the end of the new region */
 | |
| 			offset_current = current->size - new->size;
 | |
| 		}
 | |
| 
 | |
| 		if (size < current->base + offset_current + copy_size) {
 | |
| 			printf("Skip descriptor %d (%s) (region missing in the old image)\n", i,
 | |
| 				region_name(i));
 | |
| 			continue;
 | |
| 		};
 | |
| 
 | |
| 		printf("Copy Descriptor %d (%s) (%d bytes)\n", i,
 | |
| 				region_name(i), copy_size);
 | |
| 		printf("   from %08x+%08x:%08x (%10d)\n", current->base,
 | |
| 				offset_current, current->limit, current->size);
 | |
| 		printf("     to %08x+%08x:%08x (%10d)\n", new->base,
 | |
| 				offset_new, new->limit, new->size);
 | |
| 
 | |
| 		memcpy(new_image + new->base + offset_new,
 | |
| 				image + current->base + offset_current,
 | |
| 				copy_size);
 | |
| 	}
 | |
| 
 | |
| 	/* update new descriptor regions */
 | |
| 	frba = find_frba(new_image, new_extent);
 | |
| 	if (!frba)
 | |
| 		exit(EXIT_FAILURE);
 | |
| 
 | |
| 	printf("Modify Flash Descriptor regions\n");
 | |
| 	for (i = 1; i < max_regions; i++)
 | |
| 		set_region(frba, i, &new_regions[i]);
 | |
| 
 | |
| 	write_image(filename, new_image, new_extent);
 | |
| 	free(new_image);
 | |
| }
 | |
| 
 | |
| static void print_version(void)
 | |
| {
 | |
| 	printf("ifdtool v%s -- ", IFDTOOL_VERSION);
 | |
| 	printf("Copyright (C) 2011 Google Inc.\n\n");
 | |
| 	printf
 | |
| 	    ("This program is free software: you can redistribute it and/or modify\n"
 | |
| 	     "it under the terms of the GNU General Public License as published by\n"
 | |
| 	     "the Free Software Foundation, version 2 of the License.\n\n"
 | |
| 	     "This program is distributed in the hope that it will be useful,\n"
 | |
| 	     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
 | |
| 	     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
 | |
| 	     "GNU General Public License for more details.\n\n");
 | |
| }
 | |
| 
 | |
| static void print_usage(const char *name)
 | |
| {
 | |
| 	printf("usage: %s [-vhdix?] <filename>\n", name);
 | |
| 	printf("\n"
 | |
| 	       "   -d | --dump:                          dump intel firmware descriptor\n"
 | |
| 	       "   -f | --layout <filename>              dump regions into a flashrom layout file\n"
 | |
| 	       "   -F | --fmap-layout <filename>         dump IFD regions into a fmap layout template (.fmd) file\n"
 | |
| 	       "   -t | --validate                       Validate that the firmware descriptor layout matches the fmap layout\n"
 | |
| 	       "   -x | --extract:                       extract intel fd modules\n"
 | |
| 	       "   -i | --inject <region>:<module>       inject file <module> into region <region>\n"
 | |
| 	       "   -n | --newlayout <filename>           update regions using a flashrom layout file\n"
 | |
| 	       "   -O | --output <filename>              output filename\n"
 | |
| 	       "   -s | --spifreq <17|20|30|33|48|50>    set the SPI frequency\n"
 | |
| 	       "   -D | --density <512|1|2|4|8|16|32|64> set chip density (512 in KByte, others in MByte)\n"
 | |
| 	       "   -C | --chip <0|1|2>                   select spi chip on which to operate\n"
 | |
| 	       "                                         can only be used once per run:\n"
 | |
| 	       "                                         0 - both chips (default), 1 - first chip, 2 - second chip\n"
 | |
| 	       "   -e | --em100                          set SPI frequency to 20MHz and disable\n"
 | |
| 	       "                                         Dual Output Fast Read Support\n"
 | |
| 	       "   -l | --lock                           Lock firmware descriptor and ME region\n"
 | |
| 	       "   -r | --read				 Enable CPU/BIOS read access for ME region\n"
 | |
| 	       "   -u | --unlock                         Unlock firmware descriptor and ME region\n"
 | |
| 	       "   -g | --gpr0-disable                   Disable GPR0 (Global Protected Range) register\n"
 | |
| 	       "   -E | --gpr0-enable                    Enable GPR0 (Global Protected Range) register\n"
 | |
| 	       "   -c | --gpr0-status                    Checking GPR0 (Global Protected Range) register status\n"
 | |
| 	       "   -M | --altmedisable <0|1>             Set the MeDisable and AltMeDisable (or HAP for skylake or newer platform)\n"
 | |
| 	       "                                         bits to disable ME\n"
 | |
| 	       "   -p | --platform                       Add platform-specific quirks\n"
 | |
| 	       "                                         adl    - Alder Lake\n"
 | |
| 	       "                                         aplk   - Apollo Lake\n"
 | |
| 	       "                                         cnl    - Cannon Lake\n"
 | |
| 	       "                                         lbg    - Lewisburg PCH\n"
 | |
| 	       "                                         dnv    - Denverton\n"
 | |
| 	       "                                         ehl    - Elkhart Lake\n"
 | |
| 	       "                                         glk    - Gemini Lake\n"
 | |
| 	       "                                         icl    - Ice Lake\n"
 | |
| 	       "                                         ifd2   - IFDv2 Platform\n"
 | |
| 	       "                                         jsl    - Jasper Lake\n"
 | |
| 	       "                                         mtl    - Meteor Lake\n"
 | |
| 	       "                                         sklkbl - Sky Lake/Kaby Lake\n"
 | |
| 	       "                                         tgl    - Tiger Lake\n"
 | |
| 	       "                                         wbg    - Wellsburg\n"
 | |
| 	       "   -S | --setpchstrap                    Write a PCH strap\n"
 | |
| 	       "   -V | --newvalue                       The new value to write into PCH strap specified by -S\n"
 | |
| 	       "   -v | --version:                       print the version\n"
 | |
| 	       "   -h | --help:                          print this help\n\n"
 | |
| 	       "<region> is one of Descriptor, BIOS, ME, GbE, Platform Data, Secondary BIOS, "
 | |
| 	       "Device Exp1, EC, Device Exp2, IE, 10GbE_0, 10GbE_1, PTT\n"
 | |
| 	       "\n");
 | |
| }
 | |
| 
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
| 	int opt, option_index = 0;
 | |
| 	int mode_dump = 0, mode_extract = 0, mode_inject = 0, mode_spifreq = 0;
 | |
| 	int mode_em100 = 0, mode_locked = 0, mode_unlocked = 0, mode_validate = 0;
 | |
| 	int mode_layout = 0, mode_newlayout = 0, mode_density = 0, mode_setstrap = 0;
 | |
| 	int mode_read = 0, mode_altmedisable = 0, altmedisable = 0, mode_fmap_template = 0;
 | |
| 	int mode_gpr0_disable = 0, mode_gpr0_enable = 0, mode_gpr0_status = 0;
 | |
| 	char *region_type_string = NULL, *region_fname = NULL;
 | |
| 	const char *layout_fname = NULL;
 | |
| 	char *new_filename = NULL;
 | |
| 	int region_type = -1, inputfreq = 0;
 | |
| 	unsigned int value = 0;
 | |
| 	unsigned int pchstrap = 0;
 | |
| 	unsigned int new_density = 0;
 | |
| 	enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ;
 | |
| 
 | |
| 	static const struct option long_options[] = {
 | |
| 		{"dump", 0, NULL, 'd'},
 | |
| 		{"layout", 1, NULL, 'f'},
 | |
| 		{"fmap-template", 1, NULL, 'F'},
 | |
| 		{"extract", 0, NULL, 'x'},
 | |
| 		{"inject", 1, NULL, 'i'},
 | |
| 		{"newlayout", 1, NULL, 'n'},
 | |
| 		{"output", 1, NULL, 'O'},
 | |
| 		{"spifreq", 1, NULL, 's'},
 | |
| 		{"density", 1, NULL, 'D'},
 | |
| 		{"chip", 1, NULL, 'C'},
 | |
| 		{"altmedisable", 1, NULL, 'M'},
 | |
| 		{"em100", 0, NULL, 'e'},
 | |
| 		{"lock", 0, NULL, 'l'},
 | |
| 		{"read", 0, NULL, 'r'},
 | |
| 		{"unlock", 0, NULL, 'u'},
 | |
| 		{"gpr0-disable", 0, NULL, 'g'},
 | |
| 		{"gpr0-enable", 0, NULL, 'E'},
 | |
| 		{"gpr0-status", 0, NULL, 'c'},
 | |
| 		{"version", 0, NULL, 'v'},
 | |
| 		{"help", 0, NULL, 'h'},
 | |
| 		{"platform", 1, NULL, 'p'},
 | |
| 		{"validate", 0, NULL, 't'},
 | |
| 		{"setpchstrap", 1, NULL, 'S'},
 | |
| 		{"newvalue", 1, NULL, 'V'},
 | |
| 		{0, 0, 0, 0}
 | |
| 	};
 | |
| 
 | |
| 	while ((opt = getopt_long(argc, argv, "S:V:df:F:D:C:M:xi:n:O:s:p:elrugEcvth?",
 | |
| 					long_options, &option_index)) != EOF) {
 | |
| 		switch (opt) {
 | |
| 		case 'd':
 | |
| 			mode_dump = 1;
 | |
| 			break;
 | |
| 		case 'S':
 | |
| 			mode_setstrap = 1;
 | |
| 			pchstrap = strtoul(optarg, NULL, 0);
 | |
| 			break;
 | |
| 		case 'V':
 | |
| 			value = strtoul(optarg, NULL, 0);
 | |
| 			break;
 | |
| 		case 'f':
 | |
| 			mode_layout = 1;
 | |
| 			layout_fname = strdup(optarg);
 | |
| 			if (!layout_fname) {
 | |
| 				fprintf(stderr, "No layout file specified\n");
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'F':
 | |
| 			mode_fmap_template = 1;
 | |
| 			layout_fname = strdup(optarg);
 | |
| 			if (!layout_fname) {
 | |
| 				fprintf(stderr, "No layout file specified\n");
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'x':
 | |
| 			mode_extract = 1;
 | |
| 			break;
 | |
| 		case 'i':
 | |
| 			// separate type and file name
 | |
| 			region_type_string = strdup(optarg);
 | |
| 			region_fname = strchr(region_type_string, ':');
 | |
| 			if (!region_fname) {
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			region_fname[0] = '\0';
 | |
| 			region_fname++;
 | |
| 			// Descriptor, BIOS, ME, GbE, Platform
 | |
| 			// valid type?
 | |
| 			if (!strcasecmp("Descriptor", region_type_string))
 | |
| 				region_type = 0;
 | |
| 			else if (!strcasecmp("BIOS", region_type_string))
 | |
| 				region_type = 1;
 | |
| 			else if (!strcasecmp("ME", region_type_string))
 | |
| 				region_type = 2;
 | |
| 			else if (!strcasecmp("GbE", region_type_string))
 | |
| 				region_type = 3;
 | |
| 			else if (!strcasecmp("Platform Data", region_type_string))
 | |
| 				region_type = 4;
 | |
| 			else if (!strcasecmp("Device Exp1", region_type_string))
 | |
| 				region_type = 5;
 | |
| 			else if (!strcasecmp("Secondary BIOS", region_type_string))
 | |
| 				region_type = 6;
 | |
| 			else if (!strcasecmp("Reserved", region_type_string))
 | |
| 				region_type = 7;
 | |
| 			else if (!strcasecmp("EC", region_type_string))
 | |
| 				region_type = 8;
 | |
| 			else if (!strcasecmp("Device Exp2", region_type_string))
 | |
| 				region_type = 9;
 | |
| 			else if (!strcasecmp("IE", region_type_string))
 | |
| 				region_type = 10;
 | |
| 			else if (!strcasecmp("10GbE_0", region_type_string))
 | |
| 				region_type = 11;
 | |
| 			else if (!strcasecmp("10GbE_1", region_type_string))
 | |
| 				region_type = 12;
 | |
| 			else if (!strcasecmp("PTT", region_type_string))
 | |
| 				region_type = 15;
 | |
| 			if (region_type == -1) {
 | |
| 				fprintf(stderr, "No such region type: '%s'\n\n",
 | |
| 					region_type_string);
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			mode_inject = 1;
 | |
| 			break;
 | |
| 		case 'n':
 | |
| 			mode_newlayout = 1;
 | |
| 			layout_fname = strdup(optarg);
 | |
| 			if (!layout_fname) {
 | |
| 				fprintf(stderr, "No layout file specified\n");
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'O':
 | |
| 			new_filename = strdup(optarg);
 | |
| 			if (!new_filename) {
 | |
| 				fprintf(stderr, "No output filename specified\n");
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'D':
 | |
| 			mode_density = 1;
 | |
| 			new_density = strtoul(optarg, NULL, 0);
 | |
| 			switch (new_density) {
 | |
| 			case 512:
 | |
| 				new_density = COMPONENT_DENSITY_512KB;
 | |
| 				break;
 | |
| 			case 1:
 | |
| 				new_density = COMPONENT_DENSITY_1MB;
 | |
| 				break;
 | |
| 			case 2:
 | |
| 				new_density = COMPONENT_DENSITY_2MB;
 | |
| 				break;
 | |
| 			case 4:
 | |
| 				new_density = COMPONENT_DENSITY_4MB;
 | |
| 				break;
 | |
| 			case 8:
 | |
| 				new_density = COMPONENT_DENSITY_8MB;
 | |
| 				break;
 | |
| 			case 16:
 | |
| 				new_density = COMPONENT_DENSITY_16MB;
 | |
| 				break;
 | |
| 			case 32:
 | |
| 				new_density = COMPONENT_DENSITY_32MB;
 | |
| 				break;
 | |
| 			case 64:
 | |
| 				new_density = COMPONENT_DENSITY_64MB;
 | |
| 				break;
 | |
| 			case 0:
 | |
| 				new_density = COMPONENT_DENSITY_UNUSED;
 | |
| 				break;
 | |
| 			default:
 | |
| 				printf("error: Unknown density\n");
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'C':
 | |
| 			selected_chip = strtol(optarg, NULL, 0);
 | |
| 			if (selected_chip > 2) {
 | |
| 				fprintf(stderr, "error: Invalid chip selection\n");
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'M':
 | |
| 			mode_altmedisable = 1;
 | |
| 			altmedisable = strtol(optarg, NULL, 0);
 | |
| 			if (altmedisable > 1) {
 | |
| 				fprintf(stderr, "error: Illegal value\n");
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 's':
 | |
| 			// Parse the requested SPI frequency
 | |
| 			inputfreq = strtol(optarg, NULL, 0);
 | |
| 			switch (inputfreq) {
 | |
| 			case 17:
 | |
| 				spifreq = SPI_FREQUENCY_17MHZ;
 | |
| 				break;
 | |
| 			case 20:
 | |
| 				spifreq = SPI_FREQUENCY_20MHZ;
 | |
| 				break;
 | |
| 			case 30:
 | |
| 				spifreq = SPI_FREQUENCY_50MHZ_30MHZ;
 | |
| 				break;
 | |
| 			case 33:
 | |
| 				spifreq = SPI_FREQUENCY_33MHZ;
 | |
| 				break;
 | |
| 			case 48:
 | |
| 				spifreq = SPI_FREQUENCY_48MHZ;
 | |
| 				break;
 | |
| 			case 50:
 | |
| 				spifreq = SPI_FREQUENCY_50MHZ_30MHZ;
 | |
| 				break;
 | |
| 			default:
 | |
| 				fprintf(stderr, "Invalid SPI Frequency: %d\n",
 | |
| 					inputfreq);
 | |
| 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			mode_spifreq = 1;
 | |
| 			break;
 | |
| 		case 'e':
 | |
| 			mode_em100 = 1;
 | |
| 			break;
 | |
| 		case 'l':
 | |
| 			mode_locked = 1;
 | |
| 			if (mode_unlocked == 1) {
 | |
| 				fprintf(stderr, "Locking/Unlocking FD and ME are mutually exclusive\n");
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'r':
 | |
| 			mode_read = 1;
 | |
| 			break;
 | |
| 		case 'u':
 | |
| 			mode_unlocked = 1;
 | |
| 			if (mode_locked == 1) {
 | |
| 				fprintf(stderr, "Locking/Unlocking FD and ME are mutually exclusive\n");
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'g':
 | |
| 			mode_gpr0_disable = 1;
 | |
| 			break;
 | |
| 		case 'E':
 | |
| 			mode_gpr0_enable = 1;
 | |
| 			break;
 | |
| 		case 'c':
 | |
| 			mode_gpr0_status = 1;
 | |
| 			break;
 | |
| 		case 'p':
 | |
| 			if (!strcmp(optarg, "aplk")) {
 | |
| 				platform = PLATFORM_APL;
 | |
| 			} else if (!strcmp(optarg, "cnl")) {
 | |
| 				platform = PLATFORM_CNL;
 | |
| 			} else if (!strcmp(optarg, "lbg")) {
 | |
| 				platform = PLATFORM_LBG;
 | |
| 			} else if (!strcmp(optarg, "dnv")) {
 | |
| 				platform = PLATFORM_DNV;
 | |
| 			} else if (!strcmp(optarg, "ehl")) {
 | |
| 				platform = PLATFORM_EHL;
 | |
| 			} else if (!strcmp(optarg, "glk")) {
 | |
| 				platform = PLATFORM_GLK;
 | |
| 			} else if (!strcmp(optarg, "icl")) {
 | |
| 				platform = PLATFORM_ICL;
 | |
| 			} else if (!strcmp(optarg, "jsl")) {
 | |
| 				platform = PLATFORM_JSL;
 | |
| 			} else if (!strcmp(optarg, "sklkbl")) {
 | |
| 				platform = PLATFORM_SKLKBL;
 | |
| 			} else if (!strcmp(optarg, "tgl")) {
 | |
| 				platform = PLATFORM_TGL;
 | |
| 			} else if (!strcmp(optarg, "adl")) {
 | |
| 				platform = PLATFORM_ADL;
 | |
| 			} else if (!strcmp(optarg, "ifd2")) {
 | |
| 				platform = PLATFORM_IFD2;
 | |
| 			} else if (!strcmp(optarg, "mtl")) {
 | |
| 				platform = PLATFORM_MTL;
 | |
| 			} else if (!strcmp(optarg, "ptl")) {
 | |
| 				platform = PLATFORM_PTL;
 | |
| 			} else if (!strcmp(optarg, "wbg")) {
 | |
| 				platform = PLATFORM_WBG;
 | |
| 			} else {
 | |
| 				fprintf(stderr, "Unknown platform: %s\n", optarg);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 't':
 | |
| 			mode_validate = 1;
 | |
| 			break;
 | |
| 		case 'v':
 | |
| 			print_version();
 | |
| 			exit(EXIT_SUCCESS);
 | |
| 			break;
 | |
| 		case 'h':
 | |
| 		case '?':
 | |
| 		default:
 | |
| 			print_usage(argv[0]);
 | |
| 			exit(EXIT_SUCCESS);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject +
 | |
| 			mode_setstrap + mode_newlayout + (mode_spifreq | mode_em100 |
 | |
| 			mode_unlocked | mode_locked) + mode_altmedisable + mode_validate +
 | |
| 			(mode_gpr0_disable | mode_gpr0_enable) + mode_gpr0_status) > 1) {
 | |
| 		fprintf(stderr, "You may not specify more than one mode.\n\n");
 | |
| 		fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject +
 | |
| 			mode_setstrap + mode_newlayout + mode_spifreq + mode_em100 +
 | |
| 			mode_locked + mode_unlocked + mode_density + mode_altmedisable +
 | |
| 			mode_validate + (mode_gpr0_disable | mode_gpr0_enable) + mode_gpr0_status) == 0) {
 | |
| 		fprintf(stderr, "You need to specify a mode.\n\n");
 | |
| 		fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	if (optind + 1 != argc) {
 | |
| 		fprintf(stderr, "You need to specify a file.\n\n");
 | |
| 		fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	if (platform == -1)
 | |
| 		fprintf(stderr, "Warning: No platform specified. Output may be incomplete\n");
 | |
| 
 | |
| 	char *filename = argv[optind];
 | |
| 	int bios_fd = open(filename, O_RDONLY | O_BINARY);
 | |
| 	if (bios_fd == -1) {
 | |
| 		perror("Could not open file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	struct stat buf;
 | |
| 	if (fstat(bios_fd, &buf) == -1) {
 | |
| 		perror("Could not stat file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	int size = buf.st_size;
 | |
| 
 | |
| 	printf("File %s is %d bytes\n", filename, size);
 | |
| 
 | |
| 	char *image = malloc(size);
 | |
| 	if (!image) {
 | |
| 		printf("Out of memory.\n");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	if (read(bios_fd, image, size) != size) {
 | |
| 		perror("Could not read file");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	close(bios_fd);
 | |
| 
 | |
| 	// generate new filename
 | |
| 	if (new_filename == NULL) {
 | |
| 		new_filename = (char *)malloc((strlen(filename) + 5) * sizeof(char));
 | |
| 		if (!new_filename) {
 | |
| 			printf("Out of memory.\n");
 | |
| 			exit(EXIT_FAILURE);
 | |
| 		}
 | |
| 		// - 5: leave room for ".new\0"
 | |
| 		strcpy(new_filename, filename);
 | |
| 		strcat(new_filename, ".new");
 | |
| 	}
 | |
| 
 | |
| 	check_ifd_version(image, size);
 | |
| 
 | |
| 	if (mode_dump)
 | |
| 		dump_fd(image, size);
 | |
| 
 | |
| 	if (mode_layout)
 | |
| 		dump_flashrom_layout(image, size, layout_fname);
 | |
| 
 | |
| 	if (mode_fmap_template)
 | |
| 		create_fmap_template(image, size, layout_fname);
 | |
| 
 | |
| 	if (mode_extract)
 | |
| 		write_regions(image, size);
 | |
| 
 | |
| 	if (mode_validate)
 | |
| 		validate_layout(image, size);
 | |
| 
 | |
| 	if (mode_inject)
 | |
| 		inject_region(new_filename, image, size, region_type,
 | |
| 				region_fname);
 | |
| 
 | |
| 	if (mode_newlayout)
 | |
| 		new_layout(new_filename, image, size, layout_fname);
 | |
| 
 | |
| 	if (mode_spifreq)
 | |
| 		set_spi_frequency(new_filename, image, size, spifreq);
 | |
| 
 | |
| 	if (mode_density)
 | |
| 		set_chipdensity(new_filename, image, size, new_density);
 | |
| 
 | |
| 	if (mode_em100)
 | |
| 		set_em100_mode(new_filename, image, size);
 | |
| 
 | |
| 	if (mode_locked)
 | |
| 		lock_descriptor(new_filename, image, size);
 | |
| 
 | |
| 	if (mode_read)
 | |
| 		enable_cpu_read_me(new_filename, image, size);
 | |
| 
 | |
| 	if (mode_unlocked)
 | |
| 		unlock_descriptor(new_filename, image, size);
 | |
| 
 | |
| 	if (mode_gpr0_disable)
 | |
| 		disable_gpr0(new_filename, image, size);
 | |
| 
 | |
| 	if (mode_gpr0_enable)
 | |
| 		enable_gpr0(new_filename, image, size);
 | |
| 
 | |
| 	if (mode_gpr0_status)
 | |
| 		is_gpr0_protected(image, size);
 | |
| 
 | |
| 	if (mode_setstrap) {
 | |
| 		struct fpsba *fpsba = find_fpsba(image, size);
 | |
| 		const struct fdbar *fdb = find_fd(image, size);
 | |
| 		set_pchstrap(fpsba, fdb, pchstrap, value);
 | |
| 		write_image(new_filename, image, size);
 | |
| 	}
 | |
| 
 | |
| 	if (mode_altmedisable) {
 | |
| 		struct fpsba *fpsba = find_fpsba(image, size);
 | |
| 		struct fmsba *fmsba = find_fmsba(image, size);
 | |
| 		fpsba_set_altmedisable(fpsba, fmsba, altmedisable);
 | |
| 		write_image(new_filename, image, size);
 | |
| 	}
 | |
| 
 | |
| 	free(new_filename);
 | |
| 	free(image);
 | |
| 
 | |
| 	return 0;
 | |
| }
 |