summaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/intel/ifs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:35:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:39:31 +0000
commit85c675d0d09a45a135bddd15d7b385f8758c32fb (patch)
tree76267dbc9b9a130337be3640948fe397b04ac629 /drivers/platform/x86/intel/ifs
parentAdding upstream version 6.6.15. (diff)
downloadlinux-85c675d0d09a45a135bddd15d7b385f8758c32fb.tar.xz
linux-85c675d0d09a45a135bddd15d7b385f8758c32fb.zip
Adding upstream version 6.7.7.upstream/6.7.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/platform/x86/intel/ifs')
-rw-r--r--drivers/platform/x86/intel/ifs/core.c15
-rw-r--r--drivers/platform/x86/intel/ifs/ifs.h64
-rw-r--r--drivers/platform/x86/intel/ifs/load.c167
-rw-r--r--drivers/platform/x86/intel/ifs/runtest.c72
4 files changed, 281 insertions, 37 deletions
diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c
index 306f886b52..7b11198d85 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 9319185589..56b9f3e3cf 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 cefd0d886c..2cf3b4a881 100644
--- a/drivers/platform/x86/intel/ifs/load.c
+++ b/drivers/platform/x86/intel/ifs/load.c
@@ -2,6 +2,7 @@
/* Copyright(c) 2022 Intel Corporation. */
#include <linux/firmware.h>
+#include <linux/sizes.h>
#include <asm/cpu.h>
#include <asm/microcode.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)
@@ -80,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.
@@ -109,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;
}
@@ -133,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;
}
}
@@ -147,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);
@@ -179,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;
}
@@ -199,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();
@@ -219,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();
@@ -227,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) {
@@ -240,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;
}
@@ -260,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;
@@ -274,6 +395,14 @@ 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);
+ ret = -EINVAL;
+ goto release;
+ }
+
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 43c864add7..13ecd55c66 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
@@ -336,7 +389,10 @@ int do_core_test(int cpu, struct device *dev)
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:
ret = -EINVAL;