diff options
| author | Borislav Petkov <borislav.petkov@amd.com> | 2009-12-11 12:14:40 -0500 |
|---|---|---|
| committer | H. Peter Anvin <hpa@zytor.com> | 2009-12-11 13:59:21 -0500 |
| commit | 505422517d3f126bb939439e9d15dece94e11d2c (patch) | |
| tree | a2d58c0b3cdf2b1c6b66eee6d78a283224ae1ac3 | |
| parent | 5c6baba84e1ac6a79b266b40e17e692aab6604a1 (diff) | |
x86, msr: Add support for non-contiguous cpumasks
The current rd/wrmsr_on_cpus helpers assume that the supplied
cpumasks are contiguous. However, there are machines out there
like some K8 multinode Opterons which have a non-contiguous core
enumeration on each node (e.g. cores 0,2 on node 0 instead of 0,1), see
http://www.gossamer-threads.com/lists/linux/kernel/1160268.
This patch fixes out-of-bounds writes (see URL above) by adding per-CPU
msr structs which are used on the respective cores.
Additionally, two helpers, msrs_{alloc,free}, are provided for use by
the callers of the MSR accessors.
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
Cc: Aristeu Rozanski <aris@redhat.com>
Cc: Randy Dunlap <randy.dunlap@oracle.com>
Cc: Doug Thompson <dougthompson@xmission.com>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
LKML-Reference: <20091211171440.GD31998@aftab>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
| -rw-r--r-- | arch/x86/include/asm/msr.h | 3 | ||||
| -rw-r--r-- | arch/x86/lib/msr.c | 26 | ||||
| -rw-r--r-- | drivers/edac/amd64_edac.c | 46 |
3 files changed, 42 insertions, 33 deletions
diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h index 5bef931f8b14..2d228fc9b4b7 100644 --- a/arch/x86/include/asm/msr.h +++ b/arch/x86/include/asm/msr.h | |||
| @@ -244,6 +244,9 @@ do { \ | |||
| 244 | 244 | ||
| 245 | #define write_rdtscp_aux(val) wrmsr(0xc0000103, (val), 0) | 245 | #define write_rdtscp_aux(val) wrmsr(0xc0000103, (val), 0) |
| 246 | 246 | ||
| 247 | struct msr *msrs_alloc(void); | ||
| 248 | void msrs_free(struct msr *msrs); | ||
| 249 | |||
| 247 | #ifdef CONFIG_SMP | 250 | #ifdef CONFIG_SMP |
| 248 | int rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h); | 251 | int rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h); |
| 249 | int wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h); | 252 | int wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h); |
diff --git a/arch/x86/lib/msr.c b/arch/x86/lib/msr.c index 41628b104b9e..872834177937 100644 --- a/arch/x86/lib/msr.c +++ b/arch/x86/lib/msr.c | |||
| @@ -7,7 +7,6 @@ struct msr_info { | |||
| 7 | u32 msr_no; | 7 | u32 msr_no; |
| 8 | struct msr reg; | 8 | struct msr reg; |
| 9 | struct msr *msrs; | 9 | struct msr *msrs; |
| 10 | int off; | ||
| 11 | int err; | 10 | int err; |
| 12 | }; | 11 | }; |
| 13 | 12 | ||
| @@ -18,7 +17,7 @@ static void __rdmsr_on_cpu(void *info) | |||
| 18 | int this_cpu = raw_smp_processor_id(); | 17 | int this_cpu = raw_smp_processor_id(); |
| 19 | 18 | ||
| 20 | if (rv->msrs) | 19 | if (rv->msrs) |
| 21 | reg = &rv->msrs[this_cpu - rv->off]; | 20 | reg = per_cpu_ptr(rv->msrs, this_cpu); |
| 22 | else | 21 | else |
| 23 | reg = &rv->reg; | 22 | reg = &rv->reg; |
| 24 | 23 | ||
| @@ -32,7 +31,7 @@ static void __wrmsr_on_cpu(void *info) | |||
| 32 | int this_cpu = raw_smp_processor_id(); | 31 | int this_cpu = raw_smp_processor_id(); |
| 33 | 32 | ||
| 34 | if (rv->msrs) | 33 | if (rv->msrs) |
| 35 | reg = &rv->msrs[this_cpu - rv->off]; | 34 | reg = per_cpu_ptr(rv->msrs, this_cpu); |
| 36 | else | 35 | else |
| 37 | reg = &rv->reg; | 36 | reg = &rv->reg; |
| 38 | 37 | ||
| @@ -80,7 +79,6 @@ static void __rwmsr_on_cpus(const struct cpumask *mask, u32 msr_no, | |||
| 80 | 79 | ||
| 81 | memset(&rv, 0, sizeof(rv)); | 80 | memset(&rv, 0, sizeof(rv)); |
| 82 | 81 | ||
| 83 | rv.off = cpumask_first(mask); | ||
| 84 | rv.msrs = msrs; | 82 | rv.msrs = msrs; |
| 85 | rv.msr_no = msr_no; | 83 | rv.msr_no = msr_no; |
| 86 | 84 | ||
| @@ -120,6 +118,26 @@ void wrmsr_on_cpus(const struct cpumask *mask, u32 msr_no, struct msr *msrs) | |||
| 120 | } | 118 | } |
| 121 | EXPORT_SYMBOL(wrmsr_on_cpus); | 119 | EXPORT_SYMBOL(wrmsr_on_cpus); |
| 122 | 120 | ||
| 121 | struct msr *msrs_alloc(void) | ||
| 122 | { | ||
| 123 | struct msr *msrs = NULL; | ||
| 124 | |||
| 125 | msrs = alloc_percpu(struct msr); | ||
| 126 | if (!msrs) { | ||
| 127 | pr_warning("%s: error allocating msrs\n", __func__); | ||
| 128 | return NULL; | ||
| 129 | } | ||
| 130 | |||
| 131 | return msrs; | ||
| 132 | } | ||
| 133 | EXPORT_SYMBOL(msrs_alloc); | ||
| 134 | |||
| 135 | void msrs_free(struct msr *msrs) | ||
| 136 | { | ||
| 137 | free_percpu(msrs); | ||
| 138 | } | ||
| 139 | EXPORT_SYMBOL(msrs_free); | ||
| 140 | |||
| 123 | /* These "safe" variants are slower and should be used when the target MSR | 141 | /* These "safe" variants are slower and should be used when the target MSR |
| 124 | may not actually exist. */ | 142 | may not actually exist. */ |
| 125 | static void __rdmsr_safe_on_cpu(void *info) | 143 | static void __rdmsr_safe_on_cpu(void *info) |
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 5fdd6daa40ea..df5b68433f34 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
| @@ -13,6 +13,8 @@ module_param(report_gart_errors, int, 0644); | |||
| 13 | static int ecc_enable_override; | 13 | static int ecc_enable_override; |
| 14 | module_param(ecc_enable_override, int, 0644); | 14 | module_param(ecc_enable_override, int, 0644); |
| 15 | 15 | ||
| 16 | static struct msr *msrs; | ||
| 17 | |||
| 16 | /* Lookup table for all possible MC control instances */ | 18 | /* Lookup table for all possible MC control instances */ |
| 17 | struct amd64_pvt; | 19 | struct amd64_pvt; |
| 18 | static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES]; | 20 | static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES]; |
| @@ -2495,8 +2497,7 @@ static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid) | |||
| 2495 | static bool amd64_nb_mce_bank_enabled_on_node(int nid) | 2497 | static bool amd64_nb_mce_bank_enabled_on_node(int nid) |
| 2496 | { | 2498 | { |
| 2497 | cpumask_var_t mask; | 2499 | cpumask_var_t mask; |
| 2498 | struct msr *msrs; | 2500 | int cpu, nbe; |
| 2499 | int cpu, nbe, idx = 0; | ||
| 2500 | bool ret = false; | 2501 | bool ret = false; |
| 2501 | 2502 | ||
| 2502 | if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { | 2503 | if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { |
| @@ -2507,32 +2508,22 @@ static bool amd64_nb_mce_bank_enabled_on_node(int nid) | |||
| 2507 | 2508 | ||
| 2508 | get_cpus_on_this_dct_cpumask(mask, nid); | 2509 | get_cpus_on_this_dct_cpumask(mask, nid); |
| 2509 | 2510 | ||
| 2510 | msrs = kzalloc(sizeof(struct msr) * cpumask_weight(mask), GFP_KERNEL); | ||
| 2511 | if (!msrs) { | ||
| 2512 | amd64_printk(KERN_WARNING, "%s: error allocating msrs\n", | ||
| 2513 | __func__); | ||
| 2514 | free_cpumask_var(mask); | ||
| 2515 | return false; | ||
| 2516 | } | ||
| 2517 | |||
| 2518 | rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); | 2511 | rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); |
| 2519 | 2512 | ||
| 2520 | for_each_cpu(cpu, mask) { | 2513 | for_each_cpu(cpu, mask) { |
| 2521 | nbe = msrs[idx].l & K8_MSR_MCGCTL_NBE; | 2514 | struct msr *reg = per_cpu_ptr(msrs, cpu); |
| 2515 | nbe = reg->l & K8_MSR_MCGCTL_NBE; | ||
| 2522 | 2516 | ||
| 2523 | debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", | 2517 | debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", |
| 2524 | cpu, msrs[idx].q, | 2518 | cpu, reg->q, |
| 2525 | (nbe ? "enabled" : "disabled")); | 2519 | (nbe ? "enabled" : "disabled")); |
| 2526 | 2520 | ||
| 2527 | if (!nbe) | 2521 | if (!nbe) |
| 2528 | goto out; | 2522 | goto out; |
| 2529 | |||
| 2530 | idx++; | ||
| 2531 | } | 2523 | } |
| 2532 | ret = true; | 2524 | ret = true; |
| 2533 | 2525 | ||
| 2534 | out: | 2526 | out: |
| 2535 | kfree(msrs); | ||
| 2536 | free_cpumask_var(mask); | 2527 | free_cpumask_var(mask); |
| 2537 | return ret; | 2528 | return ret; |
| 2538 | } | 2529 | } |
| @@ -2540,8 +2531,7 @@ out: | |||
| 2540 | static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) | 2531 | static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) |
| 2541 | { | 2532 | { |
| 2542 | cpumask_var_t cmask; | 2533 | cpumask_var_t cmask; |
| 2543 | struct msr *msrs = NULL; | 2534 | int cpu; |
| 2544 | int cpu, idx = 0; | ||
| 2545 | 2535 | ||
| 2546 | if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { | 2536 | if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { |
| 2547 | amd64_printk(KERN_WARNING, "%s: error allocating mask\n", | 2537 | amd64_printk(KERN_WARNING, "%s: error allocating mask\n", |
| @@ -2551,34 +2541,27 @@ static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) | |||
| 2551 | 2541 | ||
| 2552 | get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id); | 2542 | get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id); |
| 2553 | 2543 | ||
| 2554 | msrs = kzalloc(sizeof(struct msr) * cpumask_weight(cmask), GFP_KERNEL); | ||
| 2555 | if (!msrs) { | ||
| 2556 | amd64_printk(KERN_WARNING, "%s: error allocating msrs\n", | ||
| 2557 | __func__); | ||
| 2558 | return -ENOMEM; | ||
| 2559 | } | ||
| 2560 | |||
| 2561 | rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); | 2544 | rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); |
| 2562 | 2545 | ||
| 2563 | for_each_cpu(cpu, cmask) { | 2546 | for_each_cpu(cpu, cmask) { |
| 2564 | 2547 | ||
| 2548 | struct msr *reg = per_cpu_ptr(msrs, cpu); | ||
| 2549 | |||
| 2565 | if (on) { | 2550 | if (on) { |
| 2566 | if (msrs[idx].l & K8_MSR_MCGCTL_NBE) | 2551 | if (reg->l & K8_MSR_MCGCTL_NBE) |
| 2567 | pvt->flags.ecc_report = 1; | 2552 | pvt->flags.ecc_report = 1; |
| 2568 | 2553 | ||
| 2569 | msrs[idx].l |= K8_MSR_MCGCTL_NBE; | 2554 | reg->l |= K8_MSR_MCGCTL_NBE; |
| 2570 | } else { | 2555 | } else { |
| 2571 | /* | 2556 | /* |
| 2572 | * Turn off ECC reporting only when it was off before | 2557 | * Turn off ECC reporting only when it was off before |
| 2573 | */ | 2558 | */ |
| 2574 | if (!pvt->flags.ecc_report) | 2559 | if (!pvt->flags.ecc_report) |
| 2575 | msrs[idx].l &= ~K8_MSR_MCGCTL_NBE; | 2560 | reg->l &= ~K8_MSR_MCGCTL_NBE; |
| 2576 | } | 2561 | } |
| 2577 | idx++; | ||
| 2578 | } | 2562 | } |
| 2579 | wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); | 2563 | wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); |
| 2580 | 2564 | ||
| 2581 | kfree(msrs); | ||
| 2582 | free_cpumask_var(cmask); | 2565 | free_cpumask_var(cmask); |
| 2583 | 2566 | ||
| 2584 | return 0; | 2567 | return 0; |
| @@ -3036,6 +3019,8 @@ static int __init amd64_edac_init(void) | |||
| 3036 | if (cache_k8_northbridges() < 0) | 3019 | if (cache_k8_northbridges() < 0) |
| 3037 | return err; | 3020 | return err; |
| 3038 | 3021 | ||
| 3022 | msrs = msrs_alloc(); | ||
| 3023 | |||
| 3039 | err = pci_register_driver(&amd64_pci_driver); | 3024 | err = pci_register_driver(&amd64_pci_driver); |
| 3040 | if (err) | 3025 | if (err) |
| 3041 | return err; | 3026 | return err; |
| @@ -3071,6 +3056,9 @@ static void __exit amd64_edac_exit(void) | |||
| 3071 | edac_pci_release_generic_ctl(amd64_ctl_pci); | 3056 | edac_pci_release_generic_ctl(amd64_ctl_pci); |
| 3072 | 3057 | ||
| 3073 | pci_unregister_driver(&amd64_pci_driver); | 3058 | pci_unregister_driver(&amd64_pci_driver); |
| 3059 | |||
| 3060 | msrs_free(msrs); | ||
| 3061 | msrs = NULL; | ||
| 3074 | } | 3062 | } |
| 3075 | 3063 | ||
| 3076 | module_init(amd64_edac_init); | 3064 | module_init(amd64_edac_init); |
