Merge tag 'x86_microcode_for_v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / arch / x86 / kernel / cpu / intel.c
index 4278996..291d416 100644 (file)
@@ -210,12 +210,154 @@ int intel_cpu_collect_info(struct ucode_cpu_info *uci)
        csig.rev = intel_get_microcode_revision();
 
        uci->cpu_sig = csig;
-       uci->valid = 1;
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(intel_cpu_collect_info);
 
+/*
+ * Returns 1 if update has been found, 0 otherwise.
+ */
+int intel_find_matching_signature(void *mc, unsigned int csig, int cpf)
+{
+       struct microcode_header_intel *mc_hdr = mc;
+       struct extended_sigtable *ext_hdr;
+       struct extended_signature *ext_sig;
+       int i;
+
+       if (intel_cpu_signatures_match(csig, cpf, mc_hdr->sig, mc_hdr->pf))
+               return 1;
+
+       /* Look for ext. headers: */
+       if (get_totalsize(mc_hdr) <= get_datasize(mc_hdr) + MC_HEADER_SIZE)
+               return 0;
+
+       ext_hdr = mc + get_datasize(mc_hdr) + MC_HEADER_SIZE;
+       ext_sig = (void *)ext_hdr + EXT_HEADER_SIZE;
+
+       for (i = 0; i < ext_hdr->count; i++) {
+               if (intel_cpu_signatures_match(csig, cpf, ext_sig->sig, ext_sig->pf))
+                       return 1;
+               ext_sig++;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_find_matching_signature);
+
+/**
+ * intel_microcode_sanity_check() - Sanity check microcode file.
+ * @mc: Pointer to the microcode file contents.
+ * @print_err: Display failure reason if true, silent if false.
+ * @hdr_type: Type of file, i.e. normal microcode file or In Field Scan file.
+ *            Validate if the microcode header type matches with the type
+ *            specified here.
+ *
+ * Validate certain header fields and verify if computed checksum matches
+ * with the one specified in the header.
+ *
+ * Return: 0 if the file passes all the checks, -EINVAL if any of the checks
+ * fail.
+ */
+int intel_microcode_sanity_check(void *mc, bool print_err, int hdr_type)
+{
+       unsigned long total_size, data_size, ext_table_size;
+       struct microcode_header_intel *mc_header = mc;
+       struct extended_sigtable *ext_header = NULL;
+       u32 sum, orig_sum, ext_sigcount = 0, i;
+       struct extended_signature *ext_sig;
+
+       total_size = get_totalsize(mc_header);
+       data_size = get_datasize(mc_header);
+
+       if (data_size + MC_HEADER_SIZE > total_size) {
+               if (print_err)
+                       pr_err("Error: bad microcode data file size.\n");
+               return -EINVAL;
+       }
+
+       if (mc_header->ldrver != 1 || mc_header->hdrver != hdr_type) {
+               if (print_err)
+                       pr_err("Error: invalid/unknown microcode update format. Header type %d\n",
+                              mc_header->hdrver);
+               return -EINVAL;
+       }
+
+       ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
+       if (ext_table_size) {
+               u32 ext_table_sum = 0;
+               u32 *ext_tablep;
+
+               if (ext_table_size < EXT_HEADER_SIZE ||
+                   ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
+                       if (print_err)
+                               pr_err("Error: truncated extended signature table.\n");
+                       return -EINVAL;
+               }
+
+               ext_header = mc + MC_HEADER_SIZE + data_size;
+               if (ext_table_size != exttable_size(ext_header)) {
+                       if (print_err)
+                               pr_err("Error: extended signature table size mismatch.\n");
+                       return -EFAULT;
+               }
+
+               ext_sigcount = ext_header->count;
+
+               /*
+                * Check extended table checksum: the sum of all dwords that
+                * comprise a valid table must be 0.
+                */
+               ext_tablep = (u32 *)ext_header;
+
+               i = ext_table_size / sizeof(u32);
+               while (i--)
+                       ext_table_sum += ext_tablep[i];
+
+               if (ext_table_sum) {
+                       if (print_err)
+                               pr_warn("Bad extended signature table checksum, aborting.\n");
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * Calculate the checksum of update data and header. The checksum of
+        * valid update data and header including the extended signature table
+        * must be 0.
+        */
+       orig_sum = 0;
+       i = (MC_HEADER_SIZE + data_size) / sizeof(u32);
+       while (i--)
+               orig_sum += ((u32 *)mc)[i];
+
+       if (orig_sum) {
+               if (print_err)
+                       pr_err("Bad microcode data checksum, aborting.\n");
+               return -EINVAL;
+       }
+
+       if (!ext_table_size)
+               return 0;
+
+       /*
+        * Check extended signature checksum: 0 => valid.
+        */
+       for (i = 0; i < ext_sigcount; i++) {
+               ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
+                         EXT_SIGNATURE_SIZE * i;
+
+               sum = (mc_header->sig + mc_header->pf + mc_header->cksum) -
+                     (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
+               if (sum) {
+                       if (print_err)
+                               pr_err("Bad extended signature checksum, aborting.\n");
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);
+
 static void early_init_intel(struct cpuinfo_x86 *c)
 {
        u64 misc_enable;