lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <b647ffbd0904150312jdd898b7wf7485e0ffe8d5414@mail.gmail.com>
Date:	Wed, 15 Apr 2009 12:12:30 +0200
From:	Dmitry Adamushko <dmitry.adamushko@...il.com>
To:	Rusty Russell <rusty@...tcorp.com.au>
Cc:	Hugh Dickins <hugh@...itas.com>,
	Linus Torvalds <torvalds@...ux-foundation.org>,
	Andrew Morton <akpm@...ux-foundation.org>,
	Ingo Molnar <mingo@...e.hu>, Peter Oruba <peter.oruba@....com>,
	Arjan van de Ven <arjan@...radead.org>,
	linux-kernel@...r.kernel.org
Subject: Re: [PATCH] x86 microcode: revert some work_on_cpu

2009/4/15 Rusty Russell <rusty@...tcorp.com.au>:
> On Wed, 15 Apr 2009 03:55:42 am Hugh Dickins wrote:
>> Revert part of af5c820a3169e81af869c113e18ec7588836cd50
>> x86: cpumask: use work_on_cpu in arch/x86/kernel/microcode_core.c
>>
>> That change is causing only one Intel CPU's microcode to be updated e.g.
>> microcode: CPU3 updated from revision 0x9 to 0x17, date = 2005-04-22
>> where before it announced that also for CPU0 and CPU1 and CPU2.
>>
>> We cannot use work_on_cpu() in the CONFIG_MICROCODE_OLD_INTERFACE code,
>> because Intel's request_microcode_user() involves a copy_from_user() from
>> /sbin/microcode_ctl, which therefore needs to be on that CPU at the time.
>
> Erk.  Ack the reversion, but this needs to be fixed properly.
>
> We can't just mug a process's affinity.  I'll look at this code again and
> see what I can do.

Rusty,


what's about something like below?

Disclaimer: it's not even compilation-tested, consider it merely as an
illustration of an approach. I'll be able to test it later.

- run collect_cpu_info() and apply_microcode() on a target  cpu, the
rest of callbacks are just fine to run on any cpu;

- some synchronization changes (based on
http://lkml.indiana.edu/hypermail/linux/kernel/0903.1/00525.html)
See the "Synchronization" note in microcode_core.c

- removed sysfs_remove_group() from mc_sysdev_add() to avoid warnings
being triggered in some cases (there were a few reports on lkml) --
perhaps, to be reworked.

- some things perhaps need to be changed, e.g. add a return code to
apply_microcode(), ...

(the non-white-space damaged version is enclosed)

diff -upr linux-2.6.git/arch/x86/include/asm/microcode.h
linux-2.6.git.my/arch/x86/include/asm/microcode.h
--- linux-2.6.git/arch/x86/include/asm/microcode.h      2009-04-14
22:51:48.000000000 +0200
+++ linux-2.6.git.my/arch/x86/include/asm/microcode.h   2009-04-15
11:49:47.000000000 +0200
@@ -13,10 +13,16 @@ struct microcode_ops {
        int  (*request_microcode_user) (int cpu, const void __user
*buf, size_t size);
        int  (*request_microcode_fw) (int cpu, struct device *device);

-       void (*apply_microcode) (int cpu);
+       void (*microcode_fini_cpu) (int cpu);

+       /*
+        * The generic 'microcode_core' part guarantees that
+        * the callbacks below run on a target cpu when they
+        * are being called.
+        * See also the "Synchronization" section in microcode_core.c.
+        */
+       void (*apply_microcode) (int cpu);
        int  (*collect_cpu_info) (int cpu, struct cpu_signature *csig);
-       void (*microcode_fini_cpu) (int cpu);
 };

 struct ucode_cpu_info {
diff -upr linux-2.6.git/arch/x86/kernel/microcode_amd.c
linux-2.6.git.my/arch/x86/kernel/microcode_amd.c
--- linux-2.6.git/arch/x86/kernel/microcode_amd.c       2009-04-14
22:51:48.000000000 +0200
+++ linux-2.6.git.my/arch/x86/kernel/microcode_amd.c    2009-04-15
11:46:38.000000000 +0200
@@ -17,7 +17,6 @@
 #include <linux/capability.h>
 #include <linux/miscdevice.h>
 #include <linux/firmware.h>
-#include <linux/spinlock.h>
 #include <linux/cpumask.h>
 #include <linux/pci_ids.h>
 #include <linux/uaccess.h>
@@ -79,9 +78,6 @@ struct microcode_amd {
 #define UCODE_CONTAINER_SECTION_HDR    8
 #define UCODE_CONTAINER_HEADER_SIZE    12

-/* serialize access to the physical write */
-static DEFINE_SPINLOCK(microcode_update_lock);
-
 static struct equiv_cpu_entry *equiv_cpu_table;

 static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
@@ -158,11 +154,9 @@ static void apply_microcode_amd(int cpu)
        if (mc_amd == NULL)
                return;

-       spin_lock_irqsave(&microcode_update_lock, flags);
        wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code);
        /* get patch id after patching */
        rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
-       spin_unlock_irqrestore(&microcode_update_lock, flags);

        /* check current patch id and patch's id for match */
        if (rev != mc_amd->hdr.patch_id) {
@@ -327,9 +321,6 @@ static int request_microcode_fw(int cpu,
        const struct firmware *firmware;
        int ret;

-       /* We should bind the task to the CPU */
-       BUG_ON(cpu != raw_smp_processor_id());
-
        ret = request_firmware(&firmware, fw_name, device);
        if (ret) {
                printk(KERN_ERR "microcode: failed to load file %s\n", fw_name);
diff -upr linux-2.6.git/arch/x86/kernel/microcode_core.c
linux-2.6.git.my/arch/x86/kernel/microcode_core.c
--- linux-2.6.git/arch/x86/kernel/microcode_core.c      2009-04-14
22:51:48.000000000 +0200
+++ linux-2.6.git.my/arch/x86/kernel/microcode_core.c   2009-04-15
11:45:48.000000000 +0200
@@ -101,36 +101,89 @@ MODULE_LICENSE("GPL");

 static struct microcode_ops    *microcode_ops;

-/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
+/*
+ * Synchronization.
+ *
+ * All non cpu-hotplug-callback call sites use:
+ *
+ * - microcode_mutex to synchronize with each other;
+ * - get/put_online_cpus() to synchronize with
+ *   the cpu-hotplug-callback call sites.
+ *
+ * We guarantee that only a single cpu is being
+ * updated at any particular moment of time.
+ */
 static DEFINE_MUTEX(microcode_mutex);

 struct ucode_cpu_info          ucode_cpu_info[NR_CPUS];
 EXPORT_SYMBOL_GPL(ucode_cpu_info);

+/*
+ * Operations that are run on a target cpu:
+ */
+
+struct collect_for_cpu {
+       struct cpu_signature    cpu_sig;
+       int                     cpu;
+};
+
+static long collect_cpu_info_local(void *arg)
+{
+       struct collect_for_cpu *cfc = arg;
+
+       BUG_ON(cfc->cpu != smp_processor_id());
+
+       return microcode_ops->collect_cpu_info(cfc->cpu, &cfc->cpu_sig);
+}
+
+static int collect_cpu_info_on_target(int cpu, struct cpu_signature *cpu_sig)
+{
+       return work_on_cpu(cpu, collect_cpu_info_local, cpu_sig);
+}
+
+static void collect_cpu_info(int cpu)
+{
+       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+       memset(uci, 0, sizeof(*uci));
+       if (!collect_cpu_info_on_target(cpu, &uci->cpu_sig))
+               uci->valid = 1;
+}
+
+static long apply_microcode_local(void *arg)
+{
+       int cpu = (int)arg;
+
+       BUG_ON(cpu != smp_processor_id());
+       microcode_ops->apply_microcode(cpu);
+
+       return 0;
+}
+
+static int apply_microcode_on_target(int cpu)
+{
+       return work_on_cpu(cpu, apply_microcode_local, (void *)cpu);
+}
+
 #ifdef CONFIG_MICROCODE_OLD_INTERFACE
 static int do_microcode_update(const void __user *buf, size_t size)
 {
-       cpumask_t old;
        int error = 0;
        int cpu;

-       old = current->cpus_allowed;
-
        for_each_online_cpu(cpu) {
                struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

                if (!uci->valid)
                        continue;

-               set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
                error = microcode_ops->request_microcode_user(cpu, buf, size);
                if (error < 0)
-                       goto out;
+                       break;
                if (!error)
-                       microcode_ops->apply_microcode(cpu);
+                       apply_microcode_on_target(cpu);
        }
-out:
-       set_cpus_allowed_ptr(current, &old);
+
        return error;
 }

@@ -205,17 +258,16 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
 /* fake device for request_firmware */
 static struct platform_device  *microcode_pdev;

-static long reload_for_cpu(void *unused)
+static int reload_for_cpu(int cpu)
 {
-       struct ucode_cpu_info *uci = ucode_cpu_info + smp_processor_id();
+       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
        int err = 0;

        mutex_lock(&microcode_mutex);
        if (uci->valid) {
-               err = microcode_ops->request_microcode_fw(smp_processor_id(),
-                                                         &microcode_pdev->dev);
+               err = microcode_ops->request_microcode_fw(cpu,
&microcode_pdev->dev);
                if (!err)
-                       microcode_ops->apply_microcode(smp_processor_id());
+                       apply_microcode_on_target(cpu);
        }
        mutex_unlock(&microcode_mutex);
        return err;
@@ -235,7 +287,7 @@ static ssize_t reload_store(struct sys_d
        if (val == 1) {
                get_online_cpus();
                if (cpu_online(cpu))
-                       err = work_on_cpu(cpu, reload_for_cpu, NULL);
+                       err = reload_for_cpu(cpu);
                put_online_cpus();
        }
        if (err)
@@ -275,7 +327,7 @@ static struct attribute_group mc_attr_gr
        .name           = "microcode",
 };

-static void __microcode_fini_cpu(int cpu)
+static void microcode_fini_cpu(int cpu)
 {
        struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

@@ -283,22 +335,6 @@ static void __microcode_fini_cpu(int cpu
        uci->valid = 0;
 }

-static void microcode_fini_cpu(int cpu)
-{
-       mutex_lock(&microcode_mutex);
-       __microcode_fini_cpu(cpu);
-       mutex_unlock(&microcode_mutex);
-}
-
-static void collect_cpu_info(int cpu)
-{
-       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-
-       memset(uci, 0, sizeof(*uci));
-       if (!microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig))
-               uci->valid = 1;
-}
-
 static int microcode_resume_cpu(int cpu)
 {
        struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
@@ -313,15 +349,15 @@ static int microcode_resume_cpu(int cpu)
         * Let's verify that the 'cached' ucode does belong
         * to this cpu (a bit of paranoia):
         */
-       if (microcode_ops->collect_cpu_info(cpu, &nsig)) {
-               __microcode_fini_cpu(cpu);
+       if (collect_cpu_info_on_target(cpu, &nsig)) {
+               microcode_fini_cpu(cpu);
                printk(KERN_ERR "failed to collect_cpu_info for
resuming cpu #%d\n",
                                cpu);
                return -1;
        }

        if ((nsig.sig != uci->cpu_sig.sig) || (nsig.pf != uci->cpu_sig.pf)) {
-               __microcode_fini_cpu(cpu);
+               microcode_fini_cpu(cpu);
                printk(KERN_ERR "cached ucode doesn't match the
resuming cpu #%d\n",
                                cpu);
                /* Should we look for a new ucode here? */
@@ -331,9 +367,9 @@ static int microcode_resume_cpu(int cpu)
        return 0;
 }

-static long microcode_update_cpu(void *unused)
+static int microcode_init_cpu(int cpu)
 {
-       struct ucode_cpu_info *uci = ucode_cpu_info + smp_processor_id();
+       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
        int err = 0;

        /*
@@ -341,25 +377,16 @@ static long microcode_update_cpu(void *u
         * otherwise just request a firmware:
         */
        if (uci->valid) {
-               err = microcode_resume_cpu(smp_processor_id());
+               err = microcode_resume_cpu(cpu);
        } else {
-               collect_cpu_info(smp_processor_id());
+               collect_cpu_info(cpu);
                if (uci->valid && system_state == SYSTEM_RUNNING)
                        err = microcode_ops->request_microcode_fw(
-                                       smp_processor_id(),
+                                       cpu,
                                        &microcode_pdev->dev);
        }
        if (!err)
-               microcode_ops->apply_microcode(smp_processor_id());
-       return err;
-}
-
-static int microcode_init_cpu(int cpu)
-{
-       int err;
-       mutex_lock(&microcode_mutex);
-       err = work_on_cpu(cpu, microcode_update_cpu, NULL);
-       mutex_unlock(&microcode_mutex);
+               apply_microcode_on_target(cpu);

        return err;
 }
@@ -380,8 +407,6 @@ static int mc_sysdev_add(struct sys_devi
                return err;

        err = microcode_init_cpu(cpu);
-       if (err)
-               sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);

        return err;
 }
@@ -406,8 +431,17 @@ static int mc_sysdev_resume(struct sys_d
        if (!cpu_online(cpu))
                return 0;

-       /* only CPU 0 will apply ucode here */
-       microcode_update_cpu(NULL);
+       /*
+        * All non-bootup cpus are still disabled,
+        * so only CPU 0 will apply ucode here.
+        *
+        * Moreover, there can be no concurrent
+        * updates from any other places at this point.
+        */
+       WARN_ON(cpu != 0);
+
+       microcode_init_cpu(cpu);
+
        return 0;
 }

@@ -471,9 +505,6 @@ static int __init microcode_init(void)
                return -ENODEV;
        }

-       error = microcode_dev_init();
-       if (error)
-               return error;
        microcode_pdev = platform_device_register_simple("microcode", -1,
                                                         NULL, 0);
        if (IS_ERR(microcode_pdev)) {
@@ -482,14 +513,26 @@ static int __init microcode_init(void)
        }

        get_online_cpus();
+       /*
+        * --dimm. Hmm, we can avoid it if we perhaps first
+        * try to apply ucode in mc_sysdev_add() and only
+        * then create a sysfs group.
+        */
+       mutex_lock(&microcode_mutex);
        error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver);
+       mutex_unlock(&microcode_mutex);
+
        put_online_cpus();
+
        if (error) {
-               microcode_dev_exit();
                platform_device_unregister(microcode_pdev);
                return error;
        }

+       error = microcode_dev_init();
+       if (error)
+               return error;
+
        register_hotcpu_notifier(&mc_cpu_notifier);

        printk(KERN_INFO
@@ -507,7 +550,9 @@ static void __exit microcode_exit(void)
        unregister_hotcpu_notifier(&mc_cpu_notifier);

        get_online_cpus();
+       mutex_lock(&microcode_mutex);
        sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver);
+       mutex_unlock(&microcode_mutex);
        put_online_cpus();

        platform_device_unregister(microcode_pdev);
diff -upr linux-2.6.git/arch/x86/kernel/microcode_intel.c
linux-2.6.git.my/arch/x86/kernel/microcode_intel.c
--- linux-2.6.git/arch/x86/kernel/microcode_intel.c     2009-04-14
22:51:48.000000000 +0200
+++ linux-2.6.git.my/arch/x86/kernel/microcode_intel.c  2009-04-15
11:47:21.000000000 +0200
@@ -75,7 +75,6 @@
 #include <linux/miscdevice.h>
 #include <linux/firmware.h>
 #include <linux/smp_lock.h>
-#include <linux/spinlock.h>
 #include <linux/cpumask.h>
 #include <linux/uaccess.h>
 #include <linux/vmalloc.h>
@@ -150,9 +149,6 @@ struct extended_sigtable {

 #define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)

-/* serialize access to the physical write to MSR 0x79 */
-static DEFINE_SPINLOCK(microcode_update_lock);
-
 static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
 {
        struct cpuinfo_x86 *c = &cpu_data(cpu_num);
@@ -176,15 +172,11 @@ static int collect_cpu_info(int cpu_num,
                csig->pf = 1 << ((val[1] >> 18) & 7);
        }

-       /* serialize access to the physical write to MSR 0x79 */
-       spin_lock_irqsave(&microcode_update_lock, flags);
-
        wrmsr(MSR_IA32_UCODE_REV, 0, 0);
        /* see notes above for revision 1.07.  Apparent chip bug */
        sync_core();
        /* get the current revision from MSR 0x8B */
        rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev);
-       spin_unlock_irqrestore(&microcode_update_lock, flags);

        pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n",
                        csig->sig, csig->pf, csig->rev);
@@ -336,9 +328,6 @@ static void apply_microcode(int cpu)
        if (mc_intel == NULL)
                return;

-       /* serialize access to the physical write to MSR 0x79 */
-       spin_lock_irqsave(&microcode_update_lock, flags);
-
        /* write microcode via MSR 0x79 */
        wrmsr(MSR_IA32_UCODE_WRITE,
              (unsigned long) mc_intel->bits,
@@ -351,7 +340,6 @@ static void apply_microcode(int cpu)
        /* get the current revision from MSR 0x8B */
        rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);

-       spin_unlock_irqrestore(&microcode_update_lock, flags);
        if (val[1] != mc_intel->hdr.rev) {
                printk(KERN_ERR "microcode: CPU%d update from revision "
                                "0x%x to 0x%x failed\n",
@@ -445,8 +433,6 @@ static int request_microcode_fw(int cpu,
        const struct firmware *firmware;
        int ret;

-       /* We should bind the task to the CPU */
-       BUG_ON(cpu != raw_smp_processor_id());
        sprintf(name, "intel-ucode/%02x-%02x-%02x",
                c->x86, c->x86_model, c->x86_mask);
        ret = request_firmware(&firmware, name, device);
@@ -470,9 +456,6 @@ static int get_ucode_user(void *to, cons

 static int request_microcode_user(int cpu, const void __user *buf, size_t size)
 {
-       /* We should bind the task to the CPU */
-       BUG_ON(cpu != raw_smp_processor_id());
-
        return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user);
 }



---





-- 
Best regards,
Dmitry Adamushko

View attachment "microcode.patch" of type "text/x-patch" (13246 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ