1 /* 2 * Copyright 2004-2012, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2002, Carlos Hasan. 4 * 5 * Distributed under the terms of the MIT license. 6 */ 7 8 9 #include <OS.h> 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <cpu_type.h> 16 17 18 // TODO: -disable_cpu_sn option is not yet implemented 19 // TODO: most of this file should go into an architecture dependent source file 20 #if defined(__i386__) || defined(__x86_64__) 21 22 struct cache_description { 23 uint8 code; 24 const char *description; 25 } static sIntelCacheDescriptions[] = { 26 {0x01, "Instruction TLB: 4k-byte pages, 4-way set associative, 32 entries"}, 27 {0x02, "Instruction TLB: 4M-byte pages, fully associative, 2 entries"}, 28 {0x03, "Data TLB: 4k-byte pages, 4-way set associative, 64 entries"}, 29 {0x04, "Data TLB: 4M-byte pages, 4-way set associative, 8 entries"}, 30 {0x05, "Data TLB: 4M-byte pages, 4-way set associative, 32 entries"}, 31 {0x06, "L1 inst cache: 8 KB, 4-way set associative, 32 bytes/line"}, 32 {0x08, "L1 inst cache: 16 KB, 4-way set associative, 32 bytes/line"}, 33 {0x09, "L1 inst cache: 43 KB, 4-way set associative, 32 bytes/line"}, 34 {0x0A, "L1 data cache: 8 KB, 2-way set associative, 32 bytes/line"}, 35 {0x0B, "Code TLB: 4M-byte pages, 4-way set associative, 4 entries"}, 36 {0x0C, "L1 data cache: 16 KB, 4-way set associative, 32 bytes/line"}, 37 {0x0D, "L1 data cache: 16 KB, 4-way set associative, 64-bytes/line, ECC"}, 38 {0x0E, "L1 data cache, 24 KB, 6-way set associative, 64-bytes/line"}, 39 {0x10, /* IA-64 */ "L1 data cache: 16 KB, 4-way set associative, 32 bytes/line"}, 40 {0x15, /* IA-64 */ "L1 inst cache: 16 KB, 4-way set associative, 32 bytes/line"}, 41 {0x1A, /* IA-64 */ "L2 cache: 96 KB, 6-way set associative, 64 bytes/line"}, 42 {0x1D, "L2 cache: 128 KB, 2-way set associative, 64 bytes/line"}, 43 {0x21, "L2 cache: 256 KB (MLC), 8-way set associative, 64-bytes/line"}, 44 {0x22, "L3 cache: 512 KB, 4-way set associative (!), 64 bytes/line, dual-sectored"}, 45 {0x23, "L3 cache: 1 MB, 8-way set associative, 64 bytes/line, dual-sectored"}, 46 {0x24, "L2 cache: 1 MB, 8-way set associative, 64 bytes/line"}, 47 {0x25, "L3 cache: 2 MB, 8-way set associative, 64 bytes/line, dual-sectored"}, 48 {0x29, "L3 cache: 4 MB, 8-way set associative, 64 bytes/line, dual-sectored"}, 49 {0x2c, "L1 data cache: 32 KB, 8-way set associative, 64 bytes/line"}, 50 {0x30, "L1 inst cache: 32 KB, 8-way set associative, 64 bytes/line"}, 51 {0x39, "L2 cache: 128 KB, 4-way set associative, 64 bytes/line, sectored"}, 52 {0x3A, "L2 cache: 192 KB, 4-way set associative, 64 bytes/line, sectored"}, 53 {0x3B, "L2 cache: 128 KB, 2-way set associative, 64 bytes/line, sectored"}, 54 {0x3C, "L2 cache: 256 KB, 4-way set associative, 64 bytes/line, sectored"}, 55 {0x3D, "L2 cache: 384 KB, 6-way set associative, 64 bytes/line, sectored"}, 56 {0x3E, "L2 cache: 512 KB, 4-way set associative, 64 bytes/line, sectored"}, 57 {0x40, NULL /*"No integrated L2 cache (P6 core) or L3 cache (P4 core)"*/}, 58 // this one is separately handled 59 {0x41, "L2 cache: 128 KB, 4-way set associative, 32 bytes/line"}, 60 {0x42, "L2 cache: 256 KB, 4-way set associative, 32 bytes/line"}, 61 {0x43, "L2 cache: 512 KB, 4-way set associative, 32 bytes/line"}, 62 {0x44, "L2 cache: 1024 KB, 4-way set associative, 32 bytes/line"}, 63 {0x45, "L2 cache: 2048 KB, 4-way set associative, 32 bytes/line"}, 64 {0x46, "L3 cache: 4096 KB, 4-way set associative, 64 bytes/line"}, 65 {0x47, "L3 cache: 8192 KB, 8-way set associative, 64 bytes/line"}, 66 {0x48, "L2 cache: 3072 KB, 12-way set associative, 64 bytes/line, unified on-die"}, 67 // 0x49 requires special handling, either 4M L3 (Xeon MP, 0F06; otherwise 4M L2 68 {0x4A, "L3 cache: 6144 KB, 12-way set associative, 64 bytes/line"}, 69 {0x4B, "L3 cache: 8192 KB, 16-way set associative, 64 bytes/line"}, 70 {0x4C, "L3 cache: 12288 KB, 12-way set associative, 64 bytes/line"}, 71 {0x4D, "L3 cache: 16384 KB, 16-way set associative, 64 bytes/line"}, 72 {0x4E, "L2 cache: 6144 KB, 24-way set associative, 64 bytes/line"}, 73 {0x4F, "Inst TLB, 4K-bytes pages, 32 entries"}, 74 {0x50, "Inst TLB: 4K/4M/2M-bytes pages, fully associative, 64 entries"}, 75 {0x51, "Inst TLB: 4K/4M/2M-bytes pages, fully associative, 128 entries"}, 76 {0x52, "Inst TLB: 4K/4M/2M-bytes pages, fully associative, 256 entries"}, 77 {0x55, "Inst TLB: 2M/4M-bytes pages, fully associative, 7 entries"}, 78 {0x56, "L1 Data TLB: 4M-bytes pages, 4-way set associative, 16 entries"}, 79 {0x57, "L1 Data TLB: 4K-bytes pages, 4-way set associative, 16 entries"}, 80 {0x59, "L0 Data TLB: 4K-bytes pages, fully associative, 16 entries"}, 81 {0x5A, "L0 Data TLB: 2M/4M-bytes pages, 4-way set associative, 32 entries"}, 82 {0x5B, "Data TLB: 4K/4M-bytes pages, fully associative, 64 entries"}, 83 {0x5C, "Data TLB: 4K/4M-bytes pages, fully associative, 128 entries"}, 84 {0x5D, "Data TLB: 4K/4M-bytes pages, fully associative, 256 entries"}, 85 {0x60, "L1 data cache: 16 KB, 8-way set associative, 64 bytes/line, sectored"}, 86 {0x61, "Code TLB: 4K pages, fully associative, 48 entries"}, 87 {0x63, "Data TLB: 2M/4M-bytes pages, 4-way set associative, 32 entries"}, 88 {0x64, "Data TLB: 4K pages, 4-way set associative, 512 entries"}, 89 {0x66, "L1 data cache: 8 KB, 4-way set associative, 64 bytes/line, sectored"}, 90 {0x67, "L1 data cache: 16 KB, 4-way set associative, 64 bytes/line, sectored"}, 91 {0x68, "L1 data cache: 32 KB, 4-way set associative, 64 bytes/line, sectored"}, 92 {0x6A, "L0 Data TLB: 4K pages, 8-way set associative, 64 entries"}, 93 {0x6B, "Data TLB: 4K pages, 8-way set associative, 256 entries"}, 94 {0x6C, "Data TLB: 2M/4M pages, 8-way set associative, 128 entries"}, 95 {0x6D, "Data TLB: 1G pages, fully associative, 16 entries"}, 96 // {0x70, "Cyrix specific: Code and data TLB: 4k-bytes pages, 4-way set associative, 32 entries"}, 97 {0x70, "Inst trace cache: 12K µOPs, 8-way set associative"}, 98 {0x71, "Inst trace cache: 16K µOPs, 8-way set associative"}, 99 {0x72, "Inst trace cache: 32K µOPs, 8-way set associative"}, 100 {0x73, "Inst trace cache: 64K µOPs, 8-way set associative"}, 101 // {0x74, "Cyrix specific: ???"}, 102 {0x76, "Code TLB: 2M/4M pages, fully associative, 8 entries"}, 103 {0x77, /* IA-64 */ "L1 inst cache: 16 KB, 4-way set associative, 64 bytes/line, sectored"}, 104 // {0x77, "Cyrix specific: ???"}, 105 {0x78, "L2 cache: 1024 KB, 4-way set associative, 64 bytes/line"}, 106 {0x79, "L2 cache: 128 KB, 8-way set associative, 64 bytes/line, dual-sectored"}, 107 {0x7A, "L2 cache: 256 KB, 8-way set associative, 64 bytes/line, dual-sectored"}, 108 {0x7B, "L2 cache: 512 KB, 8-way set associative, 64 bytes/line, dual-sectored"}, 109 {0x7C, "L2 cache: 1024 KB, 8-way set associative, 64 bytes/line, dual-sectored"}, 110 {0x7D, "L2 cache: 2048 KB, 8-way set associative, 64 bytes/line"}, 111 {0x7E, /* IA-64 */ "L2 cache: 256 KB, 8-way set associative, 128 bytes/line, sectored"}, 112 {0x7F, "L2 cache: 512 KB, 2-way set associative, 64 bytes/line"}, 113 {0x80, /* Cyrix specific */ "L1 cache: 16 KB, 4-way set associative, 16 bytes/line"}, 114 {0x81, "L2 cache: 128 KB, 8-way set associative, 32 bytes/line"}, 115 {0x82, "L2 cache: 256 KB, 8-way set associative, 32 bytes/line"}, 116 // {0x82, "Cyrix specific: ???"}, 117 {0x83, "L2 cache: 512 KB, 8-way set associative, 32 bytes/line"}, 118 {0x84, "L2 cache: 1024 KB, 8-way set associative, 32 bytes/line"}, 119 // {0x84, "Cyrix specific: ???"}, 120 {0x85, "L2 cache: 2048 KB, 8-way set associative, 32 bytes/line"}, 121 {0x86, "L2 cache: 512 KB, 4-way set associative, 64 bytes/line"}, 122 {0x87, "L2 cache: 1024 KB, 8-way set associative, 64 bytes/line"}, 123 {0x88, /* IA-64 */ "L3 cache: 2 MB, 4-way set associative, 64 bytes/line"}, 124 {0x89, /* IA-64 */ "L3 cache: 4 MB, 4-way set associative, 64 bytes/line"}, 125 {0x8A, /* IA-64 */ "L3 cache: 8 MB, 4-way set associative, 64 bytes/line"}, 126 {0x8D, /* IA-64 */ "L3 cache: 3 MB, 12-way set associative, 128 bytes/line"}, 127 {0x90, /* IA-64 */ "Inst TLB: 4K-256Mbytes pages, fully associative, 64 entries"}, 128 {0x96, /* IA-64 */ "L1 data TLB: 4K-256M bytes pages, fully associative, 32 entries"}, 129 {0x9B, /* IA-64 */ "L2 data TLB: 4K-256M bytes pages, fully associative, 96 entries"}, 130 {0xA0, "Data TLB: 4K-bytes pages, fully associative, 32 entries"}, 131 {0xB0, "Inst TLB: 4K-bytes pages, 4-way set associative, 128 entries"}, 132 {0xB1, "Inst TLB: 2M-bytes pages, 4-way set associative, 8 entries OR 4M, 4-way, 4 entries"}, 133 // Intel doesn't give any details how to determine which of the two options is the case 134 // as per Intel Application Note 485, November 2008. 135 {0xB2, "Inst TLB: 4K-bytes pages, 4-way set associative, 64 entries"}, 136 {0xB3, "Data TLB: 4K-bytes pages, 4-way set associative, 128 entries"}, 137 {0xB4, "Data TLB: 4K-bytes pages, 4-way set associative, 256 entries"}, 138 {0xB5, "Code TLB: 4K-bytes pages, 8-way set associative, 64 entries"}, 139 {0xB6, "Code TLB: 4K-bytes pages, 8-way set associative, 128 entries"}, 140 {0xBA, "Data TLB, 4K-bytes pages, 4-way set associative, 64 entries"}, 141 {0xC0, "Data TLB, 4K-4M bytes pages, 4-way set associative, 8 entries"}, 142 {0xC1, "L2 cache: 4K/2M bytes pages, 8-way set associative, 1024 entries"}, 143 {0xC2, "Data TLB, 2M/4M bytes pages, 4-way set associative, 16 entries"}, 144 {0xC3, "Shared 2nd-level TLB: 4K/2M, 6-way set associative, 1536 entries"}, 145 {0xC4, "Data TLB, 2M/4M bytes pages, 4-way set associative, 32 entries"}, 146 {0xCA, "Shared 2nd-level TLB: 4K, 4-way set associative, 512 entries"}, 147 {0xD0, "L3 cache: 512 KB, 4-way set associative, 64-bytes/line"}, 148 {0xD1, "L3 cache: 1024 KB, 4-way set associative, 64-bytes/line"}, 149 {0xD2, "L3 cache: 2048 KB, 4-way set associative, 64-bytes/line"}, 150 {0xD6, "L3 cache: 1024 KB, 8-way set associative, 64-bytes/line"}, 151 {0xD7, "L3 cache: 2048 KB, 8-way set associative, 64-bytes/line"}, 152 {0xD8, "L3 cache: 4096 KB, 8-way set associative, 64-bytes/line"}, 153 {0xDC, "L3 cache: 2048 KB, 12-way set associative, 64-bytes/line"}, 154 {0xDD, "L3 cache: 4096 KB, 12-way set associative, 64-bytes/line"}, 155 {0xDE, "L3 cache: 8192 KB, 12-way set associative, 64-bytes/line"}, 156 {0xE2, "L3 cache: 2048 KB, 16-way set associative, 64-bytes/line"}, 157 {0xE3, "L3 cache: 4096 KB, 16-way set associative, 64-bytes/line"}, 158 {0xE4, "L3 cache: 8192 KB, 16-way set associative, 64-bytes/line"}, 159 {0xEA, "L3 cache: 12288 KB, 24-way set associative, 64-bytes/line"}, 160 {0xEB, "L3 cache: 18432 KB, 24-way set associative, 64-bytes/line"}, 161 {0xEC, "L3 cache: 24576 KB, 24-way set associative, 64-bytes/line"}, 162 {0xF0, "64-byte Prefetching"}, 163 {0xF1, "128-byte Prefetching"}, 164 {0xFF, NULL}, // print_intel_cache_desc() will query level 0000_0004h 165 {0, NULL} 166 }; 167 168 169 /* CPU Features */ 170 static const char *kFeatures[32] = { 171 "FPU", "VME", "DE", "PSE", 172 "TSC", "MSR", "PAE", "MCE", 173 "CX8", "APIC", NULL, "SEP", 174 "MTRR", "PGE", "MCA", "CMOV", 175 "PAT", "PSE36", "PSN", "CFLUSH", 176 NULL, "DS", "ACPI", "MMX", 177 "FXSTR", "SSE", "SSE2", "SS", 178 "HTT", "TM", "IA64", "PBE", 179 }; 180 181 /* CPU Extended features */ 182 static const char *kExtendedFeatures[32] = { 183 "SSE3", "PCLMULDQ", "DTES64", "MONITOR", "DS-CPL", "VMX", "SMX", "EST", 184 "TM2", "SSSE3", "CNTXT-ID", "SDBG", "FMA", "CX16", "xTPR", "PDCM", 185 NULL, "PCID", "DCA", "SSE4.1", "SSE4.2", "x2APIC", "MOVEB", "POPCNT", 186 "TSC-DEADLINE", "AES", "XSAVE", "OSXSAVE", "AVX", "F16C", "RDRND", 187 "HYPERVISOR" 188 }; 189 190 191 /* AMD Extended features leaf 0x80000001 */ 192 static const char *kAMDExtFeatures[32] = { 193 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 194 NULL, NULL, NULL, "SCE", NULL, NULL, NULL, NULL, 195 NULL, NULL, NULL, "MP", "NX", NULL, "AMD-MMX", NULL, 196 "FXSR", "FFXSR", "GBPAGES", "RDTSCP", NULL, "64", "3DNow+", "3DNow!" 197 }; 198 199 200 /* AMD Extended features leaf 0x80000007 */ 201 static const char *kAMDExtFeaturesPower[32] = { 202 "TS", "FID", "VID", "TTP", "TM", "STC", "MUL100", "HWPS", 203 "ITSC", "CPB", "EFRO", "PFI", "PA", NULL, NULL, NULL, 204 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 205 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 206 }; 207 208 209 /* AMD Extended features leaf 0x80000008 */ 210 static const char *kAMDExtFeaturesTwo[32] = { 211 "CLZERO", "IRPERF", "XSAVEPTR", NULL, NULL, NULL, NULL, NULL, 212 NULL, NULL, NULL, NULL, "AMD_IBPB", NULL, "AMD_IBRS", "AMD_STIBP", 213 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 214 "AMD_SSBD", "VIRT_SSBD", "AMD_SSB_NO", NULL, NULL, NULL, NULL, NULL 215 }; 216 217 218 static void 219 print_intel_cache_descriptors(enum cpu_vendor vendor, uint32 model, 220 cpuid_info *info) 221 { 222 uint8 cacheDescriptors[15]; // Max 223 224 int maxDesc = 0; 225 int i = 0; 226 227 // put valid values into array 228 if ((info->regs.eax & 0x80000000) == 0) { 229 // eax is valid, include values 230 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 231 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 232 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 233 } else { 234 i += 3; 235 } 236 if ((info->regs.ebx & 0x80000000) == 0) { 237 // ebx is valid, include values 238 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 239 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 240 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 241 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 242 } else { 243 i += 4; 244 } 245 if ((info->regs.edx & 0x80000000) == 0) { 246 // edx is valid, include values 247 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 248 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 249 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 250 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 251 } else { 252 i += 4; 253 } 254 if ((info->regs.ecx & 0x80000000) == 0) { 255 // ecx is valid, include values 256 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 257 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 258 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 259 cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++]; 260 } 261 262 putchar('\n'); 263 264 for (int i = 0; i < maxDesc; i++) { 265 // ignore NULL descriptors 266 if (cacheDescriptors[i] == 0) 267 continue; 268 269 int j; 270 for (j = 0; sIntelCacheDescriptions[j].code; j++) { 271 if (cacheDescriptors[i] == sIntelCacheDescriptions[j].code) { 272 if (cacheDescriptors[i] == 0x40) { 273 printf("\tNo integrated L%u cache\n", 274 ((model >> 8) & 0xf) == 0xf 275 && vendor == B_CPU_VENDOR_INTEL ? 3 : 2); 276 } else if (cacheDescriptors[i] == 0xff) { 277 break; 278 } else 279 printf("\t%s\n", sIntelCacheDescriptions[j].description); 280 break; 281 } 282 } 283 284 // Reached the end without finding a descriptor 285 if (sIntelCacheDescriptions[j].code == 0) 286 printf("\tUnknown cache descriptor 0x%02x\n", cacheDescriptors[i]); 287 } 288 } 289 290 291 #endif // __i386__ || __x86_64__ 292 293 294 static void 295 print_TLB(uint32 reg, const char *pages) 296 { 297 int entries[2]; 298 int ways[2]; 299 const char *name[2] = { "Inst TLB", "Data TLB" }; 300 301 entries[0] = (reg & 0xff); 302 ways[0] = ((reg >> 8) & 0xff); 303 entries[1] = ((reg >> 16) & 0xff); 304 ways[1] = ((reg >> 24) & 0xff); 305 306 for (int num = 0; num < 2; num++) { 307 printf("\t%s: %s%s%u entries, ", name[num], 308 pages ? pages : "", pages ? " pages, " : "", entries[num]); 309 310 if (ways[num] == 0xff) 311 printf("fully associative\n"); 312 else 313 printf("%u-way set associative\n", ways[num]); 314 } 315 } 316 317 318 static void 319 print_level2_cache(uint32 reg, const char *name) 320 { 321 uint32 size = (reg >> 16) & 0xffff; 322 uint32 ways = (reg >> 12) & 0xf; 323 uint32 linesPerTag = (reg >> 8) & 0xf; 324 // intel does not define this 325 uint32 lineSize = reg & 0xff; 326 327 printf("\t%s: %" B_PRIu32 " KB, ", name, size); 328 if (ways == 0xf) 329 printf("fully associative, "); 330 else if (ways == 0x1) 331 printf("direct-mapped, "); 332 else 333 printf("%lu-way set associative, ", 1UL << (ways / 2)); 334 printf("%" B_PRIu32 " lines/tag, %" B_PRIu32 " bytes/line\n", linesPerTag, 335 lineSize); 336 } 337 338 339 static void 340 print_level1_cache(uint32 reg, const char *name) 341 { 342 uint32 size = (reg >> 24) & 0xff; 343 uint32 ways = (reg >> 16) & 0xff; 344 uint32 linesPerTag = (reg >> 8) & 0xff; 345 uint32 lineSize = reg & 0xff; 346 347 printf("\t%s: %" B_PRIu32 " KB, ", name, size); 348 if (ways == 0xff) 349 printf("fully associative, "); 350 else 351 printf("%" B_PRIu32 "-way set associative, ", ways); 352 printf("%" B_PRIu32 " lines/tag, %" B_PRIu32 " bytes/line\n", linesPerTag, 353 lineSize); 354 } 355 356 357 #if defined(__i386__) || defined(__x86_64__) 358 359 static void 360 print_cache_desc(int32 cpu) 361 { 362 cpuid_info info; 363 get_cpuid(&info, 0x80000005, cpu); 364 365 putchar('\n'); 366 367 if (info.regs.eax) 368 print_TLB(info.regs.eax, info.regs.ebx ? "2M/4M-byte" : NULL); 369 if (info.regs.ebx) 370 print_TLB(info.regs.ebx, info.regs.eax ? "4K-byte" : NULL); 371 372 print_level1_cache(info.regs.ecx, "L1 inst cache"); 373 print_level1_cache(info.regs.edx, "L1 data cache"); 374 375 get_cpuid(&info, 0x80000006, cpu); 376 print_level2_cache(info.regs.ecx, "L2 cache"); 377 } 378 379 380 static void 381 print_intel_cache_desc(int32 cpu) 382 { 383 cpuid_info info; 384 385 // A second parameters needs to be passed to CPUID which determines the 386 // cache level to query 387 get_cpuid(&info, 0x00000004, cpu); 388 389 putchar('\n'); 390 391 uint32 type = info.regs.eax & 0xf; 392 uint32 level = (info.regs.eax & 0x70) >> 4; 393 bool isFullyAssoc = info.regs.eax & 0x100; 394 395 uint32 lineSize = (info.regs.ebx & 0xfff) + 1; 396 uint32 linesPerTag = ((info.regs.ebx & 0x3ff000) >> 12) + 1; 397 uint32 ways = ((info.regs.ebx & 0xffc00000) >> 22) + 1; 398 399 printf("\tL%" B_PRId32 " ",level); 400 401 switch (type) { 402 case 1: printf("Data cache "); break; 403 case 2: printf("Inst cache "); break; 404 case 3: printf("Unified cache "); break; 405 default: break; 406 } 407 408 if (isFullyAssoc) 409 printf("fully associative, "); 410 else 411 printf("%" B_PRIu32 "-way set associative, ", ways); 412 printf("%" B_PRIu32 " lines/tag, %" B_PRIu32 " bytes/line\n", linesPerTag, 413 lineSize); 414 415 get_cpuid(&info, 0x80000006, cpu); 416 uint32 sets = info.regs.ecx; 417 print_level2_cache(sets, "L2 cache"); 418 } 419 420 421 static void 422 print_transmeta_features(uint32 features) 423 { 424 if (features & (1 << 16)) 425 printf("\t\tFCMOV\n"); 426 } 427 428 #endif // __i386__ || __x86_64__ 429 430 431 static void 432 print_features(const char** table, uint32 features) 433 { 434 int32 found = 0; 435 436 for (int32 i = 0; i < 32; i++) { 437 if ((features & (1UL << i)) && table[i] != NULL) { 438 printf("%s%s", found == 0 ? "\t\t" : " ", table[i]); 439 found++; 440 if (found > 0 && (found % 16) == 0) { 441 putchar('\n'); 442 found = 0; 443 } 444 } 445 } 446 447 if (found != 0) 448 putchar('\n'); 449 } 450 451 452 #if defined(__i386__) || defined(__x86_64__) 453 454 static void 455 print_processor_signature(enum cpu_vendor vendor, cpuid_info *info) 456 { 457 printf("\tSignature: 0x%1" B_PRIx32 "%1" B_PRIx32 "0%1" B_PRIx32 458 "%1" B_PRIx32 "%1" B_PRIx32 "; ", info->eax_1.extended_family, 459 info->eax_1.extended_model, info->eax_1.family, 460 info->eax_1.model, info->eax_1.stepping); 461 if (vendor == B_CPU_VENDOR_AMD) { 462 printf("Type %" B_PRIu32 ", family %" B_PRIu32 ", model %" B_PRIu32 463 ", stepping %" B_PRIu32 "\n", 464 info->eax_1.type, 465 info->eax_1.family + (info->eax_1.family == 0xf 466 ? info->eax_1.extended_family : 0), 467 info->eax_1.model + (info->eax_1.model == 0xf 468 ? info->eax_1.extended_model << 4 : 0), 469 info->eax_1.stepping); 470 } else if (vendor == B_CPU_VENDOR_INTEL) { 471 // model calculation is different for INTEL 472 printf("Type %" B_PRIu32 ", family %" B_PRIu32 ", model %" B_PRIu32 473 ", stepping %" B_PRIu32 "\n", 474 info->eax_1.type, 475 info->eax_1.family + (info->eax_1.family == 0xf 476 ? info->eax_1.extended_family : 0), 477 info->eax_1.model 478 + ((info->eax_1.family == 0xf || info->eax_1.family == 0x6) 479 ? info->eax_1.extended_model << 4 : 0), 480 info->eax_1.stepping); 481 } 482 } 483 484 #endif // __i386__ || __x86_64__ 485 486 487 static void 488 dump_platform(system_info *info) 489 { 490 cpu_topology_node_info root; 491 uint32 count = 1; 492 get_cpu_topology_info(&root, &count); 493 494 const char* platform; 495 switch (root.data.root.platform) { 496 case B_CPU_x86: 497 platform = "IntelArchitecture"; 498 break; 499 500 case B_CPU_x86_64: 501 platform = "IntelArchitecture (64 bit)"; 502 break; 503 504 default: 505 platform = "unknown"; 506 break; 507 } 508 509 printf("%s\n", platform); 510 } 511 512 513 #if defined(__i386__) || defined(__x86_64__) 514 515 static void 516 dump_cpu(enum cpu_vendor vendor, uint32 model, int32 cpu) 517 { 518 // References: 519 // http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html 520 // http://www.sandpile.org/ia32/cpuid.htm 521 // http://www.sandpile.org/x86/cpuid.htm 522 // http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/TN13.pdf (Duron erratum) 523 524 cpuid_info baseInfo; 525 if (get_cpuid(&baseInfo, 0, cpu) != B_OK) { 526 // this CPU doesn't support cpuid 527 return; 528 } 529 530 int32 maxStandardFunction = baseInfo.eax_0.max_eax; 531 if (maxStandardFunction >= 500) { 532 // old Pentium sample chips has cpu signature here 533 maxStandardFunction = 0; 534 } 535 536 // Extended cpuid 537 538 cpuid_info cpuInfo; 539 get_cpuid(&cpuInfo, 0x80000000, cpu); 540 541 // Extended cpuid is only supported if max_eax is greater than the 542 // service id 543 int32 maxExtendedFunction = 0; 544 if (cpuInfo.eax_0.max_eax > 0x80000000) 545 maxExtendedFunction = cpuInfo.eax_0.max_eax & 0xff; 546 547 if (maxExtendedFunction >=4 ) { 548 char buffer[49]; 549 char *name = buffer; 550 551 memset(buffer, 0, sizeof(buffer)); 552 553 for (int32 i = 0; i < 3; i++) { 554 cpuid_info nameInfo; 555 get_cpuid(&nameInfo, 0x80000002 + i, cpu); 556 557 memcpy(name, &nameInfo.regs.eax, 4); 558 memcpy(name + 4, &nameInfo.regs.ebx, 4); 559 memcpy(name + 8, &nameInfo.regs.ecx, 4); 560 memcpy(name + 12, &nameInfo.regs.edx, 4); 561 name += 16; 562 } 563 564 // cut off leading spaces (names are right aligned) 565 name = buffer; 566 while (name[0] == ' ') 567 name++; 568 569 // the BIOS may not have set the processor name 570 if (name[0]) 571 printf("CPU #%" B_PRId32 ": \"%s\"\n", cpu, name); 572 else { 573 // Intel CPUs don't seem to have the genuine vendor field 574 printf("CPU #%" B_PRId32 ": %.12s\n", cpu, 575 vendor == B_CPU_VENDOR_INTEL ? 576 baseInfo.eax_0.vendor_id : cpuInfo.eax_0.vendor_id); 577 } 578 } else { 579 printf("CPU #%" B_PRId32 ": %.12s\n", cpu, baseInfo.eax_0.vendor_id); 580 if (maxStandardFunction == 0) 581 return; 582 } 583 584 get_cpuid(&cpuInfo, 1, cpu); 585 print_processor_signature(vendor, &cpuInfo); 586 printf("\tFeatures: 0x%08" B_PRIx32 "\n", cpuInfo.eax_1.features); 587 print_features(kFeatures, cpuInfo.eax_1.features); 588 589 if (maxStandardFunction >= 1) { 590 /* Extended features */ 591 printf("\tExtended Features (0x00000001): 0x%08" B_PRIx32 "\n", 592 cpuInfo.eax_1.extended_features); 593 print_features(kExtendedFeatures, cpuInfo.eax_1.extended_features); 594 } 595 596 /* Extended CPUID Information */ 597 if (maxExtendedFunction >= 1) { 598 get_cpuid(&cpuInfo, 0x80000001, cpu); 599 if (vendor == B_CPU_VENDOR_INTEL || vendor == B_CPU_VENDOR_AMD) { 600 // 0x80000001 EDX has overlap of 64,ED,SY/SE between amd and intel 601 printf("\tExtended Features (0x80000001): 0x%08" B_PRIx32 "\n", 602 cpuInfo.eax_1.features); 603 print_features(kAMDExtFeatures, cpuInfo.regs.edx); 604 } 605 606 if (vendor == B_CPU_VENDOR_AMD) { 607 if (maxExtendedFunction >= 7) { 608 get_cpuid(&cpuInfo, 0x80000007, cpu); 609 printf("\tExtended Features (0x80000007): 0x%08" B_PRIx32 "\n", 610 cpuInfo.regs.edx); 611 print_features(kAMDExtFeaturesPower, cpuInfo.regs.edx); 612 } 613 if (maxExtendedFunction >= 8) { 614 get_cpuid(&cpuInfo, 0x80000008, cpu); 615 printf("\tExtended Features (0x80000008): 0x%08" B_PRIx32 "\n", 616 cpuInfo.regs.ebx); 617 print_features(kAMDExtFeaturesTwo, cpuInfo.regs.ebx); 618 } 619 } else if (vendor == B_CPU_VENDOR_TRANSMETA) 620 print_transmeta_features(cpuInfo.regs.edx); 621 } 622 623 /* Cache/TLB descriptors */ 624 if (maxExtendedFunction >= 5) { 625 if (!strncmp(baseInfo.eax_0.vendor_id, "CyrixInstead", 12)) { 626 get_cpuid(&cpuInfo, 0x00000002, cpu); 627 print_intel_cache_descriptors(vendor, model, &cpuInfo); 628 } else if (vendor == B_CPU_VENDOR_INTEL) { 629 // Intel does not support extended function 5 (but it does 6 hmm) 630 print_intel_cache_desc(cpu); 631 } else { 632 print_cache_desc(cpu); 633 } 634 } 635 636 if (maxStandardFunction >= 2) { 637 do { 638 get_cpuid(&cpuInfo, 2, cpu); 639 640 if (cpuInfo.eax_2.call_num > 0) 641 print_intel_cache_descriptors(vendor, model, &cpuInfo); 642 } while (cpuInfo.eax_2.call_num > 1); 643 } 644 645 /* Serial number */ 646 if (maxStandardFunction >= 3) { 647 cpuid_info flagsInfo; 648 get_cpuid(&flagsInfo, 1, cpu); 649 650 if (flagsInfo.eax_1.features & (1UL << 18)) { 651 get_cpuid(&cpuInfo, 3, cpu); 652 printf("Serial number: %04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32 653 "-%04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32 "\n", 654 flagsInfo.eax_1.features >> 16, 655 flagsInfo.eax_1.features & 0xffff, 656 cpuInfo.regs.edx >> 16, cpuInfo.regs.edx & 0xffff, 657 cpuInfo.regs.ecx >> 16, cpuInfo.regs.edx & 0xffff); 658 } 659 } 660 661 putchar('\n'); 662 } 663 664 #endif // __i386__ || __x86_64__ 665 666 667 static void 668 dump_cpus(system_info *info) 669 { 670 uint32 topologyNodeCount = 0; 671 cpu_topology_node_info* topology = NULL; 672 get_cpu_topology_info(NULL, &topologyNodeCount); 673 if (topologyNodeCount != 0) 674 topology = new cpu_topology_node_info[topologyNodeCount]; 675 get_cpu_topology_info(topology, &topologyNodeCount); 676 677 enum cpu_platform platform = B_CPU_UNKNOWN; 678 enum cpu_vendor cpuVendor = B_CPU_VENDOR_UNKNOWN; 679 uint32 cpuModel = 0; 680 uint64 frequency = 0; 681 for (uint32 i = 0; i < topologyNodeCount; i++) { 682 switch (topology[i].type) { 683 case B_TOPOLOGY_ROOT: 684 platform = topology[i].data.root.platform; 685 break; 686 687 case B_TOPOLOGY_PACKAGE: 688 cpuVendor = topology[i].data.package.vendor; 689 break; 690 691 case B_TOPOLOGY_CORE: 692 cpuModel = topology[i].data.core.model; 693 frequency = topology[i].data.core.default_frequency; 694 break; 695 696 default: 697 break; 698 } 699 } 700 delete[] topology; 701 702 const char *vendor = get_cpu_vendor_string(cpuVendor); 703 const char *model = get_cpu_model_string(platform, cpuVendor, cpuModel); 704 char modelString[32]; 705 706 if (model == NULL && vendor == NULL) 707 model = "(Unknown)"; 708 else if (model == NULL) { 709 model = modelString; 710 snprintf(modelString, 32, "(Unknown %" B_PRIx32 ")", cpuModel); 711 } 712 713 printf("%" B_PRId32 " %s%s%s, revision %04" B_PRIx32 " running at %" 714 B_PRIu64 "MHz\n\n", 715 info->cpu_count, 716 vendor ? vendor : "", vendor ? " " : "", model, 717 cpuModel, 718 frequency / 1000000); 719 720 #if defined(__i386__) || defined(__x86_64__) 721 for (uint32 cpu = 0; cpu < info->cpu_count; cpu++) 722 dump_cpu(cpuVendor, cpuModel, cpu); 723 #endif // __i386__ || __x86_64__ 724 } 725 726 727 static void 728 dump_mem(system_info *info) 729 { 730 printf("%10" B_PRIu64 " bytes free (used/max %10" B_PRIu64 " / %10" 731 B_PRIu64 ")\n", 732 B_PAGE_SIZE * (uint64)(info->max_pages - info->used_pages), 733 B_PAGE_SIZE * (uint64)info->used_pages, 734 B_PAGE_SIZE * (uint64)info->max_pages); 735 printf(" (cached %10" B_PRIu64 ")\n", 736 B_PAGE_SIZE * (uint64)info->cached_pages); 737 } 738 739 740 static void 741 dump_sem(system_info *info) 742 { 743 printf("%10" B_PRId32 " semaphores free (used/max %10" B_PRId32 " / %10" 744 B_PRId32 ")\n", 745 info->max_sems - info->used_sems, 746 info->used_sems, info->max_sems); 747 } 748 749 750 static void 751 dump_ports(system_info *info) 752 { 753 printf("%10" B_PRId32 " ports free (used/max %10" B_PRId32 " / %10" 754 B_PRId32 ")\n", 755 info->max_ports - info->used_ports, 756 info->used_ports, info->max_ports); 757 } 758 759 760 static void 761 dump_thread(system_info *info) 762 { 763 printf("%10" B_PRId32 " threads free (used/max %10" B_PRId32 " / %10" 764 B_PRId32 ")\n", 765 info->max_threads - info->used_threads, 766 info->used_threads, info->max_threads); 767 } 768 769 770 static void 771 dump_team(system_info *info) 772 { 773 printf("%10" B_PRId32 " teams free (used/max %10" B_PRId32 " / %10" 774 B_PRId32 ")\n", 775 info->max_teams - info->used_teams, 776 info->used_teams, info->max_teams); 777 } 778 779 780 static void 781 dump_kinfo(system_info *info) 782 { 783 printf("Kernel name: %s built on: %s %s version 0x%" B_PRIx64 "\n", 784 info->kernel_name, 785 info->kernel_build_date, info->kernel_build_time, 786 info->kernel_version ); 787 } 788 789 790 static void 791 dump_system_info(system_info *info) 792 { 793 dump_kinfo(info); 794 dump_cpus(info); 795 dump_mem(info); 796 dump_sem(info); 797 dump_ports(info); 798 dump_thread(info); 799 dump_team(info); 800 } 801 802 803 int 804 main(int argc, char *argv[]) 805 { 806 if (!is_computer_on()) { 807 printf("The computer is not on! No info available\n"); 808 exit(EXIT_FAILURE); 809 } 810 811 system_info info; 812 if (get_system_info(&info) != B_OK) { 813 printf("Error getting system information!\n"); 814 return 1; 815 } 816 817 if (argc <= 1) { 818 dump_system_info(&info); 819 } else { 820 for (int i = 1; i < argc; i++) { 821 const char *opt = argv[i]; 822 if (strncmp(opt, "-id", strlen(opt)) == 0) { 823 /* note: the original also assumes this option on "sysinfo -" */ 824 printf("%#.8x %#.8x\n", 0,0); 825 } else if (strncmp(opt, "-cpu", strlen(opt)) == 0) { 826 dump_cpus(&info); 827 } else if (strncmp(opt, "-mem", strlen(opt)) == 0) { 828 dump_mem(&info); 829 } else if (strncmp(opt, "-semaphores", strlen(opt)) == 0) { 830 dump_sem(&info); 831 } else if (strncmp(opt, "-ports", strlen(opt)) == 0) { 832 dump_ports(&info); 833 } else if (strncmp(opt, "-threads", strlen(opt)) == 0) { 834 dump_thread(&info); 835 } else if (strncmp(opt, "-teams", strlen(opt)) == 0) { 836 dump_team(&info); 837 } else if (strncmp(opt, "-kinfo", strlen(opt)) == 0) { 838 dump_kinfo(&info); 839 } else if (strncmp(opt, "-platform", strlen(opt)) == 0) { 840 dump_platform(&info); 841 } else if (strncmp(opt, "-disable_cpu_sn", strlen(opt)) == 0) { 842 /* TODO: printf("CPU #%d serial number: old state: %s, new state: %s\n", ... ); */ 843 fprintf(stderr, "Sorry, not yet implemented\n"); 844 } else { 845 const char *name = strrchr(argv[0], '/'); 846 if (name == NULL) 847 name = argv[0]; 848 else 849 name++; 850 851 fprintf(stderr, "Usage:\n"); 852 fprintf(stderr, " %s [-id|-cpu|-mem|-semaphore|-ports|-threads|-teams|-platform|-disable_cpu_sn|-kinfo]\n", name); 853 return 0; 854 } 855 } 856 } 857 return 0; 858 } 859