Merge tag 'x86_microcode_for_v6.7_rc1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / platform / x86 / intel / ifs / load.c
index cefd0d8..a1ee1a7 100644 (file)
@@ -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,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;