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>] [day] [month] [year] [list]
Date:	Sun, 03 Aug 2008 17:56:29 +0200
From:	Dmitry Adamushko <dmitry.adamushko@...il.com>
To:	Ingo Molnar <mingo@...e.hu>
Cc:	Tigran Aivazian <tigran@...azian.fsnet.co.uk>,
	"H. Peter Anvin" <hpa@...or.com>,
	Peter Oruba <peter.oruba@....com>,
	Thomas Gleixner <tglx@...utronix.de>,
	Max Krasnyansky <maxk@...lcomm.com>,
	"Rafael J. Wysocki" <rjw@...k.pl>,
	LKML <linux-kernel@...r.kernel.org>
Subject: [rfc-patch, bugfix 1/2] x86-microcode: generic updates


From: Dmitry Adamushko <dmitry.adamushko@...il.com>

(1) Introduce microcode_update_cpu() which is a generic routine to do ucode-updates (for both bootup-path
and system-resume-path);

(2) Adapt the logic of cpu-hotplug handlers;

(3) Fix a race with sched_setaffinity() in reload_store()

We introduce another mechanism to run ucode-updates on a target cpu
to replace set_cpus_allowed_ptr() being called from cpu-hotplug events

(which causes a bug#11197: http://www.ussg.iu.edu/hypermail/linux/kernel/0807.3/3791.html)

microcode_update_cpu() can be run either from start_secondary()
(perhaps via a function pointer) or scheduled via keventd (implemented by the following patch).

all in all, we remove more lines than we add (84/79)

(Almost)-Signed-off-by: Dmitry Adamushko <dmitry.adamushko@...il.com>

---

--- a/arch/x86/kernel/microcode.c
+++ b/arch/x86/kernel/microcode.c
@@ -126,16 +126,16 @@ static DEFINE_MUTEX(microcode_mutex);
 
 static struct ucode_cpu_info {
 	int valid;
+	int resume;
 	unsigned int sig;
 	unsigned int pf;
 	unsigned int rev;
 	microcode_t *mc;
 } ucode_cpu_info[NR_CPUS];
 
-static void collect_cpu_info(int cpu_num)
+static void collect_cpu_info(int cpu_num, struct ucode_cpu_info *uci)
 {
 	struct cpuinfo_x86 *c = &cpu_data(cpu_num);
-	struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
 	unsigned int val[2];
 
 	/* We should bind the task to the CPU */
@@ -569,79 +569,85 @@ static int cpu_request_microcode(int cpu)
 	return error;
 }
 
-static int apply_microcode_check_cpu(int cpu)
+static void microcode_fini_cpu(int cpu)
 {
-	struct cpuinfo_x86 *c = &cpu_data(cpu);
 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-	cpumask_t old;
-	unsigned int val[2];
-	int err = 0;
 
-	/* Check if the microcode is available */
-	if (!uci->mc)
-		return 0;
+	mutex_lock(&microcode_mutex);
+	uci->valid = 0;
+	uci->resume = 0;
+	vfree(uci->mc);
+	uci->mc = NULL;
+	mutex_unlock(&microcode_mutex);
+}
 
-	old = current->cpus_allowed;
-	set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
+static void microcode_set_cpu_frozen(int cpu)
+{
+	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
 
-	/* Check if the microcode we have in memory matches the CPU */
-	if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
-	    cpu_has(c, X86_FEATURE_IA64) || uci->sig != cpuid_eax(0x00000001))
-		err = -EINVAL;
+	mutex_lock(&microcode_mutex);
+	if (uci->valid)
+		uci->resume = 1;
+	mutex_unlock(&microcode_mutex);
+}
 
-	if (!err && ((c->x86_model >= 5) || (c->x86 > 6))) {
-		/* get processor flags from MSR 0x17 */
-		rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
-		if (uci->pf != (1 << ((val[1] >> 18) & 7)))
-			err = -EINVAL;
-	}
+static int microcode_resume_cpu(int cpu, struct ucode_cpu_info *new_uci)
+{
+	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
 
-	if (!err) {
-		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], val[1]);
-		if (uci->rev != val[1])
-			err = -EINVAL;
-	}
+	if (!uci->resume)
+		return 0;
+
+	uci->resume = 0;
 
-	if (!err)
+	/* Check if the 'cached' microcode is ok: */
+	if (!uci->mc)
+		return 1;
+
+	if (new_uci->sig == uci->sig &&
+	    new_uci->pf  == uci->pf &&
+	    new_uci->rev == uci->rev) {
 		apply_microcode(cpu);
-	else
-		printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:"
-			" sig=0x%x, pf=0x%x, rev=0x%x\n",
-			cpu, uci->sig, uci->pf, uci->rev);
+	} else {
+		microcode_fini_cpu(cpu);
+		*uci = *new_uci;
+	}
 
-	set_cpus_allowed_ptr(current, &old);
-	return err;
+	return 1;
 }
 
-static void microcode_init_cpu(int cpu, int resume)
+void microcode_update_cpu(int cpu)
 {
-	cpumask_t old;
-	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+	struct ucode_cpu_info *uci = ucode_cpu_info + cpu, 
+			      new_uci;
 
-	old = current->cpus_allowed;
+	memset(&new_uci, 0, sizeof(new_uci));
 
-	set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
 	mutex_lock(&microcode_mutex);
-	collect_cpu_info(cpu);
-	if (uci->valid && system_state == SYSTEM_RUNNING && !resume)
-		cpu_request_microcode(cpu);
+	collect_cpu_info(cpu, &new_uci);
+
+	if (new_uci.valid) {
+		/*
+		 * Check if the system resume is in progress,
+		 * otherwise just request a firmware:
+		 */
+		if (!microcode_resume_cpu(cpu, &new_uci)) {
+			*uci = new_uci;
+
+			if (system_state == SYSTEM_RUNNING)
+				cpu_request_microcode(cpu);
+		}
+	}
 	mutex_unlock(&microcode_mutex);
-	set_cpus_allowed_ptr(current, &old);
 }
 
-static void microcode_fini_cpu(int cpu)
+static void microcode_init_cpu(int cpu)
 {
-	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+	cpumask_t old = current->cpus_allowed;
 
-	mutex_lock(&microcode_mutex);
-	uci->valid = 0;
-	vfree(uci->mc);
-	uci->mc = NULL;
-	mutex_unlock(&microcode_mutex);
+	set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
+	microcode_update_cpu(cpu);
+	set_cpus_allowed_ptr(current, &old);
 }
 
 static ssize_t reload_store(struct sys_device *dev,
@@ -660,14 +666,15 @@ static ssize_t reload_store(struct sys_device *dev,
 		cpumask_t old = current->cpus_allowed;
 
 		get_online_cpus();
-		set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
-
-		mutex_lock(&microcode_mutex);
-		if (uci->valid)
-			err = cpu_request_microcode(cpu);
-		mutex_unlock(&microcode_mutex);
+		if (cpu_online(cpu)) {
+			set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
+			mutex_lock(&microcode_mutex);
+			if (uci->valid)
+				err = cpu_request_microcode(cpu);
+			mutex_unlock(&microcode_mutex);
+			set_cpus_allowed_ptr(current, &old);
+		}
 		put_online_cpus();
-		set_cpus_allowed_ptr(current, &old);
 	}
 	if (err)
 		return err;
@@ -706,7 +713,7 @@ static struct attribute_group mc_attr_group = {
 	.name = "microcode",
 };
 
-static int __mc_sysdev_add(struct sys_device *sys_dev, int resume)
+static int mc_sysdev_add(struct sys_device *sys_dev)
 {
 	int err, cpu = sys_dev->id;
 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
@@ -721,16 +728,11 @@ static int __mc_sysdev_add(struct sys_device *sys_dev, int resume)
 	if (err)
 		return err;
 
-	microcode_init_cpu(cpu, resume);
+	microcode_init_cpu(cpu);
 
 	return 0;
 }
 
-static int mc_sysdev_add(struct sys_device *sys_dev)
-{
-	return __mc_sysdev_add(sys_dev, 0);
-}
-
 static int mc_sysdev_remove(struct sys_device *sys_dev)
 {
 	int cpu = sys_dev->id;
@@ -770,34 +772,27 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
 
 	sys_dev = get_cpu_sysdev(cpu);
 	switch (action) {
-	case CPU_UP_CANCELED_FROZEN:
-		/* The CPU refused to come up during a system resume */
-		microcode_fini_cpu(cpu);
-		break;
 	case CPU_ONLINE:
-	case CPU_DOWN_FAILED:
-		mc_sysdev_add(sys_dev);
-		break;
 	case CPU_ONLINE_FROZEN:
-		/* System-wide resume is in progress, try to apply microcode */
-		if (apply_microcode_check_cpu(cpu)) {
-			/* The application of microcode failed */
-			microcode_fini_cpu(cpu);
-			__mc_sysdev_add(sys_dev, 1);
-			break;
-		}
+	case CPU_DOWN_FAILED:
 	case CPU_DOWN_FAILED_FROZEN:
 		if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group))
 			printk(KERN_ERR "microcode: Failed to create the sysfs "
 				"group for CPU%d\n", cpu);
 		break;
 	case CPU_DOWN_PREPARE:
-		mc_sysdev_remove(sys_dev);
-		break;
 	case CPU_DOWN_PREPARE_FROZEN:
 		/* Suspend is in progress, only remove the interface */
 		sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
 		break;
+	case CPU_DEAD:
+	case CPU_UP_CANCELED_FROZEN:
+                /* The CPU refused to come up during a system resume */
+		microcode_fini_cpu(cpu);
+		break;
+	case CPU_DEAD_FROZEN:
+		microcode_set_cpu_frozen(cpu);
+		break;
 	}
 	return NOTIFY_OK;
 }


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ