summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/intel/ifs/load.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/intel/ifs/load.c')
-rw-r--r--drivers/platform/x86/intel/ifs/load.c158
1 files changed, 144 insertions, 14 deletions
diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c
index cefd0d886cfd..959b1878cae6 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();
@@ -260,6 +382,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 +397,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;