--- 2006-06-20/xen/arch/x86/microcode.c 2006-04-06 17:50:26.000000000 +0200 +++ 2006-06-20/xen/arch/x86/microcode.c 2006-06-23 14:37:27.000000000 +0200 @@ -95,7 +95,10 @@ MODULE_AUTHOR("Tigran Aivazian count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) -/* serialize access to the physical write to MSR 0x79 */ -static DEFINE_SPINLOCK(microcode_update_lock); - /* no concurrent ->write()s are allowed on /dev/cpu/microcode */ static DECLARE_MUTEX(microcode_sem); @@ -126,14 +126,15 @@ static unsigned int user_buffer_size; /* typedef enum mc_error_code { MC_SUCCESS = 0, - MC_NOTFOUND = 1, - MC_MARKED = 2, - MC_ALLOCATED = 3, + MC_IGNORED = 1, + MC_NOTFOUND = 2, + MC_MARKED = 3, + MC_ALLOCATED = 4, } mc_error_code_t; static struct ucode_cpu_info { unsigned int sig; - unsigned int pf; + unsigned int pf, orig_pf; unsigned int rev; unsigned int cksum; mc_error_code_t err; @@ -163,6 +164,7 @@ static void collect_cpu_info (void *unus rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); uci->pf = 1 << ((val[1] >> 18) & 7); } + uci->orig_pf = uci->pf; } wrmsr(MSR_IA32_UCODE_REV, 0, 0); @@ -196,23 +198,34 @@ static inline void mark_microcode_update pr_debug(" Checksum 0x%x\n", cksum); if (mc_header->rev < uci->rev) { - printk(KERN_ERR "microcode: CPU%d not 'upgrading' to earlier revision" - " 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev); - goto out; + if (uci->err == MC_NOTFOUND) { + uci->err = MC_IGNORED; + uci->cksum = mc_header->rev; + } else if (uci->err == MC_IGNORED && uci->cksum < mc_header->rev) + uci->cksum = mc_header->rev; } else if (mc_header->rev == uci->rev) { - /* notify the caller of success on this cpu */ - uci->err = MC_SUCCESS; - printk(KERN_ERR "microcode: CPU%d already at revision" - " 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev); - goto out; + if (uci->err < MC_MARKED) { + /* notify the caller of success on this cpu */ + uci->err = MC_SUCCESS; + } + } else if (uci->err != MC_ALLOCATED || mc_header->rev > uci->mc->hdr.rev) { + pr_debug("microcode: CPU%d found a matching microcode update with " + " revision 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev); + uci->cksum = cksum; + uci->pf = pf; /* keep the original mc pf for cksum calculation */ + uci->err = MC_MARKED; /* found the match */ + for_each_online_cpu(cpu_num) { + if (ucode_cpu_info + cpu_num != uci + && ucode_cpu_info[cpu_num].mc == uci->mc) { + uci->mc = NULL; + break; + } + } + if (uci->mc != NULL) { + vfree(uci->mc); + uci->mc = NULL; + } } - - pr_debug("microcode: CPU%d found a matching microcode update with " - " revision 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev); - uci->cksum = cksum; - uci->pf = pf; /* keep the original mc pf for cksum calculation */ - uci->err = MC_MARKED; /* found the match */ -out: return; } @@ -251,13 +264,11 @@ static int find_matching_ucodes (void) error = -EINVAL; goto out; } - - for (cpu_num = 0; cpu_num < num_online_cpus(); cpu_num++) { + + for_each_online_cpu(cpu_num) { struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - if (uci->err != MC_NOTFOUND) /* already found a match or not an online cpu*/ - continue; - if (sigmatch(mc_header.sig, uci->sig, mc_header.pf, uci->pf)) + if (sigmatch(mc_header.sig, uci->sig, mc_header.pf, uci->orig_pf)) mark_microcode_update(cpu_num, &mc_header, mc_header.sig, mc_header.pf, mc_header.cksum); } @@ -294,18 +305,19 @@ static int find_matching_ucodes (void) error = -EFAULT; goto out; } - for (cpu_num = 0; cpu_num < num_online_cpus(); cpu_num++) { + for_each_online_cpu(cpu_num) { struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - if (uci->err != MC_NOTFOUND) /* already found a match or not an online cpu*/ - continue; - if (sigmatch(ext_sig.sig, uci->sig, ext_sig.pf, uci->pf)) { + + if (sigmatch(ext_sig.sig, uci->sig, ext_sig.pf, uci->orig_pf)) { mark_microcode_update(cpu_num, &mc_header, ext_sig.sig, ext_sig.pf, ext_sig.cksum); } } } } /* now check if any cpu has matched */ - for (cpu_num = 0, allocated_flag = 0, sum = 0; cpu_num < num_online_cpus(); cpu_num++) { + allocated_flag = 0; + sum = 0; + for_each_online_cpu(cpu_num) { if (ucode_cpu_info[cpu_num].err == MC_MARKED) { struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; if (!allocated_flag) { @@ -361,19 +373,10 @@ out: static void do_update_one (void * unused) { - unsigned long flags; unsigned int val[2]; int cpu_num = smp_processor_id(); struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - if (uci->mc == NULL) { - printk(KERN_INFO "microcode: No new microcode data for CPU%d\n", cpu_num); - return; - } - - /* serialize access to the physical write to MSR 0x79 */ - spin_lock_irqsave(µcode_update_lock, flags); - /* write microcode via MSR 0x79 */ wrmsr(MSR_IA32_UCODE_WRITE, (unsigned long) uci->mc->bits, @@ -388,7 +391,6 @@ static void do_update_one (void * unused /* notify the caller of success on this cpu */ uci->err = MC_SUCCESS; - spin_unlock_irqrestore(µcode_update_lock, flags); printk(KERN_INFO "microcode: CPU%d updated from revision " "0x%x to 0x%x, date = %08x \n", cpu_num, uci->rev, val[1], uci->mc->hdr.date); @@ -397,37 +399,65 @@ static void do_update_one (void * unused static int do_microcode_update (void) { - int i, error; + int i, j, error; + cpumask_t cpu_mask = cpu_online_map; - if (on_each_cpu(collect_cpu_info, NULL, 1, 1) != 0) { - printk(KERN_ERR "microcode: Error! Could not run on all processors\n"); - error = -EIO; - goto out; + for_each_cpu_mask(i, cpu_mask) { + if (on_selected_cpus(cpumask_of_cpu(i), collect_cpu_info, NULL, 1, 1) != 0) { + printk(KERN_ERR "microcode: Error! Could not run on all processors\n"); + return -EIO; + } } if ((error = find_matching_ucodes())) { printk(KERN_ERR "microcode: Error in the microcode data\n"); - goto out_free; + cpus_clear(cpu_mask); } - if (on_each_cpu(do_update_one, NULL, 1, 1) != 0) { - printk(KERN_ERR "microcode: Error! Could not run on all processors\n"); - error = -EIO; + for (; (i = any_online_cpu(cpu_mask)) < NR_CPUS; cpu_clear(i, cpu_mask)) { + if (ucode_cpu_info[i].mc == NULL) { + if (verbose) { + switch (ucode_cpu_info[i].err) { + case MC_SUCCESS: + printk(KERN_INFO "microcode: CPU%d already at revision 0x%x\n", + i, ucode_cpu_info[i].rev); + break; + case MC_IGNORED: + printk(KERN_WARNING "microcode: CPU%d not 'upgrading' to earlier revision" + " 0x%x (current=0x%x)\n", i, ucode_cpu_info[i].cksum, ucode_cpu_info[i].rev); + break; + default: + printk(KERN_INFO "microcode: No new microcode data for CPU%d\n", i); + break; + } + } + } else if (on_selected_cpus(cpumask_of_cpu(i), do_update_one, NULL, 1, 1) != 0) { + printk(KERN_ERR "microcode: Error! Could not run on processor %d\n", i); + error = -EIO; + } else if (ucode_cpu_info[i].err == MC_SUCCESS) { + cpus_andnot(cpu_mask, cpu_mask, cpu_sibling_map[i]); + for_each_cpu_mask(j, cpu_sibling_map[i]) { + if (j != i) { + ucode_cpu_info[j].err = MC_SUCCESS; + ASSERT(ucode_cpu_info[j].mc == ucode_cpu_info[i].mc); + ucode_cpu_info[j].mc = NULL; + } + } + } } -out_free: - for (i = 0; i < num_online_cpus(); i++) { + for (i = 0; i < NR_CPUS; i++) { if (ucode_cpu_info[i].mc) { - int j; void *tmp = ucode_cpu_info[i].mc; - vfree(tmp); - for (j = i; j < num_online_cpus(); j++) { + + for (j = 0; j < NR_CPUS; j++) { if (ucode_cpu_info[j].mc == tmp) ucode_cpu_info[j].mc = NULL; } + vfree(tmp); } } -out: + return error; }