Commit a70210f4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_microcode_for_v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 microcode and IFS updates from Borislav Petkov:
 "The IFS (In-Field Scan) stuff goes through tip because the IFS driver
  uses the same structures and similar functionality as the microcode
  loader and it made sense to route it all through this branch so that
  there are no conflicts.

   - Add support for multiple testing sequences to the Intel In-Field
     Scan driver in order to be able to run multiple different test
     patterns. Rework things and remove the BROKEN dependency so that
     the driver can be enabled (Jithu Joseph)

   - Remove the subsys interface usage in the microcode loader because
     it is not really needed

   - A couple of smaller fixes and cleanups"

* tag 'x86_microcode_for_v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (24 commits)
  x86/microcode/intel: Do not retry microcode reloading on the APs
  x86/microcode/intel: Do not print microcode revision and processor flags
  platform/x86/intel/ifs: Add missing kernel-doc entry
  Revert "platform/x86/intel/ifs: Mark as BROKEN"
  Documentation/ABI: Update IFS ABI doc
  platform/x86/intel/ifs: Add current_batch sysfs entry
  platform/x86/intel/ifs: Remove reload sysfs entry
  platform/x86/intel/ifs: Add metadata validation
  platform/x86/intel/ifs: Use generic microcode headers and functions
  platform/x86/intel/ifs: Add metadata support
  x86/microcode/intel: Use a reserved field for metasize
  x86/microcode/intel: Add hdr_type to intel_microcode_sanity_check()
  x86/microcode/intel: Reuse microcode_sanity_check()
  x86/microcode/intel: Use appropriate type in microcode_sanity_check()
  x86/microcode/intel: Reuse find_matching_signature()
  platform/x86/intel/ifs: Remove memory allocation from load path
  platform/x86/intel/ifs: Remove image loading during init
  platform/x86/intel/ifs: Return a more appropriate error code
  platform/x86/intel/ifs: Remove unused selection
  x86/microcode: Drop struct ucode_cpu_info.valid
  ...
parents 3ef3ace4 be1b670f
What: /sys/devices/virtual/misc/intel_ifs_<N>/run_test
Date: April 21 2022
KernelVersion: 5.19
Date: Nov 16 2022
KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: Write <cpu#> to trigger IFS test for one online core.
Note that the test is per core. The cpu# can be
for any thread on the core. Running on one thread
completes the test for the core containing that thread.
Example: to test the core containing cpu5: echo 5 >
/sys/devices/platform/intel_ifs.<N>/run_test
/sys/devices/virtual/misc/intel_ifs_<N>/run_test
What: /sys/devices/virtual/misc/intel_ifs_<N>/status
Date: April 21 2022
KernelVersion: 5.19
Date: Nov 16 2022
KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: The status of the last test. It can be one of "pass", "fail"
or "untested".
What: /sys/devices/virtual/misc/intel_ifs_<N>/details
Date: April 21 2022
KernelVersion: 5.19
Date: Nov 16 2022
KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: Additional information regarding the last test. The details file reports
the hex value of the SCAN_STATUS MSR. Note that the error_code field
may contain driver defined software code not defined in the Intel SDM.
What: /sys/devices/virtual/misc/intel_ifs_<N>/image_version
Date: April 21 2022
KernelVersion: 5.19
Date: Nov 16 2022
KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: Version (hexadecimal) of loaded IFS binary image. If no scan image
is loaded reports "none".
What: /sys/devices/virtual/misc/intel_ifs_<N>/reload
Date: April 21 2022
KernelVersion: 5.19
What: /sys/devices/virtual/misc/intel_ifs_<N>/current_batch
Date: Nov 16 2022
KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: Write "1" (or "y" or "Y") to reload the IFS image from
/lib/firmware/intel/ifs/ff-mm-ss.scan.
Description: Write a number less than or equal to 0xff to load an IFS test image.
The number written treated as the 2 digit suffix in the following file name:
/lib/firmware/intel/ifs_<N>/ff-mm-ss-02x.scan
Reading the file will provide the suffix of the currently loaded IFS test image.
......@@ -95,5 +95,7 @@ static inline bool intel_cpu_signatures_match(unsigned int s1, unsigned int p1,
}
extern u64 x86_read_arch_cap_msr(void);
int intel_find_matching_signature(void *mc, unsigned int csig, int cpf);
int intel_microcode_sanity_check(void *mc, bool print_err, int hdr_type);
#endif /* _ASM_X86_CPU_H */
......@@ -33,8 +33,7 @@ enum ucode_state {
};
struct microcode_ops {
enum ucode_state (*request_microcode_fw) (int cpu, struct device *,
bool refresh_fw);
enum ucode_state (*request_microcode_fw) (int cpu, struct device *);
void (*microcode_fini_cpu) (int cpu);
......@@ -50,7 +49,6 @@ struct microcode_ops {
struct ucode_cpu_info {
struct cpu_signature cpu_sig;
int valid;
void *mc;
};
extern struct ucode_cpu_info ucode_cpu_info[];
......
......@@ -14,7 +14,8 @@ struct microcode_header_intel {
unsigned int pf;
unsigned int datasize;
unsigned int totalsize;
unsigned int reserved[3];
unsigned int metasize;
unsigned int reserved[2];
};
struct microcode_intel {
......@@ -41,6 +42,8 @@ struct extended_sigtable {
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable))
#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature))
#define MC_HEADER_TYPE_MICROCODE 1
#define MC_HEADER_TYPE_IFS 2
#define get_totalsize(mc) \
(((struct microcode_intel *)mc)->hdr.datasize ? \
......
......@@ -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;
......
......@@ -901,8 +901,7 @@ load_microcode_amd(bool save, u8 family, const u8 *data, size_t size)
*
* These might be larger than 2K.
*/
static enum ucode_state request_microcode_amd(int cpu, struct device *device,
bool refresh_fw)
static enum ucode_state request_microcode_amd(int cpu, struct device *device)
{
char fw_name[36] = "amd-ucode/microcode_amd.bin";
struct cpuinfo_x86 *c = &cpu_data(cpu);
......@@ -911,7 +910,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
const struct firmware *fw;
/* reload ucode container only on the boot cpu */
if (!refresh_fw || !bsp)
if (!bsp)
return UCODE_OK;
if (c->x86 >= 0x15)
......
......@@ -319,60 +319,6 @@ void reload_early_microcode(void)
}
}
static void collect_cpu_info_local(void *arg)
{
struct cpu_info_ctx *ctx = arg;
ctx->err = microcode_ops->collect_cpu_info(smp_processor_id(),
ctx->cpu_sig);
}
static int collect_cpu_info_on_target(int cpu, struct cpu_signature *cpu_sig)
{
struct cpu_info_ctx ctx = { .cpu_sig = cpu_sig, .err = 0 };
int ret;
ret = smp_call_function_single(cpu, collect_cpu_info_local, &ctx, 1);
if (!ret)
ret = ctx.err;
return ret;
}
static int collect_cpu_info(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
int ret;
memset(uci, 0, sizeof(*uci));
ret = collect_cpu_info_on_target(cpu, &uci->cpu_sig);
if (!ret)
uci->valid = 1;
return ret;
}
static void apply_microcode_local(void *arg)
{
enum ucode_state *err = arg;
*err = microcode_ops->apply_microcode(smp_processor_id());
}
static int apply_microcode_on_target(int cpu)
{
enum ucode_state err;
int ret;
ret = smp_call_function_single(cpu, apply_microcode_local, &err, 1);
if (!ret) {
if (err == UCODE_ERROR)
ret = 1;
}
return ret;
}
/* fake device for request_firmware */
static struct platform_device *microcode_pdev;
......@@ -458,7 +404,7 @@ static int __reload_late(void *info)
* below.
*/
if (cpumask_first(topology_sibling_cpumask(cpu)) == cpu)
apply_microcode_local(&err);
err = microcode_ops->apply_microcode(cpu);
else
goto wait_for_siblings;
......@@ -480,7 +426,7 @@ static int __reload_late(void *info)
* revision.
*/
if (cpumask_first(topology_sibling_cpumask(cpu)) != cpu)
apply_microcode_local(&err);
err = microcode_ops->apply_microcode(cpu);
return ret;
}
......@@ -531,7 +477,7 @@ static ssize_t reload_store(struct device *dev,
if (ret)
goto put;
tmp_ret = microcode_ops->request_microcode_fw(bsp, &microcode_pdev->dev, true);
tmp_ret = microcode_ops->request_microcode_fw(bsp, &microcode_pdev->dev);
if (tmp_ret != UCODE_NEW)
goto put;
......@@ -589,91 +535,17 @@ static void microcode_fini_cpu(int cpu)
microcode_ops->microcode_fini_cpu(cpu);
}
static enum ucode_state microcode_resume_cpu(int cpu)
static enum ucode_state microcode_init_cpu(int cpu)
{
if (apply_microcode_on_target(cpu))
return UCODE_ERROR;
pr_debug("CPU%d updated upon resume\n", cpu);
return UCODE_OK;
}
static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw)
{
enum ucode_state ustate;
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
if (uci->valid)
return UCODE_OK;
if (collect_cpu_info(cpu))
return UCODE_ERROR;
/* --dimm. Trigger a delayed update? */
if (system_state != SYSTEM_RUNNING)
return UCODE_NFOUND;
ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev, refresh_fw);
if (ustate == UCODE_NEW) {
pr_debug("CPU%d updated upon init\n", cpu);
apply_microcode_on_target(cpu);
}
return ustate;
}
static enum ucode_state microcode_update_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
/* Refresh CPU microcode revision after resume. */
collect_cpu_info(cpu);
if (uci->valid)
return microcode_resume_cpu(cpu);
return microcode_init_cpu(cpu, false);
}
static int mc_device_add(struct device *dev, struct subsys_interface *sif)
{
int err, cpu = dev->id;
if (!cpu_online(cpu))
return 0;
pr_debug("CPU%d added\n", cpu);
err = sysfs_create_group(&dev->kobj, &mc_attr_group);
if (err)
return err;
memset(uci, 0, sizeof(*uci));
if (microcode_init_cpu(cpu, true) == UCODE_ERROR)
return -EINVAL;
microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig);
return err;
return microcode_ops->apply_microcode(cpu);
}
static void mc_device_remove(struct device *dev, struct subsys_interface *sif)
{
int cpu = dev->id;
if (!cpu_online(cpu))
return;
pr_debug("CPU%d removed\n", cpu);
microcode_fini_cpu(cpu);
sysfs_remove_group(&dev->kobj, &mc_attr_group);
}
static struct subsys_interface mc_cpu_interface = {
.name = "microcode",
.subsys = &cpu_subsys,
.add_dev = mc_device_add,
.remove_dev = mc_device_remove,
};
/**
* microcode_bsp_resume - Update boot CPU microcode during resume.
*/
......@@ -682,9 +554,9 @@ void microcode_bsp_resume(void)
int cpu = smp_processor_id();
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
if (uci->valid && uci->mc)
if (uci->mc)
microcode_ops->apply_microcode(cpu);
else if (!uci->mc)
else
reload_early_microcode();
}
......@@ -694,9 +566,11 @@ static struct syscore_ops mc_syscore_ops = {
static int mc_cpu_starting(unsigned int cpu)
{
microcode_update_cpu(cpu);
pr_debug("CPU%d added\n", cpu);
return 0;
enum ucode_state err = microcode_ops->apply_microcode(cpu);
pr_debug("%s: CPU%d, err: %d\n", __func__, cpu, err);
return err == UCODE_ERROR;
}
static int mc_cpu_online(unsigned int cpu)
......@@ -713,13 +587,30 @@ static int mc_cpu_down_prep(unsigned int cpu)
struct device *dev;
dev = get_cpu_device(cpu);
microcode_fini_cpu(cpu);
/* Suspend is in progress, only remove the interface */
sysfs_remove_group(&dev->kobj, &mc_attr_group);
pr_debug("CPU%d removed\n", cpu);
pr_debug("%s: CPU%d\n", __func__, cpu);
return 0;
}
static void setup_online_cpu(struct work_struct *work)
{
int cpu = smp_processor_id();
enum ucode_state err;
err = microcode_init_cpu(cpu);
if (err == UCODE_ERROR) {
pr_err("Error applying microcode on CPU%d\n", cpu);
return;
}
mc_cpu_online(cpu);
}
static struct attribute *cpu_root_microcode_attrs[] = {
#ifdef CONFIG_MICROCODE_LATE_LOADING
&dev_attr_reload.attr,
......@@ -750,28 +641,19 @@ static int __init microcode_init(void)
if (!microcode_ops)
return -ENODEV;
microcode_pdev = platform_device_register_simple("microcode", -1,
NULL, 0);
microcode_pdev = platform_device_register_simple("microcode", -1, NULL, 0);
if (IS_ERR(microcode_pdev))
return PTR_ERR(microcode_pdev);
cpus_read_lock();
mutex_lock(&microcode_mutex);
error = subsys_interface_register(&mc_cpu_interface);
mutex_unlock(&microcode_mutex);
cpus_read_unlock();
if (error)
goto out_pdev;
error = sysfs_create_group(&cpu_subsys.dev_root->kobj,
&cpu_root_microcode_group);
error = sysfs_create_group(&cpu_subsys.dev_root->kobj, &cpu_root_microcode_group);
if (error) {
pr_err("Error creating microcode group!\n");
goto out_driver;
goto out_pdev;
}
/* Do per-CPU setup */
schedule_on_each_cpu(setup_online_cpu);
register_syscore_ops(&mc_syscore_ops);
cpuhp_setup_state_nocalls(CPUHP_AP_MICROCODE_LOADER, "x86/microcode:starting",
mc_cpu_starting, NULL);
......@@ -782,15 +664,6 @@ static int __init microcode_init(void)
return 0;
out_driver:
cpus_read_lock();
mutex_lock(&microcode_mutex);
subsys_interface_unregister(&mc_cpu_interface);
mutex_unlock(&microcode_mutex);
cpus_read_unlock();
out_pdev:
platform_device_unregister(microcode_pdev);
return error;
......
......@@ -45,34 +45,6 @@ static struct microcode_intel *intel_ucode_patch;
/* last level cache size per core */
static int llc_size_per_core;
/*
* Returns 1 if update has been found, 0 otherwise.
*/
static int 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;
}
/*
* Returns 1 if update has been found, 0 otherwise.
*/
......@@ -83,7 +55,7 @@ static int has_newer_microcode(void *mc, unsigned int csig, int cpf, int new_rev
if (mc_hdr->rev <= new_rev)
return 0;
return find_matching_signature(mc, csig, cpf);
return intel_find_matching_signature(mc, csig, cpf);
}
static struct ucode_patch *memdup_patch(void *data, unsigned int size)
......@@ -117,7 +89,7 @@ static void save_microcode_patch(struct ucode_cpu_info *uci, void *data, unsigne
sig = mc_saved_hdr->sig;
pf = mc_saved_hdr->pf;
if (find_matching_signature(data, sig, pf)) {
if (intel_find_matching_signature(data, sig, pf)) {
prev_found = true;
if (mc_hdr->rev <= mc_saved_hdr->rev)
......@@ -149,7 +121,7 @@ static void save_microcode_patch(struct ucode_cpu_info *uci, void *data, unsigne
if (!p)
return;
if (!find_matching_signature(p->data, uci->cpu_sig.sig, uci->cpu_sig.pf))
if (!intel_find_matching_signature(p->data, uci->cpu_sig.sig, uci->cpu_sig.pf))
return;
/*
......@@ -163,104 +135,6 @@ static void save_microcode_patch(struct ucode_cpu_info *uci, void *data, unsigne
intel_ucode_patch = p->data;
}
static int microcode_sanity_check(void *mc, int print_err)
{
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 != 1) {
if (print_err)
pr_err("Error: invalid/unknown microcode update format.\n");
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;
}
/*
* Get microcode matching with BSP's model. Only CPUs with the same model as
* BSP can stay in the platform.
......@@ -281,12 +155,12 @@ scan_microcode(void *data, size_t size, struct ucode_cpu_info *uci, bool save)
mc_size = get_totalsize(mc_header);
if (!mc_size ||
mc_size > size ||
microcode_sanity_check(data, 0) < 0)
intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)
break;
size -= mc_size;
if (!find_matching_signature(data, uci->cpu_sig.sig,
if (!intel_find_matching_signature(data, uci->cpu_sig.sig,
uci->cpu_sig.pf)) {
data += mc_size;
continue;
......@@ -621,7 +495,6 @@ void load_ucode_intel_ap(void)
else
iup = &intel_ucode_patch;
reget:
if (!*iup) {
patch = __load_ucode_intel(&uci);
if (!patch)
......@@ -632,12 +505,7 @@ void load_ucode_intel_ap(void)
uci.mc = *iup;
if (apply_microcode_early(&uci, true)) {
/* Mixed-silicon system? Try to refetch the proper patch: */
*iup = NULL;
goto reget;
}
apply_microcode_early(&uci, true);
}
static struct microcode_intel *find_patch(struct ucode_cpu_info *uci)
......@@ -652,7 +520,7 @@ static struct microcode_intel *find_patch(struct ucode_cpu_info *uci)
if (phdr->rev <= uci->cpu_sig.rev)
continue;
if (!find_matching_signature(phdr,
if (!intel_find_matching_signature(phdr,
uci->cpu_sig.sig,
uci->cpu_sig.pf))
continue;
......@@ -680,7 +548,6 @@ void reload_ucode_intel(void)
static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
{
static struct cpu_signature prev;
struct cpuinfo_x86 *c = &cpu_data(cpu_num);
unsigned int val[2];
......@@ -696,13 +563,6 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
csig->rev = c->microcode;
/* No extra locking on prev, races are harmless. */
if (csig->sig != prev.sig || csig->pf != prev.pf || csig->rev != prev.rev) {
pr_info("sig=0x%x, pf=0x%x, revision=0x%x\n",
csig->sig, csig->pf, csig->rev);
prev = *csig;
}
return 0;
}
......@@ -820,7 +680,7 @@ static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)
memcpy(mc, &mc_header, sizeof(mc_header));
data = mc + sizeof(mc_header);
if (!copy_from_iter_full(data, data_size, iter) ||
microcode_sanity_check(mc, 1) < 0) {
intel_microcode_sanity_check(mc, true, MC_HEADER_TYPE_MICROCODE) < 0) {
break;
}
......@@ -885,8 +745,7 @@ static bool is_blacklisted(unsigned int cpu)
return false;
}
static enum ucode_state request_microcode_fw(int cpu, struct device *device,
bool refresh_fw)
static enum ucode_state request_microcode_fw(int cpu, struct device *device)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
const struct firmware *firmware;
......
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
......
......@@ -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);
......
......@@ -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
......@@ -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 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)
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 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)
*/
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;
}
......@@ -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);
}
}
......
......@@ -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,
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
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment