diff options
Diffstat (limited to 'arch/x86/kernel')
| -rw-r--r-- | arch/x86/kernel/cpuid.c | 15 | ||||
| -rw-r--r-- | arch/x86/kernel/msr.c | 38 | ||||
| -rw-r--r-- | arch/x86/kernel/tsc.c | 235 |
3 files changed, 218 insertions, 70 deletions
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index 14b11b3be31c..8e9cd6a8ec12 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c | |||
| @@ -89,6 +89,8 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, | |||
| 89 | struct cpuid_regs cmd; | 89 | struct cpuid_regs cmd; |
| 90 | int cpu = iminor(file->f_path.dentry->d_inode); | 90 | int cpu = iminor(file->f_path.dentry->d_inode); |
| 91 | u64 pos = *ppos; | 91 | u64 pos = *ppos; |
| 92 | ssize_t bytes = 0; | ||
| 93 | int err = 0; | ||
| 92 | 94 | ||
| 93 | if (count % 16) | 95 | if (count % 16) |
| 94 | return -EINVAL; /* Invalid chunk size */ | 96 | return -EINVAL; /* Invalid chunk size */ |
| @@ -96,14 +98,19 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, | |||
| 96 | for (; count; count -= 16) { | 98 | for (; count; count -= 16) { |
| 97 | cmd.eax = pos; | 99 | cmd.eax = pos; |
| 98 | cmd.ecx = pos >> 32; | 100 | cmd.ecx = pos >> 32; |
| 99 | smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1); | 101 | err = smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1); |
| 100 | if (copy_to_user(tmp, &cmd, 16)) | 102 | if (err) |
| 101 | return -EFAULT; | 103 | break; |
| 104 | if (copy_to_user(tmp, &cmd, 16)) { | ||
| 105 | err = -EFAULT; | ||
| 106 | break; | ||
| 107 | } | ||
| 102 | tmp += 16; | 108 | tmp += 16; |
| 109 | bytes += 16; | ||
| 103 | *ppos = ++pos; | 110 | *ppos = ++pos; |
| 104 | } | 111 | } |
| 105 | 112 | ||
| 106 | return tmp - buf; | 113 | return bytes ? bytes : err; |
| 107 | } | 114 | } |
| 108 | 115 | ||
| 109 | static int cpuid_open(struct inode *inode, struct file *file) | 116 | static int cpuid_open(struct inode *inode, struct file *file) |
diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index e43938086885..2e2af5d18191 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c | |||
| @@ -72,21 +72,28 @@ static ssize_t msr_read(struct file *file, char __user *buf, | |||
| 72 | u32 data[2]; | 72 | u32 data[2]; |
| 73 | u32 reg = *ppos; | 73 | u32 reg = *ppos; |
| 74 | int cpu = iminor(file->f_path.dentry->d_inode); | 74 | int cpu = iminor(file->f_path.dentry->d_inode); |
| 75 | int err; | 75 | int err = 0; |
| 76 | ssize_t bytes = 0; | ||
| 76 | 77 | ||
| 77 | if (count % 8) | 78 | if (count % 8) |
| 78 | return -EINVAL; /* Invalid chunk size */ | 79 | return -EINVAL; /* Invalid chunk size */ |
| 79 | 80 | ||
| 80 | for (; count; count -= 8) { | 81 | for (; count; count -= 8) { |
| 81 | err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]); | 82 | err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]); |
| 82 | if (err) | 83 | if (err) { |
| 83 | return -EIO; | 84 | if (err == -EFAULT) /* Fix idiotic error code */ |
| 84 | if (copy_to_user(tmp, &data, 8)) | 85 | err = -EIO; |
| 85 | return -EFAULT; | 86 | break; |
| 87 | } | ||
| 88 | if (copy_to_user(tmp, &data, 8)) { | ||
| 89 | err = -EFAULT; | ||
| 90 | break; | ||
| 91 | } | ||
| 86 | tmp += 2; | 92 | tmp += 2; |
| 93 | bytes += 8; | ||
| 87 | } | 94 | } |
| 88 | 95 | ||
| 89 | return ((char __user *)tmp) - buf; | 96 | return bytes ? bytes : err; |
| 90 | } | 97 | } |
| 91 | 98 | ||
| 92 | static ssize_t msr_write(struct file *file, const char __user *buf, | 99 | static ssize_t msr_write(struct file *file, const char __user *buf, |
| @@ -96,21 +103,28 @@ static ssize_t msr_write(struct file *file, const char __user *buf, | |||
| 96 | u32 data[2]; | 103 | u32 data[2]; |
| 97 | u32 reg = *ppos; | 104 | u32 reg = *ppos; |
| 98 | int cpu = iminor(file->f_path.dentry->d_inode); | 105 | int cpu = iminor(file->f_path.dentry->d_inode); |
| 99 | int err; | 106 | int err = 0; |
| 107 | ssize_t bytes = 0; | ||
| 100 | 108 | ||
| 101 | if (count % 8) | 109 | if (count % 8) |
| 102 | return -EINVAL; /* Invalid chunk size */ | 110 | return -EINVAL; /* Invalid chunk size */ |
| 103 | 111 | ||
| 104 | for (; count; count -= 8) { | 112 | for (; count; count -= 8) { |
| 105 | if (copy_from_user(&data, tmp, 8)) | 113 | if (copy_from_user(&data, tmp, 8)) { |
| 106 | return -EFAULT; | 114 | err = -EFAULT; |
| 115 | break; | ||
| 116 | } | ||
| 107 | err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]); | 117 | err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]); |
| 108 | if (err) | 118 | if (err) { |
| 109 | return -EIO; | 119 | if (err == -EFAULT) /* Fix idiotic error code */ |
| 120 | err = -EIO; | ||
| 121 | break; | ||
| 122 | } | ||
| 110 | tmp += 2; | 123 | tmp += 2; |
| 124 | bytes += 8; | ||
| 111 | } | 125 | } |
| 112 | 126 | ||
| 113 | return ((char __user *)tmp) - buf; | 127 | return bytes ? bytes : err; |
| 114 | } | 128 | } |
| 115 | 129 | ||
| 116 | static int msr_open(struct inode *inode, struct file *file) | 130 | static int msr_open(struct inode *inode, struct file *file) |
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 8e786b0d665a..ac79bd143da8 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
| @@ -127,75 +127,202 @@ static u64 tsc_read_refs(u64 *pm, u64 *hpet) | |||
| 127 | */ | 127 | */ |
| 128 | unsigned long native_calibrate_tsc(void) | 128 | unsigned long native_calibrate_tsc(void) |
| 129 | { | 129 | { |
| 130 | unsigned long flags; | 130 | u64 tsc1, tsc2, tr1, tr2, tsc, delta, pm1, pm2, hpet1, hpet2; |
| 131 | u64 tsc1, tsc2, tr1, tr2, delta, pm1, pm2, hpet1, hpet2; | 131 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; |
| 132 | int hpet = is_hpet_enabled(); | 132 | unsigned long flags, tscmin, tscmax; |
| 133 | unsigned int tsc_khz_val = 0; | 133 | int hpet = is_hpet_enabled(), pitcnt, i; |
| 134 | 134 | ||
| 135 | local_irq_save(flags); | 135 | /* |
| 136 | 136 | * Run 5 calibration loops to get the lowest frequency value | |
| 137 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | 137 | * (the best estimate). We use two different calibration modes |
| 138 | 138 | * here: | |
| 139 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | 139 | * |
| 140 | 140 | * 1) PIT loop. We set the PIT Channel 2 to oneshot mode and | |
| 141 | outb(0xb0, 0x43); | 141 | * load a timeout of 50ms. We read the time right after we |
| 142 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | 142 | * started the timer and wait until the PIT count down reaches |
| 143 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | 143 | * zero. In each wait loop iteration we read the TSC and check |
| 144 | tr1 = get_cycles(); | 144 | * the delta to the previous read. We keep track of the min |
| 145 | while ((inb(0x61) & 0x20) == 0); | 145 | * and max values of that delta. The delta is mostly defined |
| 146 | tr2 = get_cycles(); | 146 | * by the IO time of the PIT access, so we can detect when a |
| 147 | 147 | * SMI/SMM disturbance happend between the two reads. If the | |
| 148 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | 148 | * maximum time is significantly larger than the minimum time, |
| 149 | 149 | * then we discard the result and have another try. | |
| 150 | local_irq_restore(flags); | 150 | * |
| 151 | * 2) Reference counter. If available we use the HPET or the | ||
| 152 | * PMTIMER as a reference to check the sanity of that value. | ||
| 153 | * We use separate TSC readouts and check inside of the | ||
| 154 | * reference read for a SMI/SMM disturbance. We dicard | ||
| 155 | * disturbed values here as well. We do that around the PIT | ||
| 156 | * calibration delay loop as we have to wait for a certain | ||
| 157 | * amount of time anyway. | ||
| 158 | */ | ||
| 159 | for (i = 0; i < 5; i++) { | ||
| 160 | |||
| 161 | tscmin = ULONG_MAX; | ||
| 162 | tscmax = 0; | ||
| 163 | pitcnt = 0; | ||
| 164 | |||
| 165 | local_irq_save(flags); | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Read the start value and the reference count of | ||
| 169 | * hpet/pmtimer when available: | ||
| 170 | */ | ||
| 171 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | ||
| 172 | |||
| 173 | /* Set the Gate high, disable speaker */ | ||
| 174 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | ||
| 175 | |||
| 176 | /* | ||
| 177 | * Setup CTC channel 2* for mode 0, (interrupt on terminal | ||
| 178 | * count mode), binary count. Set the latch register to 50ms | ||
| 179 | * (LSB then MSB) to begin countdown. | ||
| 180 | * | ||
| 181 | * Some devices need a delay here. | ||
| 182 | */ | ||
| 183 | outb(0xb0, 0x43); | ||
| 184 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | ||
| 185 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | ||
| 186 | |||
| 187 | tsc = tr1 = tr2 = get_cycles(); | ||
| 188 | |||
| 189 | while ((inb(0x61) & 0x20) == 0) { | ||
| 190 | tr2 = get_cycles(); | ||
| 191 | delta = tr2 - tsc; | ||
| 192 | tsc = tr2; | ||
| 193 | if ((unsigned int) delta < tscmin) | ||
| 194 | tscmin = (unsigned int) delta; | ||
| 195 | if ((unsigned int) delta > tscmax) | ||
| 196 | tscmax = (unsigned int) delta; | ||
| 197 | pitcnt++; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* | ||
| 201 | * We waited at least 50ms above. Now read | ||
| 202 | * pmtimer/hpet reference again | ||
| 203 | */ | ||
| 204 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | ||
| 205 | |||
| 206 | local_irq_restore(flags); | ||
| 207 | |||
| 208 | /* | ||
| 209 | * Sanity checks: | ||
| 210 | * | ||
| 211 | * If we were not able to read the PIT more than 5000 | ||
| 212 | * times, then we have been hit by a massive SMI | ||
| 213 | * | ||
| 214 | * If the maximum is 10 times larger than the minimum, | ||
| 215 | * then we got hit by an SMI as well. | ||
| 216 | */ | ||
| 217 | if (pitcnt > 5000 && tscmax < 10 * tscmin) { | ||
| 218 | |||
| 219 | /* Calculate the PIT value */ | ||
| 220 | delta = tr2 - tr1; | ||
| 221 | do_div(delta, 50); | ||
| 222 | |||
| 223 | /* We take the smallest value into account */ | ||
| 224 | tsc_pit_min = min(tsc_pit_min, (unsigned long) delta); | ||
| 225 | } | ||
| 226 | |||
| 227 | /* hpet or pmtimer available ? */ | ||
| 228 | if (!hpet && !pm1 && !pm2) | ||
| 229 | continue; | ||
| 230 | |||
| 231 | /* Check, whether the sampling was disturbed by an SMI */ | ||
| 232 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) | ||
| 233 | continue; | ||
| 234 | |||
| 235 | tsc2 = (tsc2 - tsc1) * 1000000LL; | ||
| 236 | |||
| 237 | if (hpet) { | ||
| 238 | if (hpet2 < hpet1) | ||
| 239 | hpet2 += 0x100000000ULL; | ||
| 240 | hpet2 -= hpet1; | ||
| 241 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | ||
| 242 | do_div(tsc1, 1000000); | ||
| 243 | } else { | ||
| 244 | if (pm2 < pm1) | ||
| 245 | pm2 += (u64)ACPI_PM_OVRRUN; | ||
| 246 | pm2 -= pm1; | ||
| 247 | tsc1 = pm2 * 1000000000LL; | ||
| 248 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | ||
| 249 | } | ||
| 250 | |||
| 251 | do_div(tsc2, tsc1); | ||
| 252 | tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2); | ||
| 253 | } | ||
| 151 | 254 | ||
| 152 | /* | 255 | /* |
| 153 | * Preset the result with the raw and inaccurate PIT | 256 | * Now check the results. |
| 154 | * calibration value | ||
| 155 | */ | 257 | */ |
| 156 | delta = (tr2 - tr1); | 258 | if (tsc_pit_min == ULONG_MAX) { |
| 157 | do_div(delta, 50); | 259 | /* PIT gave no useful value */ |
| 158 | tsc_khz_val = delta; | 260 | printk(KERN_WARNING "TSC: PIT calibration failed due to " |
| 261 | "SMI disturbance.\n"); | ||
| 262 | |||
| 263 | /* We don't have an alternative source, disable TSC */ | ||
| 264 | if (!hpet && !pm1 && !pm2) { | ||
| 265 | printk("TSC: No reference (HPET/PMTIMER) available\n"); | ||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | |||
| 269 | /* The alternative source failed as well, disable TSC */ | ||
| 270 | if (tsc_ref_min == ULONG_MAX) { | ||
| 271 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration " | ||
| 272 | "failed due to SMI disturbance.\n"); | ||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | |||
| 276 | /* Use the alternative source */ | ||
| 277 | printk(KERN_INFO "TSC: using %s reference calibration\n", | ||
| 278 | hpet ? "HPET" : "PMTIMER"); | ||
| 279 | |||
| 280 | return tsc_ref_min; | ||
| 281 | } | ||
| 159 | 282 | ||
| 160 | /* hpet or pmtimer available ? */ | 283 | /* We don't have an alternative source, use the PIT calibration value */ |
| 161 | if (!hpet && !pm1 && !pm2) { | 284 | if (!hpet && !pm1 && !pm2) { |
| 162 | printk(KERN_INFO "TSC calibrated against PIT\n"); | 285 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); |
| 163 | goto out; | 286 | return tsc_pit_min; |
| 164 | } | 287 | } |
| 165 | 288 | ||
| 166 | /* Check, whether the sampling was disturbed by an SMI */ | 289 | /* The alternative source failed, use the PIT calibration value */ |
| 167 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) { | 290 | if (tsc_ref_min == ULONG_MAX) { |
| 168 | printk(KERN_WARNING "TSC calibration disturbed by SMI, " | 291 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed due " |
| 169 | "using PIT calibration result\n"); | 292 | "to SMI disturbance. Using PIT calibration\n"); |
| 170 | goto out; | 293 | return tsc_pit_min; |
| 171 | } | 294 | } |
| 172 | 295 | ||
| 173 | tsc2 = (tsc2 - tsc1) * 1000000LL; | 296 | /* Check the reference deviation */ |
| 174 | 297 | delta = ((u64) tsc_pit_min) * 100; | |
| 175 | if (hpet) { | 298 | do_div(delta, tsc_ref_min); |
| 176 | printk(KERN_INFO "TSC calibrated against HPET\n"); | 299 | |
| 177 | if (hpet2 < hpet1) | 300 | /* |
| 178 | hpet2 += 0x100000000ULL; | 301 | * If both calibration results are inside a 5% window, the we |
| 179 | hpet2 -= hpet1; | 302 | * use the lower frequency of those as it is probably the |
| 180 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | 303 | * closest estimate. |
| 181 | do_div(tsc1, 1000000); | 304 | */ |
| 182 | } else { | 305 | if (delta >= 95 && delta <= 105) { |
| 183 | printk(KERN_INFO "TSC calibrated against PM_TIMER\n"); | 306 | printk(KERN_INFO "TSC: PIT calibration confirmed by %s.\n", |
| 184 | if (pm2 < pm1) | 307 | hpet ? "HPET" : "PMTIMER"); |
| 185 | pm2 += (u64)ACPI_PM_OVRRUN; | 308 | printk(KERN_INFO "TSC: using %s calibration value\n", |
| 186 | pm2 -= pm1; | 309 | tsc_pit_min <= tsc_ref_min ? "PIT" : |
| 187 | tsc1 = pm2 * 1000000000LL; | 310 | hpet ? "HPET" : "PMTIMER"); |
| 188 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | 311 | return tsc_pit_min <= tsc_ref_min ? tsc_pit_min : tsc_ref_min; |
| 189 | } | 312 | } |
| 190 | 313 | ||
| 191 | do_div(tsc2, tsc1); | 314 | printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n", |
| 192 | tsc_khz_val = tsc2; | 315 | hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min); |
| 193 | 316 | ||
| 194 | out: | 317 | /* |
| 195 | return tsc_khz_val; | 318 | * The calibration values differ too much. In doubt, we use |
| 319 | * the PIT value as we know that there are PMTIMERs around | ||
| 320 | * running at double speed. | ||
| 321 | */ | ||
| 322 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); | ||
| 323 | return tsc_pit_min; | ||
| 196 | } | 324 | } |
| 197 | 325 | ||
| 198 | |||
| 199 | #ifdef CONFIG_X86_32 | 326 | #ifdef CONFIG_X86_32 |
| 200 | /* Only called from the Powernow K7 cpu freq driver */ | 327 | /* Only called from the Powernow K7 cpu freq driver */ |
| 201 | int recalibrate_cpu_khz(void) | 328 | int recalibrate_cpu_khz(void) |
