#include <linux/device.h>
 #include <linux/miscdevice.h>
 
+#define MSR_ARRAY_BIST                         0x00000105
 #define MSR_COPY_SCAN_HASHES                   0x000002c2
 #define MSR_SCAN_HASHES_STATUS                 0x000002c3
 #define MSR_AUTHENTICATE_AND_COPY_CHUNK                0x000002c4
        };
 };
 
+/* MSR_ARRAY_BIST bit fields */
+union ifs_array {
+       u64     data;
+       struct {
+               u32     array_bitmask;
+               u16     array_bank;
+               u16     rsvd                    :15;
+               u16     ctrl_result             :1;
+       };
+};
+
 /*
  * Driver populated error-codes
  * 0xFD: Test timed out before completing all the chunks.
 
        }
 }
 
+#define SPINUNIT 100 /* 100 nsec */
+static atomic_t array_cpus_out;
+
+/*
+ * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
+ */
+static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
+{
+       int cpu = smp_processor_id();
+       const struct cpumask *smt_mask = cpu_smt_mask(cpu);
+       int all_cpus = cpumask_weight(smt_mask);
+
+       atomic_inc(t);
+       while (atomic_read(t) < all_cpus) {
+               if (timeout < SPINUNIT)
+                       return;
+               ndelay(SPINUNIT);
+               timeout -= SPINUNIT;
+               touch_nmi_watchdog();
+       }
+}
+
+static int do_array_test(void *data)
+{
+       union ifs_array *command = data;
+       int cpu = smp_processor_id();
+       int first;
+
+       /*
+        * Only one logical CPU on a core needs to trigger the Array test via MSR write.
+        */
+       first = cpumask_first(cpu_smt_mask(cpu));
+
+       if (cpu == first) {
+               wrmsrl(MSR_ARRAY_BIST, command->data);
+               /* Pass back the result of the test */
+               rdmsrl(MSR_ARRAY_BIST, command->data);
+       }
+
+       /* Tests complete faster if the sibling is spinning here */
+       wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC);
+
+       return 0;
+}
+
+static void ifs_array_test_core(int cpu, struct device *dev)
+{
+       union ifs_array command = {};
+       bool timed_out = false;
+       struct ifs_data *ifsd;
+       unsigned long timeout;
+
+       ifsd = ifs_get_data(dev);
+
+       command.array_bitmask = ~0U;
+       timeout = jiffies + HZ / 2;
+
+       do {
+               if (time_after(jiffies, timeout)) {
+                       timed_out = true;
+                       break;
+               }
+               atomic_set(&array_cpus_out, 0);
+               stop_core_cpuslocked(cpu, do_array_test, &command);
+
+               if (command.ctrl_result)
+                       break;
+       } while (command.array_bitmask);
+
+       ifsd->scan_details = command.data;
+
+       if (command.ctrl_result)
+               ifsd->status = SCAN_TEST_FAIL;
+       else if (timed_out || command.array_bitmask)
+               ifsd->status = SCAN_NOT_TESTED;
+       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
                ifs_test_core(cpu, dev);
                break;
        case IFS_TYPE_ARRAY_BIST:
+               ifs_array_test_core(cpu, dev);
+               break;
        default:
                return -EINVAL;
        }