summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/intel/ifs/Kconfig4
-rw-r--r--drivers/platform/x86/intel/ifs/core.c23
-rw-r--r--drivers/platform/x86/intel/ifs/ifs.h30
-rw-r--r--drivers/platform/x86/intel/ifs/load.c218
-rw-r--r--drivers/platform/x86/intel/ifs/runtest.c10
-rw-r--r--drivers/platform/x86/intel/ifs/sysfs.c41
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
};