diff options
Diffstat (limited to 'drivers/platform/x86/intel/ifs')
| -rw-r--r-- | drivers/platform/x86/intel/ifs/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/core.c | 23 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/ifs.h | 30 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/load.c | 218 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/runtest.c | 10 | ||||
| -rw-r--r-- | drivers/platform/x86/intel/ifs/sysfs.c | 41 | 
6 files changed, 191 insertions, 135 deletions
diff --git a/drivers/platform/x86/intel/ifs/Kconfig b/drivers/platform/x86/intel/ifs/Kconfig index c341a27cc1a3..3eded966757e 100644 --- a/drivers/platform/x86/intel/ifs/Kconfig +++ b/drivers/platform/x86/intel/ifs/Kconfig @@ -1,10 +1,6 @@  config INTEL_IFS  	tristate "Intel In Field Scan"  	depends on X86 && CPU_SUP_INTEL && 64BIT && SMP -	# Discussion on the list has shown that the sysfs API needs a bit -	# more work, mark this as broken for now -	depends on BROKEN -	select INTEL_IFS_DEVICE  	help  	  Enable support for the In Field Scan capability in select  	  CPUs. The capability allows for running low level tests via diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 27204e3d674d..206a617c2e02 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -4,6 +4,7 @@  #include <linux/module.h>  #include <linux/kdev_t.h>  #include <linux/semaphore.h> +#include <linux/slab.h>  #include <asm/cpu_device_id.h> @@ -22,6 +23,7 @@ MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids);  static struct ifs_device ifs_device = {  	.data = {  		.integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, +		.test_num = 0,  	},  	.misc = {  		.name = "intel_ifs_0", @@ -34,6 +36,7 @@ static int __init ifs_init(void)  {  	const struct x86_cpu_id *m;  	u64 msrval; +	int ret;  	m = x86_match_cpu(ifs_cpu_ids);  	if (!m) @@ -50,20 +53,26 @@ static int __init ifs_init(void)  	ifs_device.misc.groups = ifs_get_groups(); -	if ((msrval & BIT(ifs_device.data.integrity_cap_bit)) && -	    !misc_register(&ifs_device.misc)) { -		down(&ifs_sem); -		ifs_load_firmware(ifs_device.misc.this_device); -		up(&ifs_sem); -		return 0; +	if (!(msrval & BIT(ifs_device.data.integrity_cap_bit))) +		return -ENODEV; + +	ifs_device.data.pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL); +	if (!ifs_device.data.pkg_auth) +		return -ENOMEM; + +	ret = misc_register(&ifs_device.misc); +	if (ret) { +		kfree(ifs_device.data.pkg_auth); +		return ret;  	} -	return -ENODEV; +	return 0;  }  static void __exit ifs_exit(void)  {  	misc_deregister(&ifs_device.misc); +	kfree(ifs_device.data.pkg_auth);  }  module_init(ifs_init); diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 73c8e91cf144..046e39304fd5 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -33,13 +33,23 @@   * The driver loads the tests into memory reserved BIOS local to each CPU   * socket in a two step process using writes to MSRs to first load the   * SHA hashes for the test. Then the tests themselves. Status MSRs provide - * feedback on the success/failure of these steps. When a new test file - * is installed it can be loaded by writing to the driver reload file:: + * feedback on the success/failure of these steps.   * - *   # echo 1 > /sys/devices/virtual/misc/intel_ifs_0/reload + * The test files are kept in a fixed location: /lib/firmware/intel/ifs_0/ + * For e.g if there are 3 test files, they would be named in the following + * fashion: + * ff-mm-ss-01.scan + * ff-mm-ss-02.scan + * ff-mm-ss-03.scan + * (where ff refers to family, mm indicates model and ss indicates stepping)   * - * Similar to microcode, the current version of the scan tests is stored - * in a fixed location: /lib/firmware/intel/ifs.0/family-model-stepping.scan + * A different test file can be loaded by writing the numerical portion + * (e.g 1, 2 or 3 in the above scenario) into the curent_batch file. + * To load ff-mm-ss-02.scan, the following command can be used:: + * + *   # echo 2 > /sys/devices/virtual/misc/intel_ifs_0/current_batch + * + * The above file can also be read to know the currently loaded image.   *   * Running tests   * ------------- @@ -191,20 +201,26 @@ union ifs_status {   * struct ifs_data - attributes related to intel IFS driver   * @integrity_cap_bit: MSR_INTEGRITY_CAPS bit enumerating this test   * @loaded_version: stores the currently loaded ifs image version. + * @pkg_auth: array of bool storing per package auth status   * @loaded: If a valid test binary has been loaded into the memory   * @loading_error: Error occurred on another CPU while loading image   * @valid_chunks: number of chunks which could be validated.   * @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 + * @test_num: number indicating the test type   */  struct ifs_data {  	int	integrity_cap_bit; +	bool	*pkg_auth;  	int	loaded_version;  	bool	loaded;  	bool	loading_error;  	int	valid_chunks;  	int	status;  	u64	scan_details; +	u32	cur_batch; +	int	test_num;  };  struct ifs_work { @@ -225,10 +241,8 @@ static inline struct ifs_data *ifs_get_data(struct device *dev)  	return &d->data;  } -void ifs_load_firmware(struct device *dev); +int ifs_load_firmware(struct device *dev);  int do_core_test(int cpu, struct device *dev);  const struct attribute_group **ifs_get_groups(void); -extern struct semaphore ifs_sem; -  #endif diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index d056617ddc85..c5c24e6fdc43 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -3,27 +3,30 @@  #include <linux/firmware.h>  #include <asm/cpu.h> -#include <linux/slab.h>  #include <asm/microcode_intel.h>  #include "ifs.h" -struct ifs_header { -	u32 header_ver; -	u32 blob_revision; -	u32 date; -	u32 processor_sig; -	u32 check_sum; -	u32 loader_rev; -	u32 processor_flags; -	u32 metadata_size; -	u32 total_size; -	u32 fusa_info; -	u64 reserved; +#define IFS_CHUNK_ALIGNMENT	256 +union meta_data { +	struct { +		u32 meta_type;		// metadata type +		u32 meta_size;		// size of this entire struct including hdrs. +		u32 test_type;		// IFS test type +		u32 fusa_info;		// Fusa info +		u32 total_images;	// Total number of images +		u32 current_image;	// Current Image # +		u32 total_chunks;	// Total number of chunks in this image +		u32 starting_chunk;	// Starting chunk number in this image +		u32 size_per_chunk;	// size of each chunk +		u32 chunks_per_stride;	// number of chunks in a stride +	}; +	u8 padding[IFS_CHUNK_ALIGNMENT];  }; -#define IFS_HEADER_SIZE	(sizeof(struct ifs_header)) -static struct ifs_header *ifs_header_ptr;	/* pointer to the ifs image header */ +#define IFS_HEADER_SIZE	(sizeof(struct microcode_header_intel)) +#define META_TYPE_IFS	1 +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 */  static DECLARE_COMPLETION(ifs_done); @@ -44,6 +47,38 @@ static const char * const scan_authentication_status[] = {  	[2] = "Chunk authentication error. The hash of chunk did not match expected value"  }; +#define MC_HEADER_META_TYPE_END		(0) + +struct metadata_header { +	unsigned int		type; +	unsigned int		blk_size; +}; + +static struct metadata_header *find_meta_data(void *ucode, unsigned int meta_type) +{ +	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; +	if (!total_meta) +		return NULL; + +	meta_header = (ucode + MC_HEADER_SIZE + data_size) - total_meta; + +	while (meta_header->type != MC_HEADER_META_TYPE_END && +	       meta_header->blk_size && +	       meta_size < total_meta) { +		meta_size += meta_header->blk_size; +		if (meta_header->type == meta_type) +			return meta_header; + +		meta_header = (void *)meta_header + meta_header->blk_size; +	} +	return NULL; +} +  /*   * To copy scan hashes and authenticate test chunks, the initiating cpu must point   * to the EDX:EAX to the test image in linear address. @@ -111,6 +146,41 @@ done:  	complete(&ifs_done);  } +static int validate_ifs_metadata(struct device *dev) +{ +	struct ifs_data *ifsd = ifs_get_data(dev); +	union meta_data *ifs_meta; +	char test_file[64]; +	int ret = -EINVAL; + +	snprintf(test_file, sizeof(test_file), "%02x-%02x-%02x-%02x.scan", +		 boot_cpu_data.x86, boot_cpu_data.x86_model, +		 boot_cpu_data.x86_stepping, ifsd->cur_batch); + +	ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS); +	if (!ifs_meta) { +		dev_err(dev, "IFS Metadata missing in file %s\n", test_file); +		return ret; +	} + +	ifs_test_image_ptr = (u64)ifs_meta + sizeof(union meta_data); + +	/* Scan chunk start must be 256 byte aligned */ +	if (!IS_ALIGNED(ifs_test_image_ptr, IFS_CHUNK_ALIGNMENT)) { +		dev_err(dev, "Scan pattern is not aligned on %d bytes aligned in %s\n", +			IFS_CHUNK_ALIGNMENT, test_file); +		return ret; +	} + +	if (ifs_meta->current_image != ifsd->cur_batch) { +		dev_warn(dev, "Mismatch between filename %s and batch metadata 0x%02x\n", +			 test_file, ifs_meta->current_image); +		return ret; +	} + +	return 0; +} +  /*   * IFS requires scan chunks authenticated per each socket in the platform.   * Once the test chunk is authenticated, it is automatically copied to secured memory @@ -118,131 +188,83 @@ done:   */  static int scan_chunks_sanity_check(struct device *dev)  { -	int metadata_size, curr_pkg, cpu, ret = -ENOMEM;  	struct ifs_data *ifsd = ifs_get_data(dev); -	bool *package_authenticated;  	struct ifs_work local_work; -	char *test_ptr; +	int curr_pkg, cpu, ret; -	package_authenticated = kcalloc(topology_max_packages(), sizeof(bool), GFP_KERNEL); -	if (!package_authenticated) +	memset(ifsd->pkg_auth, 0, (topology_max_packages() * sizeof(bool))); +	ret = validate_ifs_metadata(dev); +	if (ret)  		return ret; -	metadata_size = ifs_header_ptr->metadata_size; - -	/* Spec says that if the Meta Data Size = 0 then it should be treated as 2000 */ -	if (metadata_size == 0) -		metadata_size = 2000; - -	/* Scan chunk start must be 256 byte aligned */ -	if ((metadata_size + IFS_HEADER_SIZE) % 256) { -		dev_err(dev, "Scan pattern offset within the binary is not 256 byte aligned\n"); -		return -EINVAL; -	} - -	test_ptr = (char *)ifs_header_ptr + IFS_HEADER_SIZE + metadata_size;  	ifsd->loading_error = false; - -	ifs_test_image_ptr = (u64)test_ptr; -	ifsd->loaded_version = ifs_header_ptr->blob_revision; +	ifsd->loaded_version = ifs_header_ptr->rev;  	/* copy the scan hash and authenticate per package */  	cpus_read_lock();  	for_each_online_cpu(cpu) {  		curr_pkg = topology_physical_package_id(cpu); -		if (package_authenticated[curr_pkg]) +		if (ifsd->pkg_auth[curr_pkg])  			continue;  		reinit_completion(&ifs_done);  		local_work.dev = dev;  		INIT_WORK(&local_work.w, copy_hashes_authenticate_chunks);  		schedule_work_on(cpu, &local_work.w);  		wait_for_completion(&ifs_done); -		if (ifsd->loading_error) +		if (ifsd->loading_error) { +			ret = -EIO;  			goto out; -		package_authenticated[curr_pkg] = 1; +		} +		ifsd->pkg_auth[curr_pkg] = 1;  	}  	ret = 0;  out:  	cpus_read_unlock(); -	kfree(package_authenticated);  	return ret;  } -static int ifs_sanity_check(struct device *dev, -			    const struct microcode_header_intel *mc_header) +static int image_sanity_check(struct device *dev, const struct microcode_header_intel *data)  { -	unsigned long total_size, data_size; -	u32 sum, *mc; - -	total_size = get_totalsize(mc_header); -	data_size = get_datasize(mc_header); +	struct ucode_cpu_info uci; -	if ((data_size + MC_HEADER_SIZE > total_size) || (total_size % sizeof(u32))) { -		dev_err(dev, "bad ifs data file size.\n"); +	/* Provide a specific error message when loading an older/unsupported image */ +	if (data->hdrver != MC_HEADER_TYPE_IFS) { +		dev_err(dev, "Header version %d not supported\n", data->hdrver);  		return -EINVAL;  	} -	if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { -		dev_err(dev, "invalid/unknown ifs update format.\n"); +	if (intel_microcode_sanity_check((void *)data, true, MC_HEADER_TYPE_IFS)) { +		dev_err(dev, "sanity check failed\n");  		return -EINVAL;  	} -	mc = (u32 *)mc_header; -	sum = 0; -	for (int i = 0; i < total_size / sizeof(u32); i++) -		sum += mc[i]; +	intel_cpu_collect_info(&uci); -	if (sum) { -		dev_err(dev, "bad ifs data checksum, aborting.\n"); +	if (!intel_find_matching_signature((void *)data, +					   uci.cpu_sig.sig, +					   uci.cpu_sig.pf)) { +		dev_err(dev, "cpu signature, processor flags not matching\n");  		return -EINVAL;  	}  	return 0;  } -static bool find_ifs_matching_signature(struct device *dev, struct ucode_cpu_info *uci, -					const struct microcode_header_intel *shdr) -{ -	unsigned int mc_size; - -	mc_size = get_totalsize(shdr); - -	if (!mc_size || ifs_sanity_check(dev, shdr) < 0) { -		dev_err(dev, "ifs sanity check failure\n"); -		return false; -	} - -	if (!intel_cpu_signatures_match(uci->cpu_sig.sig, uci->cpu_sig.pf, shdr->sig, shdr->pf)) { -		dev_err(dev, "ifs signature, pf not matching\n"); -		return false; -	} - -	return true; -} - -static bool ifs_image_sanity_check(struct device *dev, const struct microcode_header_intel *data) -{ -	struct ucode_cpu_info uci; - -	intel_cpu_collect_info(&uci); - -	return find_ifs_matching_signature(dev, &uci, data); -} -  /*   * Load ifs image. Before loading ifs module, the ifs image must be located - * in /lib/firmware/intel/ifs and named as {family/model/stepping}.{testname}. + * in /lib/firmware/intel/ifs_x/ and named as family-model-stepping-02x.{testname}.   */ -void ifs_load_firmware(struct device *dev) +int ifs_load_firmware(struct device *dev)  {  	struct ifs_data *ifsd = ifs_get_data(dev);  	const struct firmware *fw; -	char scan_path[32]; -	int ret; +	char scan_path[64]; +	int ret = -EINVAL; -	snprintf(scan_path, sizeof(scan_path), "intel/ifs/%02x-%02x-%02x.scan", -		 boot_cpu_data.x86, boot_cpu_data.x86_model, boot_cpu_data.x86_stepping); +	snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", +		 ifsd->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, +		 boot_cpu_data.x86_stepping, ifsd->cur_batch);  	ret = request_firmware_direct(&fw, scan_path, dev);  	if (ret) { @@ -250,17 +272,21 @@ void ifs_load_firmware(struct device *dev)  		goto done;  	} -	if (!ifs_image_sanity_check(dev, (struct microcode_header_intel *)fw->data)) { -		dev_err(dev, "ifs header sanity check failed\n"); +	ret = image_sanity_check(dev, (struct microcode_header_intel *)fw->data); +	if (ret)  		goto release; -	} -	ifs_header_ptr = (struct ifs_header *)fw->data; +	ifs_header_ptr = (struct microcode_header_intel *)fw->data;  	ifs_hash_ptr = (u64)(ifs_header_ptr + 1);  	ret = scan_chunks_sanity_check(dev); +	if (ret) +		dev_err(dev, "Load failure for batch: %02x\n", ifsd->cur_batch); +  release:  	release_firmware(fw);  done:  	ifsd->loaded = (ret == 0); + +	return ret;  } diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index b2ca2bb4501f..0bfd8fcdd7e8 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -78,14 +78,16 @@ static void message_not_tested(struct device *dev, int cpu, union ifs_status sta  static void message_fail(struct device *dev, int cpu, union ifs_status status)  { +	struct ifs_data *ifsd = ifs_get_data(dev); +  	/*  	 * control_error is set when the microcode runs into a problem  	 * loading the image from the reserved BIOS memory, or it has  	 * been corrupted. Reloading the image may fix this issue.  	 */  	if (status.control_error) { -		dev_err(dev, "CPU(s) %*pbl: could not execute from loaded scan image\n", -			cpumask_pr_args(cpu_smt_mask(cpu))); +		dev_err(dev, "CPU(s) %*pbl: could not execute from loaded scan image. Batch: %02x version: 0x%x\n", +			cpumask_pr_args(cpu_smt_mask(cpu)), ifsd->cur_batch, ifsd->loaded_version);  	}  	/* @@ -96,8 +98,8 @@ static void message_fail(struct device *dev, int cpu, union ifs_status status)  	 * the core being tested.  	 */  	if (status.signature_error) { -		dev_err(dev, "CPU(s) %*pbl: test signature incorrect.\n", -			cpumask_pr_args(cpu_smt_mask(cpu))); +		dev_err(dev, "CPU(s) %*pbl: test signature incorrect. Batch: %02x version: 0x%x\n", +			cpumask_pr_args(cpu_smt_mask(cpu)), ifsd->cur_batch, ifsd->loaded_version);  	}  } diff --git a/drivers/platform/x86/intel/ifs/sysfs.c b/drivers/platform/x86/intel/ifs/sysfs.c index 37d8380d6fa8..ee636a76b083 100644 --- a/drivers/platform/x86/intel/ifs/sysfs.c +++ b/drivers/platform/x86/intel/ifs/sysfs.c @@ -13,7 +13,7 @@   * Protects against simultaneous tests on multiple cores, or   * reloading can file while a test is in progress   */ -DEFINE_SEMAPHORE(ifs_sem); +static DEFINE_SEMAPHORE(ifs_sem);  /*   * The sysfs interface to check additional details of last test @@ -87,33 +87,42 @@ static ssize_t run_test_store(struct device *dev,  static DEVICE_ATTR_WO(run_test); -/* - * Reload the IFS image. When user wants to install new IFS image - */ -static ssize_t reload_store(struct device *dev, -			    struct device_attribute *attr, -			    const char *buf, size_t count) +static ssize_t current_batch_store(struct device *dev, +				   struct device_attribute *attr, +				   const char *buf, size_t count)  {  	struct ifs_data *ifsd = ifs_get_data(dev); -	bool res; - +	unsigned int cur_batch; +	int rc; -	if (kstrtobool(buf, &res)) +	rc = kstrtouint(buf, 0, &cur_batch); +	if (rc < 0 || cur_batch > 0xff)  		return -EINVAL; -	if (!res) -		return count;  	if (down_interruptible(&ifs_sem))  		return -EINTR; -	ifs_load_firmware(dev); +	ifsd->cur_batch = cur_batch; + +	rc = ifs_load_firmware(dev);  	up(&ifs_sem); -	return ifsd->loaded ? count : -ENODEV; +	return (rc == 0) ? count : rc; +} + +static ssize_t current_batch_show(struct device *dev, +				  struct device_attribute *attr, char *buf) +{ +	struct ifs_data *ifsd = ifs_get_data(dev); + +	if (!ifsd->loaded) +		return sysfs_emit(buf, "none\n"); +	else +		return sysfs_emit(buf, "0x%02x\n", ifsd->cur_batch);  } -static DEVICE_ATTR_WO(reload); +static DEVICE_ATTR_RW(current_batch);  /*   * Display currently loaded IFS image version. @@ -136,7 +145,7 @@ static struct attribute *plat_ifs_attrs[] = {  	&dev_attr_details.attr,  	&dev_attr_status.attr,  	&dev_attr_run_test.attr, -	&dev_attr_reload.attr, +	&dev_attr_current_batch.attr,  	&dev_attr_image_version.attr,  	NULL  };  |