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 || vendor == B_CPU_VENDOR_HYGON) { 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 cpu_info frequencyInfo; 585 if (get_cpu_info(cpu, 1, &frequencyInfo) == B_OK 586 && frequencyInfo.current_frequency != 0) { 587 printf("\tFrequency: %" B_PRId32 ".%03" B_PRId32 "\n", 588 int32(frequencyInfo.current_frequency / 1000000), 589 int32((frequencyInfo.current_frequency % 1000000) / 1000)); 590 } 591 592 get_cpuid(&cpuInfo, 1, cpu); 593 print_processor_signature(vendor, &cpuInfo); 594 printf("\tFeatures: 0x%08" B_PRIx32 "\n", cpuInfo.eax_1.features); 595 print_features(kFeatures, cpuInfo.eax_1.features); 596 597 if (maxStandardFunction >= 1) { 598 /* Extended features */ 599 printf("\tExtended Features (0x00000001): 0x%08" B_PRIx32 "\n", 600 cpuInfo.eax_1.extended_features); 601 print_features(kExtendedFeatures, cpuInfo.eax_1.extended_features); 602 } 603 604 /* Extended CPUID Information */ 605 if (maxExtendedFunction >= 1) { 606 get_cpuid(&cpuInfo, 0x80000001, cpu); 607 if (vendor == B_CPU_VENDOR_INTEL || vendor == B_CPU_VENDOR_AMD 608 || vendor == B_CPU_VENDOR_HYGON) { 609 // 0x80000001 EDX has overlap of 64,ED,SY/SE between amd and intel 610 printf("\tExtended Features (0x80000001): 0x%08" B_PRIx32 "\n", 611 cpuInfo.eax_1.features); 612 print_features(kAMDExtFeatures, cpuInfo.regs.edx); 613 } 614 615 if (vendor == B_CPU_VENDOR_AMD || vendor == B_CPU_VENDOR_HYGON) { 616 if (maxExtendedFunction >= 7) { 617 get_cpuid(&cpuInfo, 0x80000007, cpu); 618 printf("\tExtended Features (0x80000007): 0x%08" B_PRIx32 "\n", 619 cpuInfo.regs.edx); 620 print_features(kAMDExtFeaturesPower, cpuInfo.regs.edx); 621 } 622 if (maxExtendedFunction >= 8) { 623 get_cpuid(&cpuInfo, 0x80000008, cpu); 624 printf("\tExtended Features (0x80000008): 0x%08" B_PRIx32 "\n", 625 cpuInfo.regs.ebx); 626 print_features(kAMDExtFeaturesTwo, cpuInfo.regs.ebx); 627 } 628 } else if (vendor == B_CPU_VENDOR_TRANSMETA) 629 print_transmeta_features(cpuInfo.regs.edx); 630 } 631 632 /* Cache/TLB descriptors */ 633 if (maxExtendedFunction >= 5) { 634 if (!strncmp(baseInfo.eax_0.vendor_id, "CyrixInstead", 12)) { 635 get_cpuid(&cpuInfo, 0x00000002, cpu); 636 print_intel_cache_descriptors(vendor, model, &cpuInfo); 637 } else if (vendor == B_CPU_VENDOR_INTEL) { 638 // Intel does not support extended function 5 (but it does 6 hmm) 639 print_intel_cache_desc(cpu); 640 } else { 641 print_cache_desc(cpu); 642 } 643 } 644 645 if (maxStandardFunction >= 2) { 646 do { 647 get_cpuid(&cpuInfo, 2, cpu); 648 649 if (cpuInfo.eax_2.call_num > 0) 650 print_intel_cache_descriptors(vendor, model, &cpuInfo); 651 } while (cpuInfo.eax_2.call_num > 1); 652 } 653 654 /* Serial number */ 655 if (maxStandardFunction >= 3) { 656 cpuid_info flagsInfo; 657 get_cpuid(&flagsInfo, 1, cpu); 658 659 if (flagsInfo.eax_1.features & (1UL << 18)) { 660 get_cpuid(&cpuInfo, 3, cpu); 661 printf("Serial number: %04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32 662 "-%04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32 "\n", 663 flagsInfo.eax_1.features >> 16, 664 flagsInfo.eax_1.features & 0xffff, 665 cpuInfo.regs.edx >> 16, cpuInfo.regs.edx & 0xffff, 666 cpuInfo.regs.ecx >> 16, cpuInfo.regs.edx & 0xffff); 667 } 668 } 669 670 putchar('\n'); 671 } 672 673 #endif // __i386__ || __x86_64__ 674 675 676 static void 677 dump_cpus(system_info *info) 678 { 679 uint32 topologyNodeCount = 0; 680 cpu_topology_node_info* topology = NULL; 681 get_cpu_topology_info(NULL, &topologyNodeCount); 682 if (topologyNodeCount != 0) 683 topology = new cpu_topology_node_info[topologyNodeCount]; 684 get_cpu_topology_info(topology, &topologyNodeCount); 685 686 enum cpu_platform platform = B_CPU_UNKNOWN; 687 enum cpu_vendor cpuVendor = B_CPU_VENDOR_UNKNOWN; 688 uint32 cpuModel = 0; 689 uint64 frequency = 0; 690 for (uint32 i = 0; i < topologyNodeCount; i++) { 691 switch (topology[i].type) { 692 case B_TOPOLOGY_ROOT: 693 platform = topology[i].data.root.platform; 694 break; 695 696 case B_TOPOLOGY_PACKAGE: 697 cpuVendor = topology[i].data.package.vendor; 698 break; 699 700 case B_TOPOLOGY_CORE: 701 cpuModel = topology[i].data.core.model; 702 frequency = topology[i].data.core.default_frequency; 703 break; 704 705 default: 706 break; 707 } 708 } 709 delete[] topology; 710 711 const char *vendor = get_cpu_vendor_string(cpuVendor); 712 const char *model = get_cpu_model_string(platform, cpuVendor, cpuModel); 713 char modelString[32]; 714 715 if (model == NULL && vendor == NULL) 716 model = "(Unknown)"; 717 else if (model == NULL) { 718 model = modelString; 719 snprintf(modelString, 32, "(Unknown %" B_PRIx32 ")", cpuModel); 720 } 721 722 printf("%" B_PRId32 " %s%s%s, revision %04" B_PRIx32 " running at %" 723 B_PRIu64 "MHz\n\n", 724 info->cpu_count, 725 vendor ? vendor : "", vendor ? " " : "", model, 726 cpuModel, 727 frequency / 1000000); 728 729 #if defined(__i386__) || defined(__x86_64__) 730 for (uint32 cpu = 0; cpu < info->cpu_count; cpu++) 731 dump_cpu(cpuVendor, cpuModel, cpu); 732 #endif // __i386__ || __x86_64__ 733 } 734 735 736 static void 737 dump_mem(system_info *info) 738 { 739 printf("%10" B_PRIu64 " bytes free (used/max %10" B_PRIu64 " / %10" 740 B_PRIu64 ")\n", 741 B_PAGE_SIZE * (uint64)(info->max_pages - info->used_pages), 742 B_PAGE_SIZE * (uint64)info->used_pages, 743 B_PAGE_SIZE * (uint64)info->max_pages); 744 printf(" (cached %10" B_PRIu64 ")\n", 745 B_PAGE_SIZE * (uint64)info->cached_pages); 746 } 747 748 749 static void 750 dump_sem(system_info *info) 751 { 752 printf("%10" B_PRId32 " semaphores free (used/max %10" B_PRId32 " / %10" 753 B_PRId32 ")\n", 754 info->max_sems - info->used_sems, 755 info->used_sems, info->max_sems); 756 } 757 758 759 static void 760 dump_ports(system_info *info) 761 { 762 printf("%10" B_PRId32 " ports free (used/max %10" B_PRId32 " / %10" 763 B_PRId32 ")\n", 764 info->max_ports - info->used_ports, 765 info->used_ports, info->max_ports); 766 } 767 768 769 static void 770 dump_thread(system_info *info) 771 { 772 printf("%10" B_PRId32 " threads free (used/max %10" B_PRId32 " / %10" 773 B_PRId32 ")\n", 774 info->max_threads - info->used_threads, 775 info->used_threads, info->max_threads); 776 } 777 778 779 static void 780 dump_team(system_info *info) 781 { 782 printf("%10" B_PRId32 " teams free (used/max %10" B_PRId32 " / %10" 783 B_PRId32 ")\n", 784 info->max_teams - info->used_teams, 785 info->used_teams, info->max_teams); 786 } 787 788 789 static void 790 dump_kinfo(system_info *info) 791 { 792 printf("Kernel name: %s built on: %s %s version 0x%" B_PRIx64 "\n", 793 info->kernel_name, 794 info->kernel_build_date, info->kernel_build_time, 795 info->kernel_version ); 796 } 797 798 799 static void 800 dump_system_info(system_info *info) 801 { 802 dump_kinfo(info); 803 dump_cpus(info); 804 dump_mem(info); 805 dump_sem(info); 806 dump_ports(info); 807 dump_thread(info); 808 dump_team(info); 809 } 810 811 812 int 813 main(int argc, char *argv[]) 814 { 815 if (!is_computer_on()) { 816 printf("The computer is not on! No info available\n"); 817 exit(EXIT_FAILURE); 818 } 819 820 system_info info; 821 if (get_system_info(&info) != B_OK) { 822 printf("Error getting system information!\n"); 823 return 1; 824 } 825 826 if (argc <= 1) { 827 dump_system_info(&info); 828 } else { 829 for (int i = 1; i < argc; i++) { 830 const char *opt = argv[i]; 831 if (strncmp(opt, "-id", strlen(opt)) == 0) { 832 /* note: the original also assumes this option on "sysinfo -" */ 833 printf("%#.8x %#.8x\n", 0,0); 834 } else if (strncmp(opt, "-cpu", strlen(opt)) == 0) { 835 dump_cpus(&info); 836 } else if (strncmp(opt, "-mem", strlen(opt)) == 0) { 837 dump_mem(&info); 838 } else if (strncmp(opt, "-semaphores", strlen(opt)) == 0) { 839 dump_sem(&info); 840 } else if (strncmp(opt, "-ports", strlen(opt)) == 0) { 841 dump_ports(&info); 842 } else if (strncmp(opt, "-threads", strlen(opt)) == 0) { 843 dump_thread(&info); 844 } else if (strncmp(opt, "-teams", strlen(opt)) == 0) { 845 dump_team(&info); 846 } else if (strncmp(opt, "-kinfo", strlen(opt)) == 0) { 847 dump_kinfo(&info); 848 } else if (strncmp(opt, "-platform", strlen(opt)) == 0) { 849 dump_platform(&info); 850 } else if (strncmp(opt, "-disable_cpu_sn", strlen(opt)) == 0) { 851 /* TODO: printf("CPU #%d serial number: old state: %s, new state: %s\n", ... ); */ 852 fprintf(stderr, "Sorry, not yet implemented\n"); 853 } else { 854 const char *name = strrchr(argv[0], '/'); 855 if (name == NULL) 856 name = argv[0]; 857 else 858 name++; 859 860 fprintf(stderr, "Usage:\n"); 861 fprintf(stderr, " %s [-id|-cpu|-mem|-semaphore|-ports|-threads|-teams|-platform|-disable_cpu_sn|-kinfo]\n", name); 862 return 0; 863 } 864 } 865 } 866 return 0; 867 } 868