1 /* $NetBSD: aarch64.c,v 1.2 2018/05/08 11:42:43 ryo Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Ryo Shimizu <ryo@nerv.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 31 #ifndef lint 32 __RCSID("$NetBSD: aarch64.c,v 1.2 2018/05/08 11:42:43 ryo Exp $"); 33 #endif /* no lint */ 34 35 #include <sys/types.h> 36 #include <sys/cpuio.h> 37 #include <sys/sysctl.h> 38 #include <stdio.h> 39 #include <stdbool.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <inttypes.h> 43 #include <err.h> 44 45 #include <arm/cputypes.h> 46 #include <aarch64/armreg.h> 47 48 #include "../cpuctl.h" 49 50 struct cpuidtab { 51 uint32_t cpu_partnum; 52 const char *cpu_name; 53 const char *cpu_class; 54 const char *cpu_architecture; 55 }; 56 57 struct impltab { 58 uint32_t impl_id; 59 const char *impl_name; 60 }; 61 62 struct fieldinfo { 63 int bitpos; 64 int bitwidth; 65 const char *name; 66 const char * const *info; 67 }; 68 69 70 #define CPU_PARTMASK (CPU_ID_IMPLEMENTOR_MASK | CPU_ID_PARTNO_MASK) 71 const struct cpuidtab cpuids[] = { 72 { CPU_ID_CORTEXA53R0 & CPU_PARTMASK, "Cortex-A53", "Cortex", "V8-A" }, 73 { CPU_ID_CORTEXA57R0 & CPU_PARTMASK, "Cortex-A57", "Cortex", "V8-A" }, 74 { CPU_ID_CORTEXA72R0 & CPU_PARTMASK, "Cortex-A72", "Cortex", "V8-A" }, 75 { CPU_ID_CORTEXA73R0 & CPU_PARTMASK, "Cortex-A73", "Cortex", "V8-A" }, 76 { CPU_ID_CORTEXA55R1 & CPU_PARTMASK, "Cortex-A55", "Cortex", "V8.2-A" }, 77 { CPU_ID_CORTEXA75R2 & CPU_PARTMASK, "Cortex-A75", "Cortex", "V8.2-A" } 78 }; 79 80 const struct impltab implids[] = { 81 { CPU_ID_ARM_LTD, "ARM Limited" }, 82 { CPU_ID_BROADCOM, "Broadcom Corporation" }, 83 { CPU_ID_CAVIUM, "Cavium Inc." }, 84 { CPU_ID_DEC, "Digital Equipment Corporation" }, 85 { CPU_ID_INFINEON, "Infineon Technologies AG" }, 86 { CPU_ID_MOTOROLA, "Motorola or Freescale Semiconductor Inc." }, 87 { CPU_ID_NVIDIA, "NVIDIA Corporation" }, 88 { CPU_ID_APM, "Applied Micro Circuits Corporation" }, 89 { CPU_ID_QUALCOMM, "Qualcomm Inc." }, 90 { CPU_ID_SAMSUNG, "SAMSUNG" }, 91 { CPU_ID_TI, "Texas Instruments" }, 92 { CPU_ID_MARVELL, "Marvell International Ltd." }, 93 { CPU_ID_APPLE, "Apple Inc." }, 94 { CPU_ID_FARADAY, "Faraday Technology Corporation" }, 95 { CPU_ID_INTEL, "Intel Corporation" } 96 }; 97 98 /* ID_AA64PFR0_EL1 - AArch64 Processor Feature Register 0 */ 99 struct fieldinfo id_aa64pfr0_fieldinfo[] = { 100 { 101 .bitpos = 0, .bitwidth = 4, .name = "EL0", 102 .info = (const char *[16]) { /* 16=4bit */ 103 [0] = "No EL0", 104 [1] = "AArch64", 105 [2] = "AArch64/AArch32" 106 } 107 }, 108 { 109 .bitpos = 4, .bitwidth = 4, .name = "EL1", 110 .info = (const char *[16]) { /* 16=4bit */ 111 [0] = "No EL1", 112 [1] = "AArch64", 113 [2] = "AArch64/AArch32" 114 } 115 }, 116 { 117 .bitpos = 8, .bitwidth = 4, .name = "EL2", 118 .info = (const char *[16]) { /* 16=4bit */ 119 [0] = "No EL2", 120 [1] = "AArch64", 121 [2] = "AArch64/AArch32" 122 } 123 }, 124 { 125 .bitpos = 12, .bitwidth = 4, .name = "EL3", 126 .info = (const char *[16]) { /* 16=4bit */ 127 [0] = "No EL3", 128 [1] = "AArch64", 129 [2] = "AArch64/AArch32" 130 } 131 }, 132 { 133 .bitpos = 16, .bitwidth = 4, .name = "FP", 134 .info = (const char *[16]) { /* 16=4bit */ 135 [0] = "Floating Point", 136 [15] = "No Floating Point" 137 } 138 }, 139 { 140 .bitpos = 20, .bitwidth = 4, .name = "AdvSIMD", 141 .info = (const char *[16]) { /* 16=4bit */ 142 [0] = "Advanced SIMD", 143 [15] = "No Advanced SIMD" 144 } 145 }, 146 { 147 .bitpos = 24, .bitwidth = 4, .name = "GIC", 148 .info = (const char *[16]) { /* 16=4bit */ 149 [0] = "No GIC", 150 [1] = "GICv3" 151 } 152 }, 153 { .bitwidth = 0 } /* end of table */ 154 }; 155 156 /* ID_AA64ISAR0_EL1 - AArch64 Instruction Set Attribute Register 0 */ 157 struct fieldinfo id_aa64isar0_fieldinfo[] = { 158 { 159 .bitpos = 4, .bitwidth = 4, .name = "AES", 160 .info = (const char *[16]) { /* 16=4bit */ 161 [0] = "No AES", 162 [1] = "AESE/AESD/AESMC/AESIMC", 163 [2] = "AESE/AESD/AESMC/AESIMC+PMULL/PMULL2" 164 } 165 }, 166 { 167 .bitpos = 8, .bitwidth = 4, .name = "SHA1", 168 .info = (const char *[16]) { /* 16=4bit */ 169 [0] = "No SHA1", 170 [1] = "SHA1C/SHA1P/SHA1M/SHA1H/SHA1SU0/SHA1SU1" 171 } 172 }, 173 { 174 .bitpos = 12, .bitwidth = 4, .name = "SHA2", 175 .info = (const char *[16]) { /* 16=4bit */ 176 [0] = "No SHA2", 177 [1] = "SHA256H/SHA256H2/SHA256SU0/SHA256U1" 178 } 179 }, 180 { 181 .bitpos = 16, .bitwidth = 4, .name = "CRC32", 182 .info = (const char *[16]) { /* 16=4bit */ 183 [0] = "No CRC32", 184 [1] = "CRC32B/CRC32H/CRC32W/CRC32X" 185 "/CRC32CB/CRC32CH/CRC32CW/CRC32CX" 186 } 187 }, 188 { .bitwidth = 0 } /* end of table */ 189 }; 190 191 /* ID_AA64MMFR0_EL1 - AArch64 Memory Model Feature Register 0 */ 192 struct fieldinfo id_aa64mmfr0_fieldinfo[] = { 193 { 194 .bitpos = 0, .bitwidth = 4, .name = "PARange", 195 .info = (const char *[16]) { /* 16=4bit */ 196 [0] = "32bits/4GB", 197 [1] = "36bits/64GB", 198 [2] = "40bits/1TB", 199 [3] = "42bits/4TB", 200 [4] = "44bits/16TB", 201 [5] = "48bits/256TB" 202 } 203 }, 204 { 205 .bitpos = 4, .bitwidth = 4, .name = "ASIDBit", 206 .info = (const char *[16]) { /* 16=4bit */ 207 [0] = "8bits", 208 [2] = "16bits" 209 } 210 }, 211 { 212 .bitpos = 8, .bitwidth = 4, .name = "BigEnd", 213 .info = (const char *[16]) { /* 16=4bit */ 214 [0] = "No mixed-endian", 215 [1] = "Mixed-endian" 216 } 217 }, 218 { 219 .bitpos = 12, .bitwidth = 4, .name = "SNSMem", 220 .info = (const char *[16]) { /* 16=4bit */ 221 [0] = "No distinction B/W Secure and Non-secure Memory", 222 [1] = "Distinction B/W Secure and Non-secure Memory" 223 } 224 }, 225 { 226 .bitpos = 16, .bitwidth = 4, .name = "BigEndEL0", 227 .info = (const char *[16]) { /* 16=4bit */ 228 [0] = "No mixed-endian at EL0", 229 [1] = "Mixed-endian at EL0" 230 } 231 }, 232 { 233 .bitpos = 20, .bitwidth = 4, .name = "TGran16", 234 .info = (const char *[16]) { /* 16=4bit */ 235 [0] = "No 16KB granule", 236 [1] = "16KB granule" 237 } 238 }, 239 { 240 .bitpos = 24, .bitwidth = 4, .name = "TGran64", 241 .info = (const char *[16]) { /* 16=4bit */ 242 [0] = "64KB granule", 243 [15] = "No 64KB granule" 244 } 245 }, 246 { 247 .bitpos = 28, .bitwidth = 4, .name = "TGran4", 248 .info = (const char *[16]) { /* 16=4bit */ 249 [0] = "4KB granule", 250 [15] = "No 4KB granule" 251 } 252 }, 253 { .bitwidth = 0 } /* end of table */ 254 }; 255 256 /* MVFR0_EL1 - Media and VFP Feature Register 0 */ 257 struct fieldinfo mvfr0_fieldinfo[] = { 258 { 259 .bitpos = 0, .bitwidth = 4, .name = "SIMDreg", 260 .info = (const char *[16]) { /* 16=4bit */ 261 [0] = "No SIMD", 262 [1] = "16x64-bit SIMD", 263 [2] = "32x64-bit SIMD" 264 } 265 }, 266 { 267 .bitpos = 4, .bitwidth = 4, .name = "FPSP", 268 .info = (const char *[16]) { /* 16=4bit */ 269 [0] = "No VFP support single precision", 270 [1] = "VFPv2 support single precision", 271 [2] = "VFPv2/VFPv3/VFPv4 support single precision" 272 } 273 }, 274 { 275 .bitpos = 8, .bitwidth = 4, .name = "FPDP", 276 .info = (const char *[16]) { /* 16=4bit */ 277 [0] = "No VFP support double precision", 278 [1] = "VFPv2 support double precision", 279 [2] = "VFPv2/VFPv3/VFPv4 support double precision" 280 } 281 }, 282 { 283 .bitpos = 12, .bitwidth = 4, .name = "FPTrap", 284 .info = (const char *[16]) { /* 16=4bit */ 285 [0] = "VFPv2 support exception trapping", 286 [1] = "VFPv2/VFPv3/VFPv4 support exception trapping" 287 } 288 }, 289 { 290 .bitpos = 16, .bitwidth = 4, .name = "FPDivide", 291 .info = (const char *[16]) { /* 16=4bit */ 292 [0] = "VDIV not supported", 293 [1] = "VDIV supported" 294 } 295 }, 296 { 297 .bitpos = 20, .bitwidth = 4, .name = "FPSqrt", 298 .info = (const char *[16]) { /* 16=4bit */ 299 [0] = "VSQRT not supported", 300 [1] = "VSQRT supported" 301 } 302 }, 303 { 304 .bitpos = 24, .bitwidth = 4, .name = "FPShVec", 305 .info = (const char *[16]) { /* 16=4bit */ 306 [0] = "Short Vectors not supported", 307 [1] = "Short Vectors supported" 308 } 309 }, 310 { 311 .bitpos = 28, .bitwidth = 4, .name = "FPRound", 312 .info = (const char *[16]) { /* 16=4bit */ 313 [0] = "Only Round to Nearest mode", 314 [1] = "All rounding modes" 315 } 316 }, 317 { .bitwidth = 0 } /* end of table */ 318 }; 319 320 /* MVFR1_EL1 - Media and VFP Feature Register 1 */ 321 struct fieldinfo mvfr1_fieldinfo[] = { 322 { 323 .bitpos = 0, .bitwidth = 4, .name = "FPFtZ", 324 .info = (const char *[16]) { /* 16=4bit */ 325 [0] = "only the Flush-to-Zero", 326 [1] = "full Denormalized number arithmetic" 327 } 328 }, 329 { 330 .bitpos = 4, .bitwidth = 4, .name = "FPDNan", 331 .info = (const char *[16]) { /* 16=4bit */ 332 [0] = "Default NaN", 333 [1] = "Propagation of NaN" 334 } 335 }, 336 { 337 .bitpos = 8, .bitwidth = 4, .name = "SIMDLS", 338 .info = (const char *[16]) { /* 16=4bit */ 339 [0] = "No Advanced SIMD Load/Store", 340 [1] = "Advanced SIMD Load/Store" 341 } 342 }, 343 { 344 .bitpos = 12, .bitwidth = 4, .name = "SIMDInt", 345 .info = (const char *[16]) { /* 16=4bit */ 346 [0] = "No Advanced SIMD Integer", 347 [1] = "Advanced SIMD Integer" 348 } 349 }, 350 { 351 .bitpos = 16, .bitwidth = 4, .name = "SIMDSP", 352 .info = (const char *[16]) { /* 16=4bit */ 353 [0] = "No Advanced SIMD single precision", 354 [1] = "Advanced SIMD single precision" 355 } 356 }, 357 { 358 .bitpos = 20, .bitwidth = 4, .name = "SIMDHP", 359 .info = (const char *[16]) { /* 16=4bit */ 360 [0] = "No Advanced SIMD half precision", 361 [1] = "Advanced SIMD half precision" 362 } 363 }, 364 { 365 .bitpos = 24, .bitwidth = 4, .name = "FPHP", 366 .info = (const char *[16]) { /* 16=4bit */ 367 [0] = "No half precision conversion", 368 [1] = "half/single precision conversion", 369 [2] = "half/single/double precision conversion" 370 } 371 }, 372 { 373 .bitpos = 28, .bitwidth = 4, .name = "SIMDFMAC", 374 .info = (const char *[16]) { /* 16=4bit */ 375 [0] = "No Fused Multiply-Accumulate", 376 [1] = "Fused Multiply-Accumulate" 377 } 378 }, 379 { .bitwidth = 0 } /* end of table */ 380 }; 381 382 /* MVFR2_EL1 - Media and VFP Feature Register 2 */ 383 struct fieldinfo mvfr2_fieldinfo[] = { 384 { 385 .bitpos = 0, .bitwidth = 4, .name = "SIMDMisc", 386 .info = (const char *[16]) { /* 16=4bit */ 387 [0] = "No miscellaneous features", 388 [1] = "Conversion to Integer w/Directed Rounding modes", 389 [2] = "Conversion to Integer w/Directed Rounding modes" 390 ", Round to Integral floating point", 391 [3] = "Conversion to Integer w/Directed Rounding modes" 392 ", Round to Integral floating point" 393 ", MaxNum and MinNum" 394 } 395 }, 396 { 397 .bitpos = 4, .bitwidth = 4, .name = "FPMisc", 398 .info = (const char *[16]) { /* 16=4bit */ 399 [0] = "No miscellaneous features", 400 [1] = "Floating point selection", 401 [2] = "Floating point selection" 402 ", Conversion to Integer w/Directed Rounding modes", 403 [3] = "Floating point selection" 404 ", Conversion to Integer w/Directed Rounding modes" 405 ", Round to Integral floating point", 406 [4] = "Floating point selection" 407 ", Conversion to Integer w/Directed Rounding modes" 408 ", Round to Integral floating point" 409 ", MaxNum and MinNum" 410 } 411 }, 412 { .bitwidth = 0 } /* end of table */ 413 }; 414 415 static void 416 print_fieldinfo(const char *cpuname, const char *setname, 417 struct fieldinfo *fieldinfo, uint64_t data) 418 { 419 uint64_t v; 420 const char *info; 421 int i; 422 423 #define WIDTHMASK(w) (0xffffffffffffffffULL >> (64 - (w))) 424 425 for (i = 0; fieldinfo[i].bitwidth != 0; i++) { 426 v = (data >> fieldinfo[i].bitpos) & 427 WIDTHMASK(fieldinfo[i].bitwidth); 428 429 info = fieldinfo[i].info[v]; 430 if (info == NULL) 431 printf("%s: %s: %s: 0x%"PRIx64"\n", 432 cpuname, setname, fieldinfo[i].name, v); 433 else 434 printf("%s: %s: %s: %s\n", 435 cpuname, setname, fieldinfo[i].name, info); 436 } 437 } 438 439 /* MIDR_EL1 - Main ID Register */ 440 static void 441 identify_midr(const char *cpuname, uint32_t cpuid) 442 { 443 unsigned int i; 444 uint32_t implid, cpupart, variant, revision; 445 const char *implementer = NULL; 446 static char implbuf[128]; 447 448 implid = cpuid & CPU_ID_IMPLEMENTOR_MASK; 449 cpupart = cpuid & CPU_PARTMASK; 450 variant = __SHIFTOUT(cpuid, CPU_ID_VARIANT_MASK); 451 revision = __SHIFTOUT(cpuid, CPU_ID_REVISION_MASK); 452 453 for (i = 0; i < __arraycount(implids); i++) { 454 if (implid == implids[i].impl_id) { 455 implementer = implids[i].impl_name; 456 } 457 } 458 if (implementer == NULL) { 459 snprintf(implbuf, sizeof(implbuf), "unknown implementer: 0x%02x", 460 implid >> 24); 461 implementer = implbuf; 462 } 463 464 for (i = 0; i < __arraycount(cpuids); i++) { 465 if (cpupart == cpuids[i].cpu_partnum) { 466 printf("%s: %s, %s r%dp%d (%s %s core)\n", 467 cpuname, implementer, 468 cpuids[i].cpu_name, variant, revision, 469 cpuids[i].cpu_class, 470 cpuids[i].cpu_architecture); 471 return; 472 } 473 } 474 printf("%s: unknown CPU ID: 0x%08x\n", cpuname, cpuid); 475 } 476 477 /* REVIDR_EL1 - Revision ID Register */ 478 static void 479 identify_revidr(const char *cpuname, uint32_t revidr) 480 { 481 printf("%s: revision: 0x%08x\n", cpuname, revidr); 482 } 483 484 /* MPIDR_EL1 - Multiprocessor Affinity Register */ 485 static void 486 identify_mpidr(const char *cpuname, uint32_t mpidr) 487 { 488 const char *setname = "multiprocessor affinity"; 489 490 printf("%s: %s: Affinity-Level: %"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"\n", 491 cpuname, setname, 492 __SHIFTOUT(mpidr, MPIDR_AFF3), 493 __SHIFTOUT(mpidr, MPIDR_AFF2), 494 __SHIFTOUT(mpidr, MPIDR_AFF1), 495 __SHIFTOUT(mpidr, MPIDR_AFF0)); 496 497 if ((mpidr & MPIDR_U) == 0) 498 printf("%s: %s: Multiprocessor system\n", cpuname, setname); 499 else 500 printf("%s: %s: Uniprocessor system\n", cpuname, setname); 501 502 if ((mpidr & MPIDR_MT) == 0) 503 printf("%s: %s: Core Independent\n", cpuname, setname); 504 else 505 printf("%s: %s: Multi-Threading\n", cpuname, setname); 506 507 } 508 509 static void * 510 sysctlfetch(const char *sname, size_t *lenp) 511 { 512 size_t len; 513 void *data; 514 515 if (sysctlbyname(sname, NULL, &len, NULL, 0) != 0) { 516 warn("sysctlbyname: %s", sname); 517 return NULL; 518 } 519 520 data = malloc(len); 521 if (data == NULL) { 522 warn("malloc"); 523 return NULL; 524 } 525 526 if (sysctlbyname(sname, data, &len, NULL, 0) != 0) { 527 warn("sysctlbyname: %s", sname); 528 free(data); 529 return NULL; 530 } 531 532 *lenp = len; 533 return data; 534 } 535 536 void 537 identifycpu(int fd, const char *cpuname) 538 { 539 void *regs; 540 size_t len; 541 542 /* MIDR_EL1 */ 543 regs = sysctlfetch("machdep.cpu_id", &len); 544 if (regs != NULL) { 545 if (len >= sizeof(uint32_t)) 546 identify_midr(cpuname, ((uint32_t *)regs)[0]); 547 free(regs); 548 } 549 550 /* REVIDR_EL1 */ 551 regs = sysctlfetch("machdep.id_revidr", &len); 552 if (regs != NULL) { 553 if (len >= sizeof(uint32_t)) 554 identify_revidr(cpuname, ((uint32_t *)regs)[0]); 555 free(regs); 556 } 557 558 /* MPIDR_EL1 */ 559 regs = sysctlfetch("machdep.id_mpidr", &len); 560 if (regs != NULL) { 561 if (len >= sizeof(uint64_t)) 562 identify_mpidr(cpuname, ((uint64_t *)regs)[0]); 563 free(regs); 564 } 565 566 /* ID_AA64ISAR0_EL1 */ 567 regs = sysctlfetch("machdep.id_aa64isar", &len); 568 if (regs != NULL) { 569 if (len >= sizeof(uint64_t)) 570 print_fieldinfo(cpuname, "isa features 0", 571 id_aa64isar0_fieldinfo, ((uint64_t *)regs)[0]); 572 free(regs); 573 } 574 575 /* ID_AA64MMFR0_EL1 */ 576 regs = sysctlfetch("machdep.id_aa64mmfr", &len); 577 if (regs != NULL) { 578 if (len >= sizeof(uint64_t)) 579 print_fieldinfo(cpuname, "memory model 0", 580 id_aa64mmfr0_fieldinfo, ((uint64_t *)regs)[0]); 581 free(regs); 582 } 583 584 /* ID_AA64PFR0_EL1 */ 585 regs = sysctlfetch("machdep.id_aa64pfr", &len); 586 if (regs != NULL) { 587 if (len >= sizeof(uint64_t)) 588 print_fieldinfo(cpuname, "processor feature 0", 589 id_aa64pfr0_fieldinfo, ((uint64_t *)regs)[0]); 590 free(regs); 591 } 592 593 /* MVFR[012]_EL1 */ 594 regs = sysctlfetch("machdep.id_mvfr", &len); 595 if (regs != NULL) { 596 if (len >= sizeof(uint32_t)) 597 print_fieldinfo(cpuname, "media and VFP features 0", 598 mvfr0_fieldinfo, ((uint32_t *)regs)[0]); 599 if (len >= sizeof(uint32_t) * 2) 600 print_fieldinfo(cpuname, "media and VFP features 1", 601 mvfr1_fieldinfo, ((uint32_t *)regs)[1]); 602 if (len >= sizeof(uint32_t) * 3) 603 print_fieldinfo(cpuname, "media and VFP features 2", 604 mvfr2_fieldinfo, ((uint32_t *)regs)[2]); 605 free(regs); 606 } 607 } 608 609 bool 610 identifycpu_bind(void) 611 { 612 return true; 613 } 614 615 int 616 ucodeupdate_check(int fd, struct cpu_ucode *uc) 617 { 618 return 0; 619 } 620