1/* 2 * Copyright (c) 2015 ETH Zurich. 3 * All rights reserved. 4 * 5 * This file is distributed under the terms in the attached LICENSE file. 6 * If you do not find this file, copies can be found by writing to: 7 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group. 8 */ 9 10#include <cpuid_internal.h> 11 12#include <dev/cpuid_amd_dev.h> 13/* 14 * =============================================================================== 15 * helpers 16 * =============================================================================== 17 */ 18 19static uint16_t lookup_cache_assoc(uint8_t key) 20{ 21 switch (key) { 22 case cpuid_amd_cache_assoc_disabled: 23 return 0; 24 case cpuid_amd_cache_assoc_direct: 25 return 1; 26 case cpuid_amd_cache_assoc_2way: 27 return 2; 28 case cpuid_amd_cache_assoc_4way: 29 return 4; 30 case cpuid_amd_cache_assoc_8way: 31 return 8; 32 case cpuid_amd_cache_assoc_16way: 33 return 16; 34 case cpuid_amd_cache_assoc_32way: 35 return 32; 36 case cpuid_amd_cache_assoc_48way: 37 return 48; 38 case cpuid_amd_cache_assoc_64way: 39 return 64; 40 case cpuid_amd_cache_assoc_96way: 41 return 96; 42 case cpuid_amd_cache_assoc_128way : 43 return 128; 44 case cpuid_amd_cache_assoc_fully : 45 return 0xff; 46 } 47 return 0; 48} 49 50/* 51 * =============================================================================== 52 * basic processor information 53 * =============================================================================== 54 */ 55 56static errval_t proc_name(char *buf, size_t len) 57{ 58 // check if this operation is supported 59 if (cpuid_g_max_input_extended < 4) { 60 return CPUID_ERR_UNSUPPORTED_FUNCTION; 61 } 62 63 size_t written; 64 65 struct cpuid_regs reg = CPUID_REGS_INITIAL(0x80000002, 0); 66 cpuid_exec(®); 67 written = snprintf(buf, len, "%s", (char *)®.eax); 68 len -= written; 69 buf += written; 70 71 reg.eax = 0x80000003; 72 reg.ecx = 0x0; 73 cpuid_exec(®); 74 written = snprintf(buf, len, "%s", (char *)®.eax); 75 len -= written; 76 buf += written; 77 78 reg.eax = 0x80000004; 79 reg.ecx = 0x0; 80 cpuid_exec(®); 81 written = snprintf(buf, len, "%s", (char *)®.eax); 82 len -= written; 83 buf += written; 84 85 return SYS_ERR_OK; 86} 87 88static errval_t proc_family(struct cpuid_proc_family *family) 89{ 90 if (cpuid_g_max_input_basic < 1) { 91 return CPUID_ERR_UNSUPPORTED_FUNCTION; 92 } 93 94 struct cpuid_regs reg = CPUID_REGS_INITIAL(1, 0); 95 cpuid_exec(®); 96 97 cpuid_amd_family_t f = (cpuid_amd_family_t)®.eax; 98 99 family->stepping = cpuid_amd_family_stepping_extract(f); 100 family->family = cpuid_amd_family_family_extract(f); 101 family->model = cpuid_amd_family_model_extract(f); 102 // amd has no proc type 103 family->type = 0x0; 104 105 if (family->family == 0x0f) { 106 uint16_t model = cpuid_amd_family_extmodel_extract(f); 107 family->model += (model << 4); 108 } 109 110 /* if family is zero we have to consider the extended family id */ 111 if (family->family != 0x0f) { 112 family->family += cpuid_amd_family_extfamily_extract(f); 113 } 114 115 return SYS_ERR_OK; 116} 117 118static uint32_t proc_max_input_basic(void) 119{ 120 struct cpuid_regs reg = CPUID_REGS_INITIAL(0, 0); 121 cpuid_exec(®); 122 123 return reg.eax; 124} 125 126static uint32_t proc_max_input_extended(void) 127{ 128 struct cpuid_regs reg = CPUID_REGS_INITIAL(0x80000000, 0); 129 cpuid_exec(®); 130 131 return reg.eax; 132} 133 134static errval_t frequency_info(struct cpuid_freqinfo *fi) 135{ 136 return CPUID_ERR_UNSUPPORTED_FUNCTION; 137} 138 139/* 140 * =============================================================================== 141 * cache topology information 142 * =============================================================================== 143 */ 144 145static errval_t cache_info_alternate(struct cpuid_cacheinfo *ci, uint32_t idx) 146{ 147 if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x6) { 148 CPUID_PRINTF("amd_cache_info not supported. max fnct= %x\n", 149 cpuid_g_max_input_extended); 150 return CPUID_ERR_UNSUPPORTED_FUNCTION; 151 } 152 153 if (idx >= 4) { 154 return CPUID_ERR_INVALID_INDEX; 155 } 156 157 158 uint32_t fn = 0x80000006; 159 if (idx < 2) { 160 fn = 0x80000005; 161 } 162 struct cpuid_regs reg = CPUID_REGS_INITIAL(fn, 0); 163 cpuid_exec(®); 164 165 switch(idx) { 166 case 0:; 167 cpuid_amd_l1_dcache_t l1d = (cpuid_amd_l1_dcache_t)®.ecx; 168 ci->associativity = cpuid_amd_l1_dcache_assoc_extract(l1d); 169 ci->size = cpuid_amd_l1_dcache_size_extract(l1d) * 1024; 170 ci->linesize = cpuid_amd_l1_dcache_linesize_extract(l1d); 171 ci->level = 1; 172 ci->shared = 1; 173 ci->type = CPUID_CACHE_TYPE_DATA; 174 break; 175 case 1:; 176 cpuid_amd_l1_icache_t l1i = (cpuid_amd_l1_icache_t)®.edx; 177 ci->associativity = cpuid_amd_l1_icache_assoc_extract(l1i); 178 ci->size = cpuid_amd_l1_icache_size_extract(l1i) * 1024; 179 ci->linesize = cpuid_amd_l1_icache_linesize_extract(l1i); 180 ci->level = 1; 181 /* TODO: get the number of cores sharing that cache */ 182 ci->shared = 1; 183 ci->type = CPUID_CACHE_TYPE_INSTR; 184 break; 185 case 2:; 186 cpuid_amd_l2_cache_t l2 = (cpuid_amd_l2_cache_t)®.ecx; 187 ci->associativity = lookup_cache_assoc(cpuid_amd_l2_cache_assoc_extract(l2)); 188 assert(cpuid_amd_l2_cache_assoc_extract(l2)); 189 assert(ci->associativity); 190 ci->size = cpuid_amd_l2_cache_size_extract(l2) * 1024; 191 ci->linesize = cpuid_amd_l2_cache_linesize_extract(l2); 192 ci->level = 2; 193 /* TODO: get the number of cores sharing that cache */ 194 ci->shared = 1; 195 ci->type = CPUID_CACHE_TYPE_UNIFIED; 196 break; 197 case 3:; 198 /* 199 * This provides the processor���s third level cache characteristics 200 * shared by all cores 201 */ 202 cpuid_amd_l3_cache_t l3 = (cpuid_amd_l3_cache_t)®.edx; 203 ci->associativity = lookup_cache_assoc(cpuid_amd_l3_cache_assoc_extract(l3)); 204 ci->size = cpuid_amd_l3_cache_size_extract(l3) * 512 * 1024; 205 ci->linesize = cpuid_amd_l3_cache_linesize_extract(l3); 206 ci->level = 3; 207 /* TODO: get the number of cores sharing that cache */ 208 ci->shared = 1; 209 ci->type = CPUID_CACHE_TYPE_UNIFIED; 210 break; 211 default : 212 return CPUID_ERR_INVALID_INDEX; 213 } 214 if (ci->size == 0 || ci->associativity == 0 || ci->linesize == 0) { 215 /* no size, associativity indicates invalid / disabled cache */ 216 return CPUID_ERR_INVALID_INDEX; 217 } 218 219 ci->inclusive = 1; 220 ci->sets = (ci->size / ci->linesize) / ci->associativity; 221 /* TODO: get the number of cores sharing that cache */ 222 ci->shared = 1; 223 ci->name = cpuid_cache_names[ci->level][ci->type]; 224 225 return SYS_ERR_OK; 226} 227 228static errval_t cache_info(struct cpuid_cacheinfo *ci, uint32_t idx) 229{ 230 if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x1d) { 231 return cache_info_alternate(ci, idx); 232 } 233 234 struct cpuid_regs reg = CPUID_REGS_INITIAL(0x8000001d, idx); 235 cpuid_exec(®); 236 237 cpuid_amd_cache_info_eax_t ci_a = (cpuid_amd_cache_info_eax_t) ®.eax; 238 cpuid_amd_cache_info_ebx_t ci_b = (cpuid_amd_cache_info_ebx_t) ®.ebx; 239 240 ci->level = cpuid_amd_cache_info_eax_level_extract(ci_a); 241 242 switch (cpuid_amd_cache_info_eax_ctype_extract(ci_a)) { 243 case cpuid_amd_cache_type_data : 244 /* data cache */ 245 ci->type = CPUID_CACHE_TYPE_DATA; 246 break; 247 case cpuid_amd_cache_type_instr : 248 /* instruction cache */ 249 ci->type = CPUID_CACHE_TYPE_INSTR; 250 break; 251 case cpuid_amd_cache_type_unified : 252 ci->type = CPUID_CACHE_TYPE_UNIFIED; 253 /* unified cache */ 254 break; 255 default: 256 /* no more cache */ 257 ci->type = CPUID_CACHE_TYPE_INVALID; 258 return CPUID_ERR_INVALID_INDEX; 259 break; 260 } 261 262 ci->name = cpuid_cache_names[ci->level][ci->type]; 263 ci->linesize = cpuid_amd_cache_info_ebx_cachelinesize_extract(ci_b)+1; 264 265 /* the the number of sets */ 266 ci->sets = reg.ecx + 1; 267 268 if (cpuid_amd_cache_info_eax_fullyassoc_extract(ci_a)) { 269 ci->size = (size_t)ci->linesize * ci->sets; 270 ci->associativity = 0xff; 271 } else { 272 ci->associativity = cpuid_amd_cache_info_ebx_assoc_extract(ci_b)+1; 273 ci->size = (size_t)ci->linesize * ci->sets * ci->associativity; 274 } 275 276 ci->shared = cpuid_amd_cache_info_eax_num_sharing_extract(ci_a) +1; 277 278 cpuid_amd_cache_info_edx_t ci_d = (cpuid_amd_cache_info_edx_t) ®.edx; 279 ci->inclusive = cpuid_amd_cache_info_edx_inclusive_extract(ci_d); 280 281 return SYS_ERR_OK; 282} 283 284static uint16_t cache_line_size(void) 285{ 286 if (cpuid_g_max_input_basic < 1) { 287 return CPUID_ERR_UNSUPPORTED_FUNCTION; 288 } 289 290 struct cpuid_regs reg = CPUID_REGS_INITIAL(1, 0); 291 cpuid_exec(®); 292 293 cpuid_amd_miscinfo_t mi = (cpuid_amd_miscinfo_t)®.ebx; 294 295 return cpuid_amd_miscinfo_cflush_sz_extract(mi) * 8; 296} 297 298/* 299 * =============================================================================== 300 * TLB information 301 * =============================================================================== 302 */ 303 304static errval_t tlb_info(struct cpuid_tlbinfo *ti, uint32_t idx) 305{ 306 if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x5) { 307 return CPUID_ERR_UNSUPPORTED_FUNCTION; 308 } 309 struct cpuid_regs reg = CPUID_REGS_INITIAL(0x80000005, 0); 310 ti->pagesize = 0; 311 if (idx < 4) { 312 /* level 1 tlb 4k/2m pages */ 313 ti->level = 1; 314 reg.eax = 0x80000005; 315 cpuid_exec(®); 316 cpuid_amd_tlb_l1_t tlb; 317 if (idx < 2) { 318 tlb = (cpuid_amd_tlb_l1_t)®.eax; 319 ti->pagesize = LARGE_PAGE_SIZE; 320 } else { 321 tlb = (cpuid_amd_tlb_l1_t)®.ebx; 322 ti->pagesize = BASE_PAGE_SIZE; 323 } 324 if (idx & 0x1) { 325 ti->type = CPUID_CACHE_TYPE_DATA; 326 ti->associativity = cpuid_amd_tlb_l1_dtlb_assoc_extract(tlb); 327 ti->entries = cpuid_amd_tlb_l1_dtlb_sz_extract(tlb); 328 } else { 329 ti->type = CPUID_CACHE_TYPE_INSTR; 330 ti->associativity = cpuid_amd_tlb_l1_itlb_assoc_extract(tlb); 331 ti->entries = cpuid_amd_tlb_l1_itlb_sz_extract(tlb); 332 } 333 return SYS_ERR_OK; 334 } else if (idx < 8) { 335 /* level 2 tlb 4k/2m pages */ 336 if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x6) { 337 return CPUID_ERR_UNSUPPORTED_FUNCTION; 338 } 339 idx -= 4; 340 ti->level = 2; 341 ti->pagesize = (idx < 2) ? LARGE_PAGE_SIZE : BASE_PAGE_SIZE; 342 reg.eax = 0x80000006; 343 344 } else if (idx < 12){ 345 /* huge pages */ 346 if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x19) { 347 return CPUID_ERR_UNSUPPORTED_FUNCTION; 348 } 349 idx -= 8; 350 if (idx < 2) { 351 ti->level = 1; 352 } else { 353 ti->level = 2; 354 } 355 ti->pagesize = HUGE_PAGE_SIZE; 356 reg.eax = 0x80000019; 357 } else { 358 return CPUID_ERR_INVALID_INDEX; 359 } 360 361 cpuid_exec(®); 362 cpuid_amd_tlb_l1_t tlb2; 363 if (idx < 2) { 364 tlb2 = (cpuid_amd_tlb_l1_t)®.eax; 365 } else { 366 tlb2 = (cpuid_amd_tlb_l1_t)®.ebx; 367 } 368 369 if (idx & 0x1) { 370 ti->type = CPUID_CACHE_TYPE_DATA; 371 ti->associativity = cpuid_amd_tlb_l2_dtlb_assoc_extract(tlb2); 372 ti->entries = cpuid_amd_tlb_l2_dtlb_sz_extract(tlb2); 373 } else { 374 ti->type = CPUID_CACHE_TYPE_INSTR; 375 ti->associativity = cpuid_amd_tlb_l2_itlb_assoc_extract(tlb2); 376 ti->entries = cpuid_amd_tlb_l2_itlb_sz_extract(tlb2); 377 } 378 ti->associativity = lookup_cache_assoc(ti->associativity); 379 380 return SYS_ERR_OK; 381} 382 383/* 384 * =============================================================================== 385 * thread and topology information 386 * =============================================================================== 387 */ 388 389static errval_t thread_info(struct cpuid_threadinfo *ti) 390{ 391 if (cpuid_g_max_input_basic < 1) { 392 return CPUID_ERR_UNSUPPORTED_FUNCTION; 393 } 394 395 struct cpuid_regs reg = CPUID_REGS_INITIAL(0x01, 0); 396 cpuid_exec(®); 397 398 cpuid_amd_miscinfo_t mi = (cpuid_amd_miscinfo_t)®.ebx; 399 400 uint8_t local_apic_id = cpuid_amd_miscinfo_init_apicid_extract(mi); 401 uint8_t logical_processors = cpuid_amd_miscinfo_max_log_proc_extract(mi); 402 403 if (!((reg.edx >> 28) & 0x1)) { 404 /* TODO: then the following is not valid */ 405 ti->core = 0; 406 ti->hyperthread = 0; 407 ti->package = local_apic_id; 408 return SYS_ERR_OK; 409 } 410 411 if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 8) { 412 return CPUID_ERR_UNSUPPORTED_FUNCTION; 413 } 414 415 reg.eax = 0x80000008; 416 reg.ecx = 0x0; 417 cpuid_exec(®); 418 419 cpuid_amd_apicid_t ac = (cpuid_amd_apicid_t)®.ecx; 420 421 uint8_t mnc; 422 uint8_t ApicIdCoreIdSize = cpuid_amd_apicid_apic_sz_extract(ac); 423 uint8_t nc = cpuid_amd_apicid_ncores_extract(ac); 424 if (ApicIdCoreIdSize == 0) { 425 // Used by legacy dual-core/single-core processors 426 mnc = nc + 1; 427 } else { 428 // use ApicIdCoreIdSize[3:0] field 429 mnc = 1 << ApicIdCoreIdSize; 430 } 431 432 //XXX: not sure about these calculations. 433 // uint8_t hyperthreads = logical_processors / nc; 434 uint8_t ht = logical_processors / mnc; 435 uint8_t ht_shift = cpuid_bits_needed(ht - 1); 436 uint8_t ht_mask = (1 << ht_shift) - 1; 437 uint8_t core_shift = cpuid_bits_needed(mnc - 1); 438 uint8_t core_mask = (1 << core_shift) - 1; 439 440 ti->core = (local_apic_id >> (ht_shift)) & core_mask; 441 ti->package = local_apic_id >> (ht_shift + core_shift); 442 ti->hyperthread = local_apic_id & ht_mask; 443 444 return SYS_ERR_OK; 445} 446 447static errval_t topology_info(struct cpuid_topologyinfo *topo, uint8_t idx) 448{ 449 return 1; 450 return SYS_ERR_OK; 451} 452 453static errval_t feature_info(struct cpuid_featureinfo *fi) 454{ 455 USER_PANIC("NYI"); 456 return SYS_ERR_OK; 457} 458 459static errval_t address_space_info(struct cpuid_adressspaceinfo *ai) 460{ 461 if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x8) { 462 return CPUID_ERR_UNSUPPORTED_FUNCTION; 463 } 464 465 struct cpuid_regs reg = CPUID_REGS_INITIAL(0x80000008, 0); 466 cpuid_exec(®); 467 468 cpuid_amd_addrspace_t as = (cpuid_amd_addrspace_t)®.eax; 469 470 ai->physical = cpuid_amd_addrspace_physical_extract(as); 471 ai->virtual = cpuid_amd_addrspace_linear_extract(as); 472 ai->guest_physical = cpuid_amd_addrspace_guest_extract(as); 473 if (ai->guest_physical == 0) { 474 ai->guest_physical = ai->physical; 475 } 476 477 return SYS_ERR_OK; 478} 479 480/* 481 * =============================================================================== 482 * backend initialization 483 * =============================================================================== 484 */ 485 486/** 487 * \brief fills the vendor specific handler functions 488 * 489 * \param fn_tab function pointer table to be filled 490 */ 491void cpuid_amd_set_handlers(struct cpuid_functions *fn_tab) 492{ 493 fn_tab->proc_name = proc_name; 494 fn_tab->proc_family = proc_family; 495 fn_tab->proc_max_input_basic = proc_max_input_basic; 496 fn_tab->proc_max_input_extended = proc_max_input_extended; 497 fn_tab->frequency_info = frequency_info; 498 499 fn_tab->cache_info = cache_info; 500 fn_tab->cache_line_size = cache_line_size; 501 502 fn_tab->tlb_info = tlb_info; 503 504 fn_tab->thread_info = thread_info; 505 fn_tab->topology_info = topology_info; 506 507 fn_tab->feature_info = feature_info; 508 509 fn_tab->address_space_info = address_space_info; 510} 511