| 1 | /* $NetBSD: identcpu.c,v 1.50 2016/01/01 19:46:48 tls Exp $ */ |
| 2 | |
| 3 | /*- |
| 4 | * Copyright (c) 1999, 2000, 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc. |
| 5 | * All rights reserved. |
| 6 | * |
| 7 | * This code is derived from software contributed to The NetBSD Foundation |
| 8 | * by Frank van der Linden, and by Jason R. Thorpe. |
| 9 | * |
| 10 | * Redistribution and use in source and binary forms, with or without |
| 11 | * modification, are permitted provided that the following conditions |
| 12 | * are met: |
| 13 | * 1. Redistributions of source code must retain the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer. |
| 15 | * 2. Redistributions in binary form must reproduce the above copyright |
| 16 | * notice, this list of conditions and the following disclaimer in the |
| 17 | * documentation and/or other materials provided with the distribution. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
| 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
| 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 29 | * POSSIBILITY OF SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | #include <sys/cdefs.h> |
| 33 | __KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.50 2016/01/01 19:46:48 tls Exp $" ); |
| 34 | |
| 35 | #include "opt_xen.h" |
| 36 | |
| 37 | #include <sys/param.h> |
| 38 | #include <sys/systm.h> |
| 39 | #include <sys/device.h> |
| 40 | #include <sys/cpu.h> |
| 41 | |
| 42 | #include <uvm/uvm_extern.h> |
| 43 | |
| 44 | #include <machine/specialreg.h> |
| 45 | #include <machine/pio.h> |
| 46 | #include <machine/cpu.h> |
| 47 | |
| 48 | #include <x86/cputypes.h> |
| 49 | #include <x86/cacheinfo.h> |
| 50 | #include <x86/cpuvar.h> |
| 51 | #include <x86/cpu_msr.h> |
| 52 | |
| 53 | static const struct x86_cache_info intel_cpuid_cache_info[] = INTEL_CACHE_INFO; |
| 54 | |
| 55 | static const struct x86_cache_info amd_cpuid_l2cache_assoc_info[] = |
| 56 | AMD_L2CACHE_INFO; |
| 57 | |
| 58 | static const struct x86_cache_info amd_cpuid_l3cache_assoc_info[] = |
| 59 | AMD_L3CACHE_INFO; |
| 60 | |
| 61 | int cpu_vendor; |
| 62 | char cpu_brand_string[49]; |
| 63 | |
| 64 | int x86_fpu_save = FPU_SAVE_FSAVE; |
| 65 | unsigned int x86_fpu_save_size = 512; |
| 66 | uint64_t x86_xsave_features = 0; |
| 67 | |
| 68 | /* |
| 69 | * Note: these are just the ones that may not have a cpuid instruction. |
| 70 | * We deal with the rest in a different way. |
| 71 | */ |
| 72 | const int i386_nocpuid_cpus[] = { |
| 73 | CPUVENDOR_INTEL, CPUCLASS_386, /* CPU_386SX */ |
| 74 | CPUVENDOR_INTEL, CPUCLASS_386, /* CPU_386 */ |
| 75 | CPUVENDOR_INTEL, CPUCLASS_486, /* CPU_486SX */ |
| 76 | CPUVENDOR_INTEL, CPUCLASS_486, /* CPU_486 */ |
| 77 | CPUVENDOR_CYRIX, CPUCLASS_486, /* CPU_486DLC */ |
| 78 | CPUVENDOR_CYRIX, CPUCLASS_486, /* CPU_6x86 */ |
| 79 | CPUVENDOR_NEXGEN, CPUCLASS_386, /* CPU_NX586 */ |
| 80 | }; |
| 81 | |
| 82 | static const char cpu_vendor_names[][10] = { |
| 83 | "Unknown" , "Intel" , "NS/Cyrix" , "NexGen" , "AMD" , "IDT/VIA" , "Transmeta" , |
| 84 | "Vortex86" |
| 85 | }; |
| 86 | |
| 87 | static const struct x86_cache_info * |
| 88 | cache_info_lookup(const struct x86_cache_info *cai, uint8_t desc) |
| 89 | { |
| 90 | int i; |
| 91 | |
| 92 | for (i = 0; cai[i].cai_desc != 0; i++) { |
| 93 | if (cai[i].cai_desc == desc) |
| 94 | return (&cai[i]); |
| 95 | } |
| 96 | |
| 97 | return (NULL); |
| 98 | } |
| 99 | |
| 100 | static void |
| 101 | cpu_probe_intel_cache(struct cpu_info *ci) |
| 102 | { |
| 103 | const struct x86_cache_info *cai; |
| 104 | u_int descs[4]; |
| 105 | int iterations, i, j; |
| 106 | uint8_t desc; |
| 107 | |
| 108 | if (cpuid_level >= 2) { |
| 109 | /* Parse the cache info from `cpuid leaf 2', if we have it. */ |
| 110 | x86_cpuid(2, descs); |
| 111 | iterations = descs[0] & 0xff; |
| 112 | while (iterations-- > 0) { |
| 113 | for (i = 0; i < 4; i++) { |
| 114 | if (descs[i] & 0x80000000) |
| 115 | continue; |
| 116 | for (j = 0; j < 4; j++) { |
| 117 | if (i == 0 && j == 0) |
| 118 | continue; |
| 119 | desc = (descs[i] >> (j * 8)) & 0xff; |
| 120 | if (desc == 0) |
| 121 | continue; |
| 122 | cai = cache_info_lookup( |
| 123 | intel_cpuid_cache_info, desc); |
| 124 | if (cai != NULL) { |
| 125 | ci->ci_cinfo[cai->cai_index] = |
| 126 | *cai; |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | if (cpuid_level >= 4) { |
| 134 | int type, level; |
| 135 | int ways, partitions, linesize, sets; |
| 136 | int caitype = -1; |
| 137 | int totalsize; |
| 138 | |
| 139 | /* Parse the cache info from `cpuid leaf 4', if we have it. */ |
| 140 | for (i = 0; ; i++) { |
| 141 | x86_cpuid2(4, i, descs); |
| 142 | type = __SHIFTOUT(descs[0], CPUID_DCP_CACHETYPE); |
| 143 | if (type == CPUID_DCP_CACHETYPE_N) |
| 144 | break; |
| 145 | level = __SHIFTOUT(descs[0], CPUID_DCP_CACHELEVEL); |
| 146 | switch (level) { |
| 147 | case 1: |
| 148 | if (type == CPUID_DCP_CACHETYPE_I) |
| 149 | caitype = CAI_ICACHE; |
| 150 | else if (type == CPUID_DCP_CACHETYPE_D) |
| 151 | caitype = CAI_DCACHE; |
| 152 | else |
| 153 | caitype = -1; |
| 154 | break; |
| 155 | case 2: |
| 156 | if (type == CPUID_DCP_CACHETYPE_U) |
| 157 | caitype = CAI_L2CACHE; |
| 158 | else |
| 159 | caitype = -1; |
| 160 | break; |
| 161 | case 3: |
| 162 | if (type == CPUID_DCP_CACHETYPE_U) |
| 163 | caitype = CAI_L3CACHE; |
| 164 | else |
| 165 | caitype = -1; |
| 166 | break; |
| 167 | default: |
| 168 | caitype = -1; |
| 169 | break; |
| 170 | } |
| 171 | if (caitype == -1) |
| 172 | continue; |
| 173 | |
| 174 | ways = __SHIFTOUT(descs[1], CPUID_DCP_WAYS) + 1; |
| 175 | partitions =__SHIFTOUT(descs[1], CPUID_DCP_PARTITIONS) |
| 176 | + 1; |
| 177 | linesize = __SHIFTOUT(descs[1], CPUID_DCP_LINESIZE) |
| 178 | + 1; |
| 179 | sets = descs[2] + 1; |
| 180 | totalsize = ways * partitions * linesize * sets; |
| 181 | ci->ci_cinfo[caitype].cai_totalsize = totalsize; |
| 182 | ci->ci_cinfo[caitype].cai_associativity = ways; |
| 183 | ci->ci_cinfo[caitype].cai_linesize = linesize; |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | static void |
| 189 | cpu_probe_intel(struct cpu_info *ci) |
| 190 | { |
| 191 | |
| 192 | if (cpu_vendor != CPUVENDOR_INTEL) |
| 193 | return; |
| 194 | |
| 195 | cpu_probe_intel_cache(ci); |
| 196 | } |
| 197 | |
| 198 | static void |
| 199 | cpu_probe_amd_cache(struct cpu_info *ci) |
| 200 | { |
| 201 | const struct x86_cache_info *cp; |
| 202 | struct x86_cache_info *cai; |
| 203 | int family, model; |
| 204 | u_int descs[4]; |
| 205 | u_int lfunc; |
| 206 | |
| 207 | family = CPUID_TO_FAMILY(ci->ci_signature); |
| 208 | model = CPUID_TO_MODEL(ci->ci_signature); |
| 209 | |
| 210 | /* |
| 211 | * K5 model 0 has none of this info. |
| 212 | */ |
| 213 | if (family == 5 && model == 0) |
| 214 | return; |
| 215 | |
| 216 | /* |
| 217 | * Determine the largest extended function value. |
| 218 | */ |
| 219 | x86_cpuid(0x80000000, descs); |
| 220 | lfunc = descs[0]; |
| 221 | |
| 222 | /* |
| 223 | * Determine L1 cache/TLB info. |
| 224 | */ |
| 225 | if (lfunc < 0x80000005) { |
| 226 | /* No L1 cache info available. */ |
| 227 | return; |
| 228 | } |
| 229 | |
| 230 | x86_cpuid(0x80000005, descs); |
| 231 | |
| 232 | /* |
| 233 | * K6-III and higher have large page TLBs. |
| 234 | */ |
| 235 | if ((family == 5 && model >= 9) || family >= 6) { |
| 236 | cai = &ci->ci_cinfo[CAI_ITLB2]; |
| 237 | cai->cai_totalsize = AMD_L1_EAX_ITLB_ENTRIES(descs[0]); |
| 238 | cai->cai_associativity = AMD_L1_EAX_ITLB_ASSOC(descs[0]); |
| 239 | cai->cai_linesize = (4 * 1024 * 1024); |
| 240 | |
| 241 | cai = &ci->ci_cinfo[CAI_DTLB2]; |
| 242 | cai->cai_totalsize = AMD_L1_EAX_DTLB_ENTRIES(descs[0]); |
| 243 | cai->cai_associativity = AMD_L1_EAX_DTLB_ASSOC(descs[0]); |
| 244 | cai->cai_linesize = (4 * 1024 * 1024); |
| 245 | } |
| 246 | |
| 247 | cai = &ci->ci_cinfo[CAI_ITLB]; |
| 248 | cai->cai_totalsize = AMD_L1_EBX_ITLB_ENTRIES(descs[1]); |
| 249 | cai->cai_associativity = AMD_L1_EBX_ITLB_ASSOC(descs[1]); |
| 250 | cai->cai_linesize = (4 * 1024); |
| 251 | |
| 252 | cai = &ci->ci_cinfo[CAI_DTLB]; |
| 253 | cai->cai_totalsize = AMD_L1_EBX_DTLB_ENTRIES(descs[1]); |
| 254 | cai->cai_associativity = AMD_L1_EBX_DTLB_ASSOC(descs[1]); |
| 255 | cai->cai_linesize = (4 * 1024); |
| 256 | |
| 257 | cai = &ci->ci_cinfo[CAI_DCACHE]; |
| 258 | cai->cai_totalsize = AMD_L1_ECX_DC_SIZE(descs[2]); |
| 259 | cai->cai_associativity = AMD_L1_ECX_DC_ASSOC(descs[2]); |
| 260 | cai->cai_linesize = AMD_L1_ECX_DC_LS(descs[2]); |
| 261 | |
| 262 | cai = &ci->ci_cinfo[CAI_ICACHE]; |
| 263 | cai->cai_totalsize = AMD_L1_EDX_IC_SIZE(descs[3]); |
| 264 | cai->cai_associativity = AMD_L1_EDX_IC_ASSOC(descs[3]); |
| 265 | cai->cai_linesize = AMD_L1_EDX_IC_LS(descs[3]); |
| 266 | |
| 267 | /* |
| 268 | * Determine L2 cache/TLB info. |
| 269 | */ |
| 270 | if (lfunc < 0x80000006) { |
| 271 | /* No L2 cache info available. */ |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | x86_cpuid(0x80000006, descs); |
| 276 | |
| 277 | cai = &ci->ci_cinfo[CAI_L2CACHE]; |
| 278 | cai->cai_totalsize = AMD_L2_ECX_C_SIZE(descs[2]); |
| 279 | cai->cai_associativity = AMD_L2_ECX_C_ASSOC(descs[2]); |
| 280 | cai->cai_linesize = AMD_L2_ECX_C_LS(descs[2]); |
| 281 | |
| 282 | cp = cache_info_lookup(amd_cpuid_l2cache_assoc_info, |
| 283 | cai->cai_associativity); |
| 284 | if (cp != NULL) |
| 285 | cai->cai_associativity = cp->cai_associativity; |
| 286 | else |
| 287 | cai->cai_associativity = 0; /* XXX Unknown/reserved */ |
| 288 | |
| 289 | if (family < 0xf) { |
| 290 | /* No L3 cache info available. */ |
| 291 | return; |
| 292 | } |
| 293 | |
| 294 | cai = &ci->ci_cinfo[CAI_L3CACHE]; |
| 295 | cai->cai_totalsize = AMD_L3_EDX_C_SIZE(descs[3]); |
| 296 | cai->cai_associativity = AMD_L3_EDX_C_ASSOC(descs[3]); |
| 297 | cai->cai_linesize = AMD_L3_EDX_C_LS(descs[3]); |
| 298 | |
| 299 | cp = cache_info_lookup(amd_cpuid_l3cache_assoc_info, |
| 300 | cai->cai_associativity); |
| 301 | if (cp != NULL) |
| 302 | cai->cai_associativity = cp->cai_associativity; |
| 303 | else |
| 304 | cai->cai_associativity = 0; /* XXX Unknown reserved */ |
| 305 | |
| 306 | if (lfunc < 0x80000019) { |
| 307 | /* No 1GB Page TLB */ |
| 308 | return; |
| 309 | } |
| 310 | |
| 311 | x86_cpuid(0x80000019, descs); |
| 312 | |
| 313 | cai = &ci->ci_cinfo[CAI_L1_1GBDTLB]; |
| 314 | cai->cai_totalsize = AMD_L1_1GB_EAX_DTLB_ENTRIES(descs[1]); |
| 315 | cai->cai_associativity = AMD_L1_1GB_EAX_DTLB_ASSOC(descs[1]); |
| 316 | cai->cai_linesize = (1 * 1024); |
| 317 | |
| 318 | cai = &ci->ci_cinfo[CAI_L1_1GBITLB]; |
| 319 | cai->cai_totalsize = AMD_L1_1GB_EAX_IUTLB_ENTRIES(descs[0]); |
| 320 | cai->cai_associativity = AMD_L1_1GB_EAX_IUTLB_ASSOC(descs[0]); |
| 321 | cai->cai_linesize = (1 * 1024); |
| 322 | |
| 323 | cai = &ci->ci_cinfo[CAI_L2_1GBDTLB]; |
| 324 | cai->cai_totalsize = AMD_L2_1GB_EBX_DUTLB_ENTRIES(descs[1]); |
| 325 | cai->cai_associativity = AMD_L2_1GB_EBX_DUTLB_ASSOC(descs[1]); |
| 326 | cai->cai_linesize = (1 * 1024); |
| 327 | |
| 328 | cai = &ci->ci_cinfo[CAI_L2_1GBITLB]; |
| 329 | cai->cai_totalsize = AMD_L2_1GB_EBX_IUTLB_ENTRIES(descs[0]); |
| 330 | cai->cai_associativity = AMD_L2_1GB_EBX_IUTLB_ASSOC(descs[0]); |
| 331 | cai->cai_linesize = (1 * 1024); |
| 332 | } |
| 333 | |
| 334 | static void |
| 335 | cpu_probe_k5(struct cpu_info *ci) |
| 336 | { |
| 337 | int flag; |
| 338 | |
| 339 | if (cpu_vendor != CPUVENDOR_AMD || |
| 340 | CPUID_TO_FAMILY(ci->ci_signature) != 5) |
| 341 | return; |
| 342 | |
| 343 | if (CPUID_TO_MODEL(ci->ci_signature) == 0) { |
| 344 | /* |
| 345 | * According to the AMD Processor Recognition App Note, |
| 346 | * the AMD-K5 Model 0 uses the wrong bit to indicate |
| 347 | * support for global PTEs, instead using bit 9 (APIC) |
| 348 | * rather than bit 13 (i.e. "0x200" vs. 0x2000". Oops!). |
| 349 | */ |
| 350 | flag = ci->ci_feat_val[0]; |
| 351 | if ((flag & CPUID_APIC) != 0) |
| 352 | flag = (flag & ~CPUID_APIC) | CPUID_PGE; |
| 353 | ci->ci_feat_val[0] = flag; |
| 354 | } |
| 355 | |
| 356 | cpu_probe_amd_cache(ci); |
| 357 | } |
| 358 | |
| 359 | static void |
| 360 | cpu_probe_k678(struct cpu_info *ci) |
| 361 | { |
| 362 | |
| 363 | if (cpu_vendor != CPUVENDOR_AMD || |
| 364 | CPUID_TO_FAMILY(ci->ci_signature) < 6) |
| 365 | return; |
| 366 | |
| 367 | cpu_probe_amd_cache(ci); |
| 368 | } |
| 369 | |
| 370 | static inline uint8_t |
| 371 | cyrix_read_reg(uint8_t reg) |
| 372 | { |
| 373 | |
| 374 | outb(0x22, reg); |
| 375 | return inb(0x23); |
| 376 | } |
| 377 | |
| 378 | static inline void |
| 379 | cyrix_write_reg(uint8_t reg, uint8_t data) |
| 380 | { |
| 381 | |
| 382 | outb(0x22, reg); |
| 383 | outb(0x23, data); |
| 384 | } |
| 385 | |
| 386 | static void |
| 387 | cpu_probe_cyrix_cmn(struct cpu_info *ci) |
| 388 | { |
| 389 | /* |
| 390 | * i8254 latch check routine: |
| 391 | * National Geode (formerly Cyrix MediaGX) has a serious bug in |
| 392 | * its built-in i8254-compatible clock module (cs5510 cs5520). |
| 393 | * Set the variable 'clock_broken_latch' to indicate it. |
| 394 | * |
| 395 | * This bug is not present in the cs5530, and the flag |
| 396 | * is disabled again in sys/arch/i386/pci/pcib.c if this later |
| 397 | * model device is detected. Ideally, this work-around should not |
| 398 | * even be in here, it should be in there. XXX |
| 399 | */ |
| 400 | uint8_t c3; |
| 401 | #ifndef XEN |
| 402 | extern int clock_broken_latch; |
| 403 | |
| 404 | switch (ci->ci_signature) { |
| 405 | case 0x440: /* Cyrix MediaGX */ |
| 406 | case 0x540: /* GXm */ |
| 407 | clock_broken_latch = 1; |
| 408 | break; |
| 409 | } |
| 410 | #endif |
| 411 | |
| 412 | /* set up various cyrix registers */ |
| 413 | /* |
| 414 | * Enable suspend on halt (powersave mode). |
| 415 | * When powersave mode is enabled, the TSC stops counting |
| 416 | * while the CPU is halted in idle() waiting for an interrupt. |
| 417 | * This means we can't use the TSC for interval time in |
| 418 | * microtime(9), and thus it is disabled here. |
| 419 | * |
| 420 | * It still makes a perfectly good cycle counter |
| 421 | * for program profiling, so long as you remember you're |
| 422 | * counting cycles, and not time. Further, if you don't |
| 423 | * mind not using powersave mode, the TSC works just fine, |
| 424 | * so this should really be optional. XXX |
| 425 | */ |
| 426 | cyrix_write_reg(0xc2, cyrix_read_reg(0xc2) | 0x08); |
| 427 | |
| 428 | /* |
| 429 | * Do not disable the TSC on the Geode GX, it's reported to |
| 430 | * work fine. |
| 431 | */ |
| 432 | if (ci->ci_signature != 0x552) |
| 433 | ci->ci_feat_val[0] &= ~CPUID_TSC; |
| 434 | |
| 435 | /* enable access to ccr4/ccr5 */ |
| 436 | c3 = cyrix_read_reg(0xC3); |
| 437 | cyrix_write_reg(0xC3, c3 | 0x10); |
| 438 | /* cyrix's workaround for the "coma bug" */ |
| 439 | cyrix_write_reg(0x31, cyrix_read_reg(0x31) | 0xf8); |
| 440 | cyrix_write_reg(0x32, cyrix_read_reg(0x32) | 0x7f); |
| 441 | cyrix_write_reg(0x33, cyrix_read_reg(0x33) & ~0xff); |
| 442 | cyrix_write_reg(0x3c, cyrix_read_reg(0x3c) | 0x87); |
| 443 | /* disable access to ccr4/ccr5 */ |
| 444 | cyrix_write_reg(0xC3, c3); |
| 445 | } |
| 446 | |
| 447 | static void |
| 448 | cpu_probe_cyrix(struct cpu_info *ci) |
| 449 | { |
| 450 | |
| 451 | if (cpu_vendor != CPUVENDOR_CYRIX || |
| 452 | CPUID_TO_FAMILY(ci->ci_signature) < 4 || |
| 453 | CPUID_TO_FAMILY(ci->ci_signature) > 6) |
| 454 | return; |
| 455 | |
| 456 | cpu_probe_cyrix_cmn(ci); |
| 457 | } |
| 458 | |
| 459 | static void |
| 460 | cpu_probe_winchip(struct cpu_info *ci) |
| 461 | { |
| 462 | |
| 463 | if (cpu_vendor != CPUVENDOR_IDT) |
| 464 | return; |
| 465 | |
| 466 | switch (CPUID_TO_FAMILY(ci->ci_signature)) { |
| 467 | case 5: |
| 468 | /* WinChip C6 */ |
| 469 | if (CPUID_TO_MODEL(ci->ci_signature) == 4) |
| 470 | ci->ci_feat_val[0] &= ~CPUID_TSC; |
| 471 | break; |
| 472 | case 6: |
| 473 | /* |
| 474 | * VIA Eden ESP |
| 475 | * |
| 476 | * Quoting from page 3-4 of: "VIA Eden ESP Processor Datasheet" |
| 477 | * http://www.via.com.tw/download/mainboards/6/14/Eden20v115.pdf |
| 478 | * |
| 479 | * 1. The CMPXCHG8B instruction is provided and always enabled, |
| 480 | * however, it appears disabled in the corresponding CPUID |
| 481 | * function bit 0 to avoid a bug in an early version of |
| 482 | * Windows NT. However, this default can be changed via a |
| 483 | * bit in the FCR MSR. |
| 484 | */ |
| 485 | ci->ci_feat_val[0] |= CPUID_CX8; |
| 486 | wrmsr(MSR_VIA_FCR, rdmsr(MSR_VIA_FCR) | 0x00000001); |
| 487 | break; |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | static void |
| 492 | cpu_probe_c3(struct cpu_info *ci) |
| 493 | { |
| 494 | u_int family, model, stepping, descs[4], lfunc, msr; |
| 495 | struct x86_cache_info *cai; |
| 496 | |
| 497 | if (cpu_vendor != CPUVENDOR_IDT || |
| 498 | CPUID_TO_FAMILY(ci->ci_signature) < 6) |
| 499 | return; |
| 500 | |
| 501 | family = CPUID_TO_FAMILY(ci->ci_signature); |
| 502 | model = CPUID_TO_MODEL(ci->ci_signature); |
| 503 | stepping = CPUID_TO_STEPPING(ci->ci_signature); |
| 504 | |
| 505 | /* Determine the largest extended function value. */ |
| 506 | x86_cpuid(0x80000000, descs); |
| 507 | lfunc = descs[0]; |
| 508 | |
| 509 | if (family > 6 || model > 0x9 || (model == 0x9 && stepping >= 3)) { |
| 510 | /* Nehemiah or Esther */ |
| 511 | x86_cpuid(0xc0000000, descs); |
| 512 | lfunc = descs[0]; |
| 513 | if (lfunc >= 0xc0000001) { /* has ACE, RNG */ |
| 514 | int rng_enable = 0, ace_enable = 0; |
| 515 | x86_cpuid(0xc0000001, descs); |
| 516 | lfunc = descs[3]; |
| 517 | ci->ci_feat_val[4] = lfunc; |
| 518 | /* Check for and enable RNG */ |
| 519 | if (lfunc & CPUID_VIA_HAS_RNG) { |
| 520 | if (!(lfunc & CPUID_VIA_DO_RNG)) { |
| 521 | rng_enable++; |
| 522 | ci->ci_feat_val[4] |= CPUID_VIA_DO_RNG; |
| 523 | } |
| 524 | } |
| 525 | /* Check for and enable ACE (AES-CBC) */ |
| 526 | if (lfunc & CPUID_VIA_HAS_ACE) { |
| 527 | if (!(lfunc & CPUID_VIA_DO_ACE)) { |
| 528 | ace_enable++; |
| 529 | ci->ci_feat_val[4] |= CPUID_VIA_DO_ACE; |
| 530 | } |
| 531 | } |
| 532 | /* Check for and enable SHA */ |
| 533 | if (lfunc & CPUID_VIA_HAS_PHE) { |
| 534 | if (!(lfunc & CPUID_VIA_DO_PHE)) { |
| 535 | ace_enable++; |
| 536 | ci->ci_feat_val[4] |= CPUID_VIA_DO_PHE; |
| 537 | } |
| 538 | } |
| 539 | /* Check for and enable ACE2 (AES-CTR) */ |
| 540 | if (lfunc & CPUID_VIA_HAS_ACE2) { |
| 541 | if (!(lfunc & CPUID_VIA_DO_ACE2)) { |
| 542 | ace_enable++; |
| 543 | ci->ci_feat_val[4] |= CPUID_VIA_DO_ACE2; |
| 544 | } |
| 545 | } |
| 546 | /* Check for and enable PMM (modmult engine) */ |
| 547 | if (lfunc & CPUID_VIA_HAS_PMM) { |
| 548 | if (!(lfunc & CPUID_VIA_DO_PMM)) { |
| 549 | ace_enable++; |
| 550 | ci->ci_feat_val[4] |= CPUID_VIA_DO_PMM; |
| 551 | } |
| 552 | } |
| 553 | |
| 554 | /* Actually do the enables. */ |
| 555 | if (rng_enable) { |
| 556 | msr = rdmsr(MSR_VIA_RNG); |
| 557 | msr |= MSR_VIA_RNG_ENABLE; |
| 558 | /* C7 stepping 8 and subsequent CPUs have dual RNG */ |
| 559 | if (model > 0xA || (model == 0xA && stepping > 0x7)) { |
| 560 | msr |= MSR_VIA_RNG_2NOISE; |
| 561 | } |
| 562 | wrmsr(MSR_VIA_RNG, msr); |
| 563 | } |
| 564 | |
| 565 | if (ace_enable) { |
| 566 | msr = rdmsr(MSR_VIA_ACE); |
| 567 | wrmsr(MSR_VIA_ACE, msr | MSR_VIA_ACE_ENABLE); |
| 568 | } |
| 569 | |
| 570 | } |
| 571 | } |
| 572 | |
| 573 | /* |
| 574 | * Determine L1 cache/TLB info. |
| 575 | */ |
| 576 | if (lfunc < 0x80000005) { |
| 577 | /* No L1 cache info available. */ |
| 578 | return; |
| 579 | } |
| 580 | |
| 581 | x86_cpuid(0x80000005, descs); |
| 582 | |
| 583 | cai = &ci->ci_cinfo[CAI_ITLB]; |
| 584 | cai->cai_totalsize = VIA_L1_EBX_ITLB_ENTRIES(descs[1]); |
| 585 | cai->cai_associativity = VIA_L1_EBX_ITLB_ASSOC(descs[1]); |
| 586 | cai->cai_linesize = (4 * 1024); |
| 587 | |
| 588 | cai = &ci->ci_cinfo[CAI_DTLB]; |
| 589 | cai->cai_totalsize = VIA_L1_EBX_DTLB_ENTRIES(descs[1]); |
| 590 | cai->cai_associativity = VIA_L1_EBX_DTLB_ASSOC(descs[1]); |
| 591 | cai->cai_linesize = (4 * 1024); |
| 592 | |
| 593 | cai = &ci->ci_cinfo[CAI_DCACHE]; |
| 594 | cai->cai_totalsize = VIA_L1_ECX_DC_SIZE(descs[2]); |
| 595 | cai->cai_associativity = VIA_L1_ECX_DC_ASSOC(descs[2]); |
| 596 | cai->cai_linesize = VIA_L1_EDX_IC_LS(descs[2]); |
| 597 | if (family == 6 && model == 9 && stepping == 8) { |
| 598 | /* Erratum: stepping 8 reports 4 when it should be 2 */ |
| 599 | cai->cai_associativity = 2; |
| 600 | } |
| 601 | |
| 602 | cai = &ci->ci_cinfo[CAI_ICACHE]; |
| 603 | cai->cai_totalsize = VIA_L1_EDX_IC_SIZE(descs[3]); |
| 604 | cai->cai_associativity = VIA_L1_EDX_IC_ASSOC(descs[3]); |
| 605 | cai->cai_linesize = VIA_L1_EDX_IC_LS(descs[3]); |
| 606 | if (family == 6 && model == 9 && stepping == 8) { |
| 607 | /* Erratum: stepping 8 reports 4 when it should be 2 */ |
| 608 | cai->cai_associativity = 2; |
| 609 | } |
| 610 | |
| 611 | /* |
| 612 | * Determine L2 cache/TLB info. |
| 613 | */ |
| 614 | if (lfunc < 0x80000006) { |
| 615 | /* No L2 cache info available. */ |
| 616 | return; |
| 617 | } |
| 618 | |
| 619 | x86_cpuid(0x80000006, descs); |
| 620 | |
| 621 | cai = &ci->ci_cinfo[CAI_L2CACHE]; |
| 622 | if (family > 6 || model >= 9) { |
| 623 | cai->cai_totalsize = VIA_L2N_ECX_C_SIZE(descs[2]); |
| 624 | cai->cai_associativity = VIA_L2N_ECX_C_ASSOC(descs[2]); |
| 625 | cai->cai_linesize = VIA_L2N_ECX_C_LS(descs[2]); |
| 626 | } else { |
| 627 | cai->cai_totalsize = VIA_L2_ECX_C_SIZE(descs[2]); |
| 628 | cai->cai_associativity = VIA_L2_ECX_C_ASSOC(descs[2]); |
| 629 | cai->cai_linesize = VIA_L2_ECX_C_LS(descs[2]); |
| 630 | } |
| 631 | } |
| 632 | |
| 633 | static void |
| 634 | cpu_probe_geode(struct cpu_info *ci) |
| 635 | { |
| 636 | |
| 637 | if (memcmp("Geode by NSC" , ci->ci_vendor, 12) != 0 || |
| 638 | CPUID_TO_FAMILY(ci->ci_signature) != 5) |
| 639 | return; |
| 640 | |
| 641 | cpu_probe_cyrix_cmn(ci); |
| 642 | cpu_probe_amd_cache(ci); |
| 643 | } |
| 644 | |
| 645 | static void |
| 646 | cpu_probe_vortex86(struct cpu_info *ci) |
| 647 | { |
| 648 | #define PCI_MODE1_ADDRESS_REG 0x0cf8 |
| 649 | #define PCI_MODE1_DATA_REG 0x0cfc |
| 650 | #define PCI_MODE1_ENABLE 0x80000000UL |
| 651 | |
| 652 | uint32_t reg; |
| 653 | |
| 654 | if (cpu_vendor != CPUVENDOR_VORTEX86) |
| 655 | return; |
| 656 | /* |
| 657 | * CPU model available from "Customer ID register" in |
| 658 | * North Bridge Function 0 PCI space |
| 659 | * we can't use pci_conf_read() because the PCI subsystem is not |
| 660 | * not initialised early enough |
| 661 | */ |
| 662 | |
| 663 | outl(PCI_MODE1_ADDRESS_REG, PCI_MODE1_ENABLE | 0x90); |
| 664 | reg = inl(PCI_MODE1_DATA_REG); |
| 665 | |
| 666 | switch(reg) { |
| 667 | case 0x31504d44: |
| 668 | strcpy(cpu_brand_string, "Vortex86SX" ); |
| 669 | break; |
| 670 | case 0x32504d44: |
| 671 | strcpy(cpu_brand_string, "Vortex86DX" ); |
| 672 | break; |
| 673 | case 0x33504d44: |
| 674 | strcpy(cpu_brand_string, "Vortex86MX" ); |
| 675 | break; |
| 676 | case 0x37504d44: |
| 677 | strcpy(cpu_brand_string, "Vortex86EX" ); |
| 678 | break; |
| 679 | default: |
| 680 | strcpy(cpu_brand_string, "Unknown Vortex86" ); |
| 681 | break; |
| 682 | } |
| 683 | |
| 684 | #undef PCI_MODE1_ENABLE |
| 685 | #undef PCI_MODE1_ADDRESS_REG |
| 686 | #undef PCI_MODE1_DATA_REG |
| 687 | } |
| 688 | |
| 689 | #if !defined(__i386__) || defined(XEN) |
| 690 | #define cpu_probe_old_fpu(ci) |
| 691 | #else |
| 692 | static void |
| 693 | cpu_probe_old_fpu(struct cpu_info *ci) |
| 694 | { |
| 695 | uint16_t control; |
| 696 | |
| 697 | /* Check that there really is an fpu (496SX) */ |
| 698 | clts(); |
| 699 | fninit(); |
| 700 | /* Read default control word */ |
| 701 | fnstcw(&control); |
| 702 | if (control != __INITIAL_NPXCW__) { |
| 703 | /* Must be a 486SX, trap FP instructions */ |
| 704 | lcr0((rcr0() & ~CR0_MP) | CR0_EM); |
| 705 | i386_fpu_present = 0; |
| 706 | return; |
| 707 | } |
| 708 | |
| 709 | /* Check for 'FDIV' bug on the original Pentium */ |
| 710 | if (npx586bug1(4195835, 3145727) != 0) |
| 711 | /* NB 120+MHz cpus are not affected */ |
| 712 | i386_fpu_fdivbug = 1; |
| 713 | |
| 714 | stts(); |
| 715 | } |
| 716 | #endif |
| 717 | |
| 718 | static void |
| 719 | cpu_probe_fpu(struct cpu_info *ci) |
| 720 | { |
| 721 | u_int descs[4]; |
| 722 | |
| 723 | #ifdef i386 /* amd64 always has fxsave, sse and sse2 */ |
| 724 | /* If we have FXSAVE/FXRESTOR, use them. */ |
| 725 | if ((ci->ci_feat_val[0] & CPUID_FXSR) == 0) { |
| 726 | i386_use_fxsave = 0; |
| 727 | /* Allow for no fpu even if cpuid is supported */ |
| 728 | cpu_probe_old_fpu(ci); |
| 729 | return; |
| 730 | } |
| 731 | |
| 732 | i386_use_fxsave = 1; |
| 733 | /* |
| 734 | * If we have SSE/SSE2, enable XMM exceptions, and |
| 735 | * notify userland. |
| 736 | */ |
| 737 | if (ci->ci_feat_val[0] & CPUID_SSE) |
| 738 | i386_has_sse = 1; |
| 739 | if (ci->ci_feat_val[0] & CPUID_SSE2) |
| 740 | i386_has_sse2 = 1; |
| 741 | #else |
| 742 | /* |
| 743 | * For amd64 i386_use_fxsave, i386_has_sse and i386_has_sse2 are |
| 744 | * #defined to 1. |
| 745 | */ |
| 746 | #endif /* i386 */ |
| 747 | |
| 748 | x86_fpu_save = FPU_SAVE_FXSAVE; |
| 749 | |
| 750 | /* See if xsave (for AVX is supported) */ |
| 751 | if ((ci->ci_feat_val[1] & CPUID2_XSAVE) == 0) |
| 752 | return; |
| 753 | |
| 754 | x86_fpu_save = FPU_SAVE_XSAVE; |
| 755 | |
| 756 | /* xsaveopt ought to be faster than xsave */ |
| 757 | x86_cpuid2(0xd, 1, descs); |
| 758 | if (descs[0] & CPUID_PES1_XSAVEOPT) |
| 759 | x86_fpu_save = FPU_SAVE_XSAVEOPT; |
| 760 | |
| 761 | /* Get features and maximum size of the save area */ |
| 762 | x86_cpuid(0xd, descs); |
| 763 | /* XXX these probably ought to be per-cpu */ |
| 764 | if (descs[2] > 512) |
| 765 | x86_fpu_save_size = descs[2]; |
| 766 | #ifndef XEN |
| 767 | x86_xsave_features = (uint64_t)descs[3] << 32 | descs[0]; |
| 768 | #endif |
| 769 | } |
| 770 | |
| 771 | void |
| 772 | cpu_probe(struct cpu_info *ci) |
| 773 | { |
| 774 | u_int descs[4]; |
| 775 | int i; |
| 776 | uint32_t miscbytes; |
| 777 | uint32_t brand[12]; |
| 778 | |
| 779 | cpu_vendor = i386_nocpuid_cpus[cputype << 1]; |
| 780 | cpu_class = i386_nocpuid_cpus[(cputype << 1) + 1]; |
| 781 | |
| 782 | if (cpuid_level < 0) { |
| 783 | /* cpuid instruction not supported */ |
| 784 | cpu_probe_old_fpu(ci); |
| 785 | return; |
| 786 | } |
| 787 | |
| 788 | for (i = 0; i < __arraycount(ci->ci_feat_val); i++) { |
| 789 | ci->ci_feat_val[i] = 0; |
| 790 | } |
| 791 | |
| 792 | x86_cpuid(0, descs); |
| 793 | cpuid_level = descs[0]; |
| 794 | ci->ci_max_cpuid = descs[0]; |
| 795 | |
| 796 | ci->ci_vendor[0] = descs[1]; |
| 797 | ci->ci_vendor[2] = descs[2]; |
| 798 | ci->ci_vendor[1] = descs[3]; |
| 799 | ci->ci_vendor[3] = 0; |
| 800 | |
| 801 | if (memcmp(ci->ci_vendor, "GenuineIntel" , 12) == 0) |
| 802 | cpu_vendor = CPUVENDOR_INTEL; |
| 803 | else if (memcmp(ci->ci_vendor, "AuthenticAMD" , 12) == 0) |
| 804 | cpu_vendor = CPUVENDOR_AMD; |
| 805 | else if (memcmp(ci->ci_vendor, "CyrixInstead" , 12) == 0) |
| 806 | cpu_vendor = CPUVENDOR_CYRIX; |
| 807 | else if (memcmp(ci->ci_vendor, "Geode by NSC" , 12) == 0) |
| 808 | cpu_vendor = CPUVENDOR_CYRIX; |
| 809 | else if (memcmp(ci->ci_vendor, "CentaurHauls" , 12) == 0) |
| 810 | cpu_vendor = CPUVENDOR_IDT; |
| 811 | else if (memcmp(ci->ci_vendor, "GenuineTMx86" , 12) == 0) |
| 812 | cpu_vendor = CPUVENDOR_TRANSMETA; |
| 813 | else if (memcmp(ci->ci_vendor, "Vortex86 SoC" , 12) == 0) |
| 814 | cpu_vendor = CPUVENDOR_VORTEX86; |
| 815 | else |
| 816 | cpu_vendor = CPUVENDOR_UNKNOWN; |
| 817 | |
| 818 | if (cpuid_level >= 1) { |
| 819 | x86_cpuid(1, descs); |
| 820 | ci->ci_signature = descs[0]; |
| 821 | miscbytes = descs[1]; |
| 822 | ci->ci_feat_val[1] = descs[2]; |
| 823 | ci->ci_feat_val[0] = descs[3]; |
| 824 | |
| 825 | /* Determine family + class. */ |
| 826 | cpu_class = CPUID_TO_FAMILY(ci->ci_signature) |
| 827 | + (CPUCLASS_386 - 3); |
| 828 | if (cpu_class > CPUCLASS_686) |
| 829 | cpu_class = CPUCLASS_686; |
| 830 | |
| 831 | /* CLFLUSH line size is next 8 bits */ |
| 832 | if (ci->ci_feat_val[0] & CPUID_CFLUSH) |
| 833 | ci->ci_cflush_lsize = ((miscbytes >> 8) & 0xff) << 3; |
| 834 | ci->ci_initapicid = (miscbytes >> 24) & 0xff; |
| 835 | } |
| 836 | |
| 837 | /* |
| 838 | * Get the basic information from the extended cpuid leafs. |
| 839 | * These were first implemented by amd, but most of the values |
| 840 | * match with those generated by modern intel cpus. |
| 841 | */ |
| 842 | x86_cpuid(0x80000000, descs); |
| 843 | if (descs[0] >= 0x80000000) |
| 844 | ci->ci_max_ext_cpuid = descs[0]; |
| 845 | else |
| 846 | ci->ci_max_ext_cpuid = 0; |
| 847 | |
| 848 | if (ci->ci_max_ext_cpuid >= 0x80000001) { |
| 849 | /* Determine the extended feature flags. */ |
| 850 | x86_cpuid(0x80000001, descs); |
| 851 | ci->ci_feat_val[3] = descs[2]; /* %ecx */ |
| 852 | ci->ci_feat_val[2] = descs[3]; /* %edx */ |
| 853 | } |
| 854 | |
| 855 | if (ci->ci_max_ext_cpuid >= 0x80000004) { |
| 856 | x86_cpuid(0x80000002, brand); |
| 857 | x86_cpuid(0x80000003, brand + 4); |
| 858 | x86_cpuid(0x80000004, brand + 8); |
| 859 | /* Skip leading spaces on brand */ |
| 860 | for (i = 0; i < 48; i++) { |
| 861 | if (((char *) brand)[i] != ' ') |
| 862 | break; |
| 863 | } |
| 864 | memcpy(cpu_brand_string, ((char *) brand) + i, 48 - i); |
| 865 | } |
| 866 | |
| 867 | /* |
| 868 | * Get the structured extended features. |
| 869 | */ |
| 870 | if (cpuid_level >= 7) { |
| 871 | x86_cpuid(7, descs); |
| 872 | ci->ci_feat_val[5] = descs[1]; /* %ebx */ |
| 873 | ci->ci_feat_val[6] = descs[2]; /* %ecx */ |
| 874 | } |
| 875 | |
| 876 | cpu_probe_intel(ci); |
| 877 | cpu_probe_k5(ci); |
| 878 | cpu_probe_k678(ci); |
| 879 | cpu_probe_cyrix(ci); |
| 880 | cpu_probe_winchip(ci); |
| 881 | cpu_probe_c3(ci); |
| 882 | cpu_probe_geode(ci); |
| 883 | cpu_probe_vortex86(ci); |
| 884 | |
| 885 | cpu_probe_fpu(ci); |
| 886 | |
| 887 | x86_cpu_topology(ci); |
| 888 | |
| 889 | if (cpu_vendor != CPUVENDOR_AMD && (ci->ci_feat_val[0] & CPUID_TM) && |
| 890 | (rdmsr(MSR_MISC_ENABLE) & (1 << 3)) == 0) { |
| 891 | /* Enable thermal monitor 1. */ |
| 892 | wrmsr(MSR_MISC_ENABLE, rdmsr(MSR_MISC_ENABLE) | (1<<3)); |
| 893 | } |
| 894 | |
| 895 | ci->ci_feat_val[0] &= ~CPUID_FEAT_BLACKLIST; |
| 896 | if (ci == &cpu_info_primary) { |
| 897 | /* If first. Boot Processor is the cpu_feature reference. */ |
| 898 | for (i = 0; i < __arraycount(cpu_feature); i++) { |
| 899 | cpu_feature[i] = ci->ci_feat_val[i]; |
| 900 | } |
| 901 | #ifndef XEN |
| 902 | /* Early patch of text segment. */ |
| 903 | x86_patch(true); |
| 904 | #endif |
| 905 | } else { |
| 906 | /* |
| 907 | * If not first. Warn about cpu_feature mismatch for |
| 908 | * secondary CPUs. |
| 909 | */ |
| 910 | for (i = 0; i < __arraycount(cpu_feature); i++) { |
| 911 | if (cpu_feature[i] != ci->ci_feat_val[i]) |
| 912 | aprint_error_dev(ci->ci_dev, |
| 913 | "feature mismatch: cpu_feature[%d] is " |
| 914 | "%#x, but CPU reported %#x\n" , |
| 915 | i, cpu_feature[i], ci->ci_feat_val[i]); |
| 916 | } |
| 917 | } |
| 918 | } |
| 919 | |
| 920 | /* Write what we know about the cpu to the console... */ |
| 921 | void |
| 922 | cpu_identify(struct cpu_info *ci) |
| 923 | { |
| 924 | |
| 925 | cpu_setmodel("%s %d86-class" , |
| 926 | cpu_vendor_names[cpu_vendor], cpu_class + 3); |
| 927 | if (cpu_brand_string[0] != '\0') { |
| 928 | aprint_normal_dev(ci->ci_dev, "%s" , cpu_brand_string); |
| 929 | } else { |
| 930 | aprint_normal_dev(ci->ci_dev, "%s" , cpu_getmodel()); |
| 931 | if (ci->ci_data.cpu_cc_freq != 0) |
| 932 | aprint_normal(", %dMHz" , |
| 933 | (int)(ci->ci_data.cpu_cc_freq / 1000000)); |
| 934 | } |
| 935 | if (ci->ci_signature != 0) |
| 936 | aprint_normal(", id 0x%x" , ci->ci_signature); |
| 937 | aprint_normal("\n" ); |
| 938 | |
| 939 | if (cpu_brand_string[0] == '\0') { |
| 940 | strlcpy(cpu_brand_string, cpu_getmodel(), |
| 941 | sizeof(cpu_brand_string)); |
| 942 | } |
| 943 | if (cpu_class == CPUCLASS_386) { |
| 944 | panic("NetBSD requires an 80486DX or later processor" ); |
| 945 | } |
| 946 | if (cputype == CPU_486DLC) { |
| 947 | aprint_error("WARNING: BUGGY CYRIX CACHE\n" ); |
| 948 | } |
| 949 | |
| 950 | #if !defined(XEN) || defined(DOM0OPS) /* on Xen rdmsr is for Dom0 only */ |
| 951 | if (cpu_vendor == CPUVENDOR_AMD /* check enablement of an */ |
| 952 | && device_unit(ci->ci_dev) == 0 /* AMD feature only once */ |
| 953 | && ((cpu_feature[3] & CPUID_SVM) == CPUID_SVM)) { |
| 954 | uint64_t val; |
| 955 | |
| 956 | val = rdmsr(MSR_VMCR); |
| 957 | if (((val & VMCR_SVMED) == VMCR_SVMED) |
| 958 | && ((val & VMCR_LOCK) == VMCR_LOCK)) { |
| 959 | aprint_normal_dev(ci->ci_dev, |
| 960 | "SVM disabled by the BIOS\n" ); |
| 961 | } |
| 962 | } |
| 963 | #endif |
| 964 | |
| 965 | #ifdef i386 |
| 966 | if (i386_fpu_present == 0) |
| 967 | aprint_normal_dev(ci->ci_dev, "no fpu\n" ); |
| 968 | |
| 969 | if (i386_fpu_fdivbug == 1) |
| 970 | aprint_normal_dev(ci->ci_dev, |
| 971 | "WARNING: Pentium FDIV bug detected!\n" ); |
| 972 | |
| 973 | if (cpu_vendor == CPUVENDOR_TRANSMETA) { |
| 974 | u_int descs[4]; |
| 975 | x86_cpuid(0x80860000, descs); |
| 976 | if (descs[0] >= 0x80860007) |
| 977 | /* Create longrun sysctls */ |
| 978 | tmx86_init_longrun(); |
| 979 | } |
| 980 | #endif /* i386 */ |
| 981 | |
| 982 | } |
| 983 | |