diff options
Diffstat (limited to 'drivers/platform/x86/intel/ifs')
| -rw-r--r-- | drivers/platform/x86/intel/ifs/core.c | 15 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/ifs.h | 64 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/load.c | 173 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/runtest.c | 79 | 
4 files changed, 288 insertions, 43 deletions
| diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 306f886b52d2..7b11198d85a1 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -1,6 +1,7 @@  // SPDX-License-Identifier: GPL-2.0-only  /* Copyright(c) 2022 Intel Corporation. */ +#include <linux/bitfield.h>  #include <linux/module.h>  #include <linux/kdev_t.h>  #include <linux/semaphore.h> @@ -10,13 +11,16 @@  #include "ifs.h" -#define X86_MATCH(model)				\ +#define X86_MATCH(model, array_gen)				\  	X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6,	\ -		INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, NULL) +		INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, array_gen)  static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { -	X86_MATCH(SAPPHIRERAPIDS_X), -	X86_MATCH(EMERALDRAPIDS_X), +	X86_MATCH(SAPPHIRERAPIDS_X, ARRAY_GEN0), +	X86_MATCH(EMERALDRAPIDS_X, ARRAY_GEN0), +	X86_MATCH(GRANITERAPIDS_X, ARRAY_GEN0), +	X86_MATCH(GRANITERAPIDS_D, ARRAY_GEN0), +	X86_MATCH(ATOM_CRESTMONT_X, ARRAY_GEN1),  	{}  };  MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); @@ -94,6 +98,9 @@ static int __init ifs_init(void)  	for (i = 0; i < IFS_NUMTESTS; i++) {  		if (!(msrval & BIT(ifs_devices[i].test_caps->integrity_cap_bit)))  			continue; +		ifs_devices[i].rw_data.generation = FIELD_GET(MSR_INTEGRITY_CAPS_SAF_GEN_MASK, +							      msrval); +		ifs_devices[i].rw_data.array_gen = (u32)m->driver_data;  		ret = misc_register(&ifs_devices[i].misc);  		if (ret)  			goto err_exit; diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 93191855890f..56b9f3e3cf76 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -137,6 +137,10 @@  #define MSR_CHUNKS_AUTHENTICATION_STATUS	0x000002c5  #define MSR_ACTIVATE_SCAN			0x000002c6  #define MSR_SCAN_STATUS				0x000002c7 +#define MSR_ARRAY_TRIGGER			0x000002d6 +#define MSR_ARRAY_STATUS			0x000002d7 +#define MSR_SAF_CTRL				0x000004f0 +  #define SCAN_NOT_TESTED				0  #define SCAN_TEST_PASS				1  #define SCAN_TEST_FAIL				2 @@ -144,6 +148,9 @@  #define IFS_TYPE_SAF			0  #define IFS_TYPE_ARRAY_BIST		1 +#define ARRAY_GEN0			0 +#define ARRAY_GEN1			1 +  /* MSR_SCAN_HASHES_STATUS bit fields */  union ifs_scan_hashes_status {  	u64	data; @@ -158,6 +165,19 @@ union ifs_scan_hashes_status {  	};  }; +union ifs_scan_hashes_status_gen2 { +	u64	data; +	struct { +		u16	chunk_size; +		u16	num_chunks; +		u32	error_code		:8; +		u32	chunks_in_stride	:9; +		u32	rsvd			:2; +		u32	max_core_limit		:12; +		u32	valid			:1; +	}; +}; +  /* MSR_CHUNKS_AUTH_STATUS bit fields */  union ifs_chunks_auth_status {  	u64	data; @@ -170,13 +190,31 @@ union ifs_chunks_auth_status {  	};  }; +union ifs_chunks_auth_status_gen2 { +	u64	data; +	struct { +		u16	valid_chunks; +		u16	total_chunks; +		u32	error_code	:8; +		u32	rsvd2		:24; +	}; +}; +  /* MSR_ACTIVATE_SCAN bit fields */  union ifs_scan {  	u64	data;  	struct { -		u32	start	:8; -		u32	stop	:8; -		u32	rsvd	:16; +		union { +			struct { +				u8	start; +				u8	stop; +				u16	rsvd; +			} gen0; +			struct { +				u16	start; +				u16	stop; +			} gen2; +		};  		u32	delay	:31;  		u32	sigmce	:1;  	}; @@ -186,9 +224,17 @@ union ifs_scan {  union ifs_status {  	u64	data;  	struct { -		u32	chunk_num		:8; -		u32	chunk_stop_index	:8; -		u32	rsvd1			:16; +		union { +			struct { +				u8	chunk_num; +				u8	chunk_stop_index; +				u16	rsvd1; +			} gen0; +			struct { +				u16	chunk_num; +				u16	chunk_stop_index; +			} gen2; +		};  		u32	error_code		:8;  		u32	rsvd2			:22;  		u32	control_error		:1; @@ -229,6 +275,9 @@ struct ifs_test_caps {   * @status: it holds simple status pass/fail/untested   * @scan_details: opaque scan status code from h/w   * @cur_batch: number indicating the currently loaded test file + * @generation: IFS test generation enumerated by hardware + * @chunk_size: size of a test chunk + * @array_gen: test generation of array test   */  struct ifs_data {  	int	loaded_version; @@ -238,6 +287,9 @@ struct ifs_data {  	int	status;  	u64	scan_details;  	u32	cur_batch; +	u32	generation; +	u32	chunk_size; +	u32	array_gen;  };  struct ifs_work { diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index e6ae8265f3a3..a1ee1a74fc3c 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -2,8 +2,9 @@  /* Copyright(c) 2022 Intel Corporation. */  #include <linux/firmware.h> +#include <linux/sizes.h>  #include <asm/cpu.h> -#include <asm/microcode_intel.h> +#include <asm/microcode.h>  #include "ifs.h" @@ -26,6 +27,11 @@ union meta_data {  #define IFS_HEADER_SIZE	(sizeof(struct microcode_header_intel))  #define META_TYPE_IFS	1 +#define INVALIDATE_STRIDE	0x1UL +#define IFS_GEN_STRIDE_AWARE	2 +#define AUTH_INTERRUPTED_ERROR	5 +#define IFS_AUTH_RETRY_CT	10 +  static  struct microcode_header_intel *ifs_header_ptr;	/* pointer to the ifs image header */  static u64 ifs_hash_ptr;			/* Address of ifs metadata (hash) */  static u64 ifs_test_image_ptr;			/* 256B aligned address of test pattern */ @@ -44,7 +50,10 @@ static const char * const scan_hash_status[] = {  static const char * const scan_authentication_status[] = {  	[0] = "No error reported",  	[1] = "Attempt to authenticate a chunk which is already marked as authentic", -	[2] = "Chunk authentication error. The hash of chunk did not match expected value" +	[2] = "Chunk authentication error. The hash of chunk did not match expected value", +	[3] = "Reserved", +	[4] = "Chunk outside the current stride", +	[5] = "Authentication flow interrupted",  };  #define MC_HEADER_META_TYPE_END		(0) @@ -56,12 +65,13 @@ struct metadata_header {  static struct metadata_header *find_meta_data(void *ucode, unsigned int meta_type)  { +	struct microcode_header_intel *hdr = &((struct microcode_intel *)ucode)->hdr;  	struct metadata_header *meta_header;  	unsigned long data_size, total_meta;  	unsigned long meta_size = 0; -	data_size = get_datasize(ucode); -	total_meta = ((struct microcode_intel *)ucode)->hdr.metasize; +	data_size = intel_microcode_get_datasize(hdr); +	total_meta = hdr->metasize;  	if (!total_meta)  		return NULL; @@ -79,6 +89,23 @@ static struct metadata_header *find_meta_data(void *ucode, unsigned int meta_typ  	return NULL;  } +static void hashcopy_err_message(struct device *dev, u32 err_code) +{ +	if (err_code >= ARRAY_SIZE(scan_hash_status)) +		dev_err(dev, "invalid error code 0x%x for hash copy\n", err_code); +	else +		dev_err(dev, "Hash copy error : %s\n", scan_hash_status[err_code]); +} + +static void auth_err_message(struct device *dev, u32 err_code) +{ +	if (err_code >= ARRAY_SIZE(scan_authentication_status)) +		dev_err(dev, "invalid error code 0x%x for authentication\n", err_code); +	else +		dev_err(dev, "Chunk authentication error : %s\n", +			scan_authentication_status[err_code]); +} +  /*   * To copy scan hashes and authenticate test chunks, the initiating cpu must point   * to the EDX:EAX to the test image in linear address. @@ -108,11 +135,7 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)  	if (!hashes_status.valid) {  		ifsd->loading_error = true; -		if (err_code >= ARRAY_SIZE(scan_hash_status)) { -			dev_err(dev, "invalid error code 0x%x for hash copy\n", err_code); -			goto done; -		} -		dev_err(dev, "Hash copy error : %s", scan_hash_status[err_code]); +		hashcopy_err_message(dev, err_code);  		goto done;  	} @@ -132,13 +155,7 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)  		if (err_code) {  			ifsd->loading_error = true; -			if (err_code >= ARRAY_SIZE(scan_authentication_status)) { -				dev_err(dev, -					"invalid error code 0x%x for authentication\n", err_code); -				goto done; -			} -			dev_err(dev, "Chunk authentication error %s\n", -				scan_authentication_status[err_code]); +			auth_err_message(dev, err_code);  			goto done;  		}  	} @@ -146,6 +163,102 @@ done:  	complete(&ifs_done);  } +static int get_num_chunks(int gen, union ifs_scan_hashes_status_gen2 status) +{ +	return gen >= IFS_GEN_STRIDE_AWARE ? status.chunks_in_stride : status.num_chunks; +} + +static bool need_copy_scan_hashes(struct ifs_data *ifsd) +{ +	return !ifsd->loaded || +		ifsd->generation < IFS_GEN_STRIDE_AWARE || +		ifsd->loaded_version != ifs_header_ptr->rev; +} + +static int copy_hashes_authenticate_chunks_gen2(struct device *dev) +{ +	union ifs_scan_hashes_status_gen2 hashes_status; +	union ifs_chunks_auth_status_gen2 chunk_status; +	u32 err_code, valid_chunks, total_chunks; +	int i, num_chunks, chunk_size; +	union meta_data *ifs_meta; +	int starting_chunk_nr; +	struct ifs_data *ifsd; +	u64 linear_addr, base; +	u64 chunk_table[2]; +	int retry_count; + +	ifsd = ifs_get_data(dev); + +	if (need_copy_scan_hashes(ifsd)) { +		wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); +		rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); + +		/* enumerate the scan image information */ +		chunk_size = hashes_status.chunk_size * SZ_1K; +		err_code = hashes_status.error_code; + +		num_chunks = get_num_chunks(ifsd->generation, hashes_status); + +		if (!hashes_status.valid) { +			hashcopy_err_message(dev, err_code); +			return -EIO; +		} +		ifsd->loaded_version = ifs_header_ptr->rev; +		ifsd->chunk_size = chunk_size; +	} else { +		num_chunks = ifsd->valid_chunks; +		chunk_size = ifsd->chunk_size; +	} + +	if (ifsd->generation >= IFS_GEN_STRIDE_AWARE) { +		wrmsrl(MSR_SAF_CTRL, INVALIDATE_STRIDE); +		rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); +		if (chunk_status.valid_chunks != 0) { +			dev_err(dev, "Couldn't invalidate installed stride - %d\n", +				chunk_status.valid_chunks); +			return -EIO; +		} +	} + +	base = ifs_test_image_ptr; +	ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS); +	starting_chunk_nr = ifs_meta->starting_chunk; + +	/* scan data authentication and copy chunks to secured memory */ +	for (i = 0; i < num_chunks; i++) { +		retry_count = IFS_AUTH_RETRY_CT; +		linear_addr = base + i * chunk_size; + +		chunk_table[0] = starting_chunk_nr + i; +		chunk_table[1] = linear_addr; +		do { +			wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table); +			rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); +			err_code = chunk_status.error_code; +		} while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count); + +		if (err_code) { +			ifsd->loading_error = true; +			auth_err_message(dev, err_code); +			return -EIO; +		} +	} + +	valid_chunks = chunk_status.valid_chunks; +	total_chunks = chunk_status.total_chunks; + +	if (valid_chunks != total_chunks) { +		ifsd->loading_error = true; +		dev_err(dev, "Couldn't authenticate all the chunks. Authenticated %d total %d.\n", +			valid_chunks, total_chunks); +		return -EIO; +	} +	ifsd->valid_chunks = valid_chunks; + +	return 0; +} +  static int validate_ifs_metadata(struct device *dev)  {  	struct ifs_data *ifsd = ifs_get_data(dev); @@ -178,6 +291,13 @@ static int validate_ifs_metadata(struct device *dev)  		return ret;  	} +	if (ifs_meta->chunks_per_stride && +	    (ifs_meta->starting_chunk % ifs_meta->chunks_per_stride != 0)) { +		dev_warn(dev, "Starting chunk num %u not a multiple of chunks_per_stride %u\n", +			 ifs_meta->starting_chunk, ifs_meta->chunks_per_stride); +		return ret; +	} +  	return 0;  } @@ -198,7 +318,9 @@ static int scan_chunks_sanity_check(struct device *dev)  		return ret;  	ifsd->loading_error = false; -	ifsd->loaded_version = ifs_header_ptr->rev; + +	if (ifsd->generation > 0) +		return copy_hashes_authenticate_chunks_gen2(dev);  	/* copy the scan hash and authenticate per package */  	cpus_read_lock(); @@ -218,6 +340,7 @@ static int scan_chunks_sanity_check(struct device *dev)  		ifs_pkg_auth[curr_pkg] = 1;  	}  	ret = 0; +	ifsd->loaded_version = ifs_header_ptr->rev;  out:  	cpus_read_unlock(); @@ -226,7 +349,7 @@ out:  static int image_sanity_check(struct device *dev, const struct microcode_header_intel *data)  { -	struct ucode_cpu_info uci; +	struct cpu_signature sig;  	/* Provide a specific error message when loading an older/unsupported image */  	if (data->hdrver != MC_HEADER_TYPE_IFS) { @@ -239,11 +362,9 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_  		return -EINVAL;  	} -	intel_cpu_collect_info(&uci); +	intel_collect_cpu_info(&sig); -	if (!intel_find_matching_signature((void *)data, -					   uci.cpu_sig.sig, -					   uci.cpu_sig.pf)) { +	if (!intel_find_matching_signature((void *)data, &sig)) {  		dev_err(dev, "cpu signature, processor flags not matching\n");  		return -EINVAL;  	} @@ -259,6 +380,7 @@ int ifs_load_firmware(struct device *dev)  {  	const struct ifs_test_caps *test = ifs_get_test_caps(dev);  	struct ifs_data *ifsd = ifs_get_data(dev); +	unsigned int expected_size;  	const struct firmware *fw;  	char scan_path[64];  	int ret = -EINVAL; @@ -273,6 +395,13 @@ int ifs_load_firmware(struct device *dev)  		goto done;  	} +	expected_size = ((struct microcode_header_intel *)fw->data)->totalsize; +	if (fw->size != expected_size) { +		dev_err(dev, "File size mismatch (expected %u, actual %zu). Corrupted IFS image.\n", +			expected_size, fw->size); +		return -EINVAL; +	} +  	ret = image_sanity_check(dev, (struct microcode_header_intel *)fw->data);  	if (ret)  		goto release; diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 1061eb7ec399..13ecd55c6668 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -40,6 +40,8 @@ enum ifs_status_err_code {  	IFS_UNASSIGNED_ERROR_CODE		= 7,  	IFS_EXCEED_NUMBER_OF_THREADS_CONCURRENT	= 8,  	IFS_INTERRUPTED_DURING_EXECUTION	= 9, +	IFS_UNASSIGNED_ERROR_CODE_0xA		= 0xA, +	IFS_CORRUPTED_CHUNK		= 0xB,  };  static const char * const scan_test_status[] = { @@ -55,6 +57,8 @@ static const char * const scan_test_status[] = {  	[IFS_EXCEED_NUMBER_OF_THREADS_CONCURRENT] =  	"Exceeded number of Logical Processors (LP) allowed to run Scan-At-Field concurrently",  	[IFS_INTERRUPTED_DURING_EXECUTION] = "Interrupt occurred prior to SCAN start", +	[IFS_UNASSIGNED_ERROR_CODE_0xA] = "Unassigned error code 0xA", +	[IFS_CORRUPTED_CHUNK] = "Scan operation aborted due to corrupted image. Try reloading",  };  static void message_not_tested(struct device *dev, int cpu, union ifs_status status) @@ -123,6 +127,8 @@ static bool can_restart(union ifs_status status)  	case IFS_MISMATCH_ARGUMENTS_BETWEEN_THREADS:  	case IFS_CORE_NOT_CAPABLE_CURRENTLY:  	case IFS_UNASSIGNED_ERROR_CODE: +	case IFS_UNASSIGNED_ERROR_CODE_0xA: +	case IFS_CORRUPTED_CHUNK:  		break;  	}  	return false; @@ -171,21 +177,31 @@ static void ifs_test_core(int cpu, struct device *dev)  	union ifs_status status;  	unsigned long timeout;  	struct ifs_data *ifsd; +	int to_start, to_stop; +	int status_chunk;  	u64 msrvals[2];  	int retries;  	ifsd = ifs_get_data(dev); -	activate.rsvd = 0; +	activate.gen0.rsvd = 0;  	activate.delay = IFS_THREAD_WAIT;  	activate.sigmce = 0; -	activate.start = 0; -	activate.stop = ifsd->valid_chunks - 1; +	to_start = 0; +	to_stop = ifsd->valid_chunks - 1; + +	if (ifsd->generation) { +		activate.gen2.start = to_start; +		activate.gen2.stop = to_stop; +	} else { +		activate.gen0.start = to_start; +		activate.gen0.stop = to_stop; +	}  	timeout = jiffies + HZ / 2;  	retries = MAX_IFS_RETRIES; -	while (activate.start <= activate.stop) { +	while (to_start <= to_stop) {  		if (time_after(jiffies, timeout)) {  			status.error_code = IFS_SW_TIMEOUT;  			break; @@ -196,13 +212,14 @@ static void ifs_test_core(int cpu, struct device *dev)  		status.data = msrvals[1]; -		trace_ifs_status(cpu, activate, status); +		trace_ifs_status(cpu, to_start, to_stop, status.data);  		/* Some cases can be retried, give up for others */  		if (!can_restart(status))  			break; -		if (status.chunk_num == activate.start) { +		status_chunk = ifsd->generation ? status.gen2.chunk_num : status.gen0.chunk_num; +		if (status_chunk == to_start) {  			/* Check for forward progress */  			if (--retries == 0) {  				if (status.error_code == IFS_NO_ERROR) @@ -211,7 +228,11 @@ static void ifs_test_core(int cpu, struct device *dev)  			}  		} else {  			retries = MAX_IFS_RETRIES; -			activate.start = status.chunk_num; +			if (ifsd->generation) +				activate.gen2.start = status_chunk; +			else +				activate.gen0.start = status_chunk; +			to_start = status_chunk;  		}  	} @@ -308,6 +329,38 @@ static void ifs_array_test_core(int cpu, struct device *dev)  		ifsd->status = SCAN_TEST_PASS;  } +#define ARRAY_GEN1_TEST_ALL_ARRAYS	0x0ULL +#define ARRAY_GEN1_STATUS_FAIL		0x1ULL + +static int do_array_test_gen1(void *status) +{ +	int cpu = smp_processor_id(); +	int first; + +	first = cpumask_first(cpu_smt_mask(cpu)); + +	if (cpu == first) { +		wrmsrl(MSR_ARRAY_TRIGGER, ARRAY_GEN1_TEST_ALL_ARRAYS); +		rdmsrl(MSR_ARRAY_STATUS, *((u64 *)status)); +	} + +	return 0; +} + +static void ifs_array_test_gen1(int cpu, struct device *dev) +{ +	struct ifs_data *ifsd = ifs_get_data(dev); +	u64 status = 0; + +	stop_core_cpuslocked(cpu, do_array_test_gen1, &status); +	ifsd->scan_details = status; + +	if (status & ARRAY_GEN1_STATUS_FAIL) +		ifsd->status = SCAN_TEST_FAIL; +	else +		ifsd->status = SCAN_TEST_PASS; +} +  /*   * Initiate per core test. It wakes up work queue threads on the target cpu and   * its sibling cpu. Once all sibling threads wake up, the scan test gets executed and @@ -331,14 +384,18 @@ int do_core_test(int cpu, struct device *dev)  	switch (test->test_num) {  	case IFS_TYPE_SAF:  		if (!ifsd->loaded) -			return -EPERM; -		ifs_test_core(cpu, dev); +			ret = -EPERM; +		else +			ifs_test_core(cpu, dev);  		break;  	case IFS_TYPE_ARRAY_BIST: -		ifs_array_test_core(cpu, dev); +		if (ifsd->array_gen == ARRAY_GEN0) +			ifs_array_test_core(cpu, dev); +		else +			ifs_array_test_gen1(cpu, dev);  		break;  	default: -		return -EINVAL; +		ret = -EINVAL;  	}  out:  	cpus_read_unlock(); |