1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011 NetApp, Inc. 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 NETAPP, INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/pcpu.h> 31 #include <sys/systm.h> 32 #include <sys/sysctl.h> 33 34 #include <machine/clock.h> 35 #include <machine/cpufunc.h> 36 #include <machine/md_var.h> 37 #include <machine/segments.h> 38 #include <machine/specialreg.h> 39 40 #include <machine/vmm.h> 41 42 #include "vmm_host.h" 43 #include "vmm_ktr.h" 44 #include "vmm_util.h" 45 #include "x86.h" 46 47 SYSCTL_DECL(_hw_vmm); 48 static SYSCTL_NODE(_hw_vmm, OID_AUTO, topology, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 49 NULL); 50 51 #define CPUID_VM_HIGH 0x40000000 52 53 static const char bhyve_id[12] = "bhyve bhyve "; 54 55 static uint64_t bhyve_xcpuids; 56 SYSCTL_ULONG(_hw_vmm, OID_AUTO, bhyve_xcpuids, CTLFLAG_RW, &bhyve_xcpuids, 0, 57 "Number of times an unknown cpuid leaf was accessed"); 58 59 static int cpuid_leaf_b = 1; 60 SYSCTL_INT(_hw_vmm_topology, OID_AUTO, cpuid_leaf_b, CTLFLAG_RDTUN, 61 &cpuid_leaf_b, 0, NULL); 62 63 /* 64 * Round up to the next power of two, if necessary, and then take log2. 65 * Returns -1 if argument is zero. 66 */ 67 static __inline int 68 log2(u_int x) 69 { 70 71 return (fls(x << (1 - powerof2(x))) - 1); 72 } 73 74 int 75 x86_emulate_cpuid(struct vcpu *vcpu, uint64_t *rax, uint64_t *rbx, 76 uint64_t *rcx, uint64_t *rdx) 77 { 78 struct vm *vm = vcpu_vm(vcpu); 79 int vcpu_id = vcpu_vcpuid(vcpu); 80 const struct xsave_limits *limits; 81 uint64_t cr4; 82 int error, enable_invpcid, enable_rdpid, enable_rdtscp, level, 83 width, x2apic_id; 84 unsigned int func, regs[4], logical_cpus, param; 85 enum x2apic_state x2apic_state; 86 uint16_t cores, maxcpus, sockets, threads; 87 88 /* 89 * The function of CPUID is controlled through the provided value of 90 * %eax (and secondarily %ecx, for certain leaf data). 91 */ 92 func = (uint32_t)*rax; 93 param = (uint32_t)*rcx; 94 95 VCPU_CTR2(vm, vcpu_id, "cpuid %#x,%#x", func, param); 96 97 /* 98 * Requests for invalid CPUID levels should map to the highest 99 * available level instead. 100 */ 101 if (cpu_exthigh != 0 && func >= 0x80000000) { 102 if (func > cpu_exthigh) 103 func = cpu_exthigh; 104 } else if (func >= 0x40000000) { 105 if (func > CPUID_VM_HIGH) 106 func = CPUID_VM_HIGH; 107 } else if (func > cpu_high) { 108 func = cpu_high; 109 } 110 111 /* 112 * In general the approach used for CPU topology is to 113 * advertise a flat topology where all CPUs are packages with 114 * no multi-core or SMT. 115 */ 116 switch (func) { 117 /* 118 * Pass these through to the guest 119 */ 120 case CPUID_0000_0000: 121 case CPUID_0000_0002: 122 case CPUID_0000_0003: 123 case CPUID_8000_0000: 124 case CPUID_8000_0002: 125 case CPUID_8000_0003: 126 case CPUID_8000_0004: 127 case CPUID_8000_0006: 128 cpuid_count(func, param, regs); 129 break; 130 case CPUID_8000_0008: 131 cpuid_count(func, param, regs); 132 if (vmm_is_svm()) { 133 /* 134 * As on Intel (0000_0007:0, EDX), mask out 135 * unsupported or unsafe AMD extended features 136 * (8000_0008 EBX). 137 */ 138 regs[1] &= (AMDFEID_CLZERO | AMDFEID_IRPERF | 139 AMDFEID_XSAVEERPTR); 140 141 vm_get_topology(vm, &sockets, &cores, &threads, 142 &maxcpus); 143 /* 144 * Here, width is ApicIdCoreIdSize, present on 145 * at least Family 15h and newer. It 146 * represents the "number of bits in the 147 * initial apicid that indicate thread id 148 * within a package." 149 * 150 * Our topo_probe_amd() uses it for 151 * pkg_id_shift and other OSes may rely on it. 152 */ 153 width = MIN(0xF, log2(threads * cores)); 154 if (width < 0x4) 155 width = 0; 156 logical_cpus = MIN(0xFF, threads * cores - 1); 157 regs[2] = (width << AMDID_COREID_SIZE_SHIFT) | logical_cpus; 158 } 159 break; 160 161 case CPUID_8000_0001: 162 cpuid_count(func, param, regs); 163 164 /* 165 * Hide SVM from guest. 166 */ 167 regs[2] &= ~AMDID2_SVM; 168 169 /* 170 * Don't advertise extended performance counter MSRs 171 * to the guest. 172 */ 173 regs[2] &= ~AMDID2_PCXC; 174 regs[2] &= ~AMDID2_PNXC; 175 regs[2] &= ~AMDID2_PTSCEL2I; 176 177 /* 178 * Don't advertise Instruction Based Sampling feature. 179 */ 180 regs[2] &= ~AMDID2_IBS; 181 182 /* NodeID MSR not available */ 183 regs[2] &= ~AMDID2_NODE_ID; 184 185 /* Don't advertise the OS visible workaround feature */ 186 regs[2] &= ~AMDID2_OSVW; 187 188 /* Hide mwaitx/monitorx capability from the guest */ 189 regs[2] &= ~AMDID2_MWAITX; 190 191 /* Advertise RDTSCP if it is enabled. */ 192 error = vm_get_capability(vcpu, 193 VM_CAP_RDTSCP, &enable_rdtscp); 194 if (error == 0 && enable_rdtscp) 195 regs[3] |= AMDID_RDTSCP; 196 else 197 regs[3] &= ~AMDID_RDTSCP; 198 break; 199 200 case CPUID_8000_0007: 201 /* 202 * AMD uses this leaf to advertise the processor's 203 * power monitoring and RAS capabilities. These 204 * features are hardware-specific and exposing 205 * them to a guest doesn't make a lot of sense. 206 * 207 * Intel uses this leaf only to advertise the 208 * "Invariant TSC" feature with all other bits 209 * being reserved (set to zero). 210 */ 211 regs[0] = 0; 212 regs[1] = 0; 213 regs[2] = 0; 214 regs[3] = 0; 215 216 /* 217 * "Invariant TSC" can be advertised to the guest if: 218 * - host TSC frequency is invariant 219 * - host TSCs are synchronized across physical cpus 220 * 221 * XXX This still falls short because the vcpu 222 * can observe the TSC moving backwards as it 223 * migrates across physical cpus. But at least 224 * it should discourage the guest from using the 225 * TSC to keep track of time. 226 */ 227 if (tsc_is_invariant && smp_tsc) 228 regs[3] |= AMDPM_TSC_INVARIANT; 229 break; 230 231 case CPUID_8000_001D: 232 /* AMD Cache topology, like 0000_0004 for Intel. */ 233 if (!vmm_is_svm()) 234 goto default_leaf; 235 236 /* 237 * Similar to Intel, generate a fictitious cache 238 * topology for the guest with L3 shared by the 239 * package, and L1 and L2 local to a core. 240 */ 241 vm_get_topology(vm, &sockets, &cores, &threads, 242 &maxcpus); 243 switch (param) { 244 case 0: 245 logical_cpus = threads; 246 level = 1; 247 func = 1; /* data cache */ 248 break; 249 case 1: 250 logical_cpus = threads; 251 level = 2; 252 func = 3; /* unified cache */ 253 break; 254 case 2: 255 logical_cpus = threads * cores; 256 level = 3; 257 func = 3; /* unified cache */ 258 break; 259 default: 260 logical_cpus = 0; 261 level = 0; 262 func = 0; 263 break; 264 } 265 266 logical_cpus = MIN(0xfff, logical_cpus - 1); 267 regs[0] = (logical_cpus << 14) | (1 << 8) | 268 (level << 5) | func; 269 regs[1] = (func > 0) ? (CACHE_LINE_SIZE - 1) : 0; 270 regs[2] = 0; 271 regs[3] = 0; 272 break; 273 274 case CPUID_8000_001E: 275 /* 276 * AMD Family 16h+ and Hygon Family 18h additional 277 * identifiers. 278 */ 279 if (!vmm_is_svm() || CPUID_TO_FAMILY(cpu_id) < 0x16) 280 goto default_leaf; 281 282 vm_get_topology(vm, &sockets, &cores, &threads, 283 &maxcpus); 284 regs[0] = vcpu_id; 285 threads = MIN(0xFF, threads - 1); 286 regs[1] = (threads << 8) | 287 (vcpu_id >> log2(threads + 1)); 288 /* 289 * XXX Bhyve topology cannot yet represent >1 node per 290 * processor. 291 */ 292 regs[2] = 0; 293 regs[3] = 0; 294 break; 295 296 case CPUID_0000_0001: 297 do_cpuid(1, regs); 298 299 error = vm_get_x2apic_state(vcpu, &x2apic_state); 300 if (error) { 301 panic("x86_emulate_cpuid: error %d " 302 "fetching x2apic state", error); 303 } 304 305 /* 306 * Override the APIC ID only in ebx 307 */ 308 regs[1] &= ~(CPUID_LOCAL_APIC_ID); 309 regs[1] |= (vcpu_id << CPUID_0000_0001_APICID_SHIFT); 310 311 /* 312 * Don't expose VMX, SpeedStep, TME or SMX capability. 313 * Advertise x2APIC capability and Hypervisor guest. 314 */ 315 regs[2] &= ~(CPUID2_VMX | CPUID2_EST | CPUID2_TM2); 316 regs[2] &= ~(CPUID2_SMX); 317 318 regs[2] |= CPUID2_HV; 319 320 if (x2apic_state != X2APIC_DISABLED) 321 regs[2] |= CPUID2_X2APIC; 322 else 323 regs[2] &= ~CPUID2_X2APIC; 324 325 /* 326 * Only advertise CPUID2_XSAVE in the guest if 327 * the host is using XSAVE. 328 */ 329 if (!(regs[2] & CPUID2_OSXSAVE)) 330 regs[2] &= ~CPUID2_XSAVE; 331 332 /* 333 * If CPUID2_XSAVE is being advertised and the 334 * guest has set CR4_XSAVE, set 335 * CPUID2_OSXSAVE. 336 */ 337 regs[2] &= ~CPUID2_OSXSAVE; 338 if (regs[2] & CPUID2_XSAVE) { 339 error = vm_get_register(vcpu, 340 VM_REG_GUEST_CR4, &cr4); 341 if (error) 342 panic("x86_emulate_cpuid: error %d " 343 "fetching %%cr4", error); 344 if (cr4 & CR4_XSAVE) 345 regs[2] |= CPUID2_OSXSAVE; 346 } 347 348 /* 349 * Hide monitor/mwait until we know how to deal with 350 * these instructions. 351 */ 352 regs[2] &= ~CPUID2_MON; 353 354 /* 355 * Hide the performance and debug features. 356 */ 357 regs[2] &= ~CPUID2_PDCM; 358 359 /* 360 * No TSC deadline support in the APIC yet 361 */ 362 regs[2] &= ~CPUID2_TSCDLT; 363 364 /* 365 * Hide thermal monitoring 366 */ 367 regs[3] &= ~(CPUID_ACPI | CPUID_TM); 368 369 /* 370 * Hide the debug store capability. 371 */ 372 regs[3] &= ~CPUID_DS; 373 374 /* 375 * Advertise the Machine Check and MTRR capability. 376 * 377 * Some guest OSes (e.g. Windows) will not boot if 378 * these features are absent. 379 */ 380 regs[3] |= (CPUID_MCA | CPUID_MCE | CPUID_MTRR); 381 382 vm_get_topology(vm, &sockets, &cores, &threads, 383 &maxcpus); 384 logical_cpus = threads * cores; 385 regs[1] &= ~CPUID_HTT_CORES; 386 regs[1] |= (logical_cpus & 0xff) << 16; 387 regs[3] |= CPUID_HTT; 388 break; 389 390 case CPUID_0000_0004: 391 cpuid_count(func, param, regs); 392 393 if (regs[0] || regs[1] || regs[2] || regs[3]) { 394 vm_get_topology(vm, &sockets, &cores, &threads, 395 &maxcpus); 396 regs[0] &= 0x3ff; 397 regs[0] |= (cores - 1) << 26; 398 /* 399 * Cache topology: 400 * - L1 and L2 are shared only by the logical 401 * processors in a single core. 402 * - L3 and above are shared by all logical 403 * processors in the package. 404 */ 405 logical_cpus = threads; 406 level = (regs[0] >> 5) & 0x7; 407 if (level >= 3) 408 logical_cpus *= cores; 409 regs[0] |= (logical_cpus - 1) << 14; 410 } 411 break; 412 413 case CPUID_0000_0007: 414 regs[0] = 0; 415 regs[1] = 0; 416 regs[2] = 0; 417 regs[3] = 0; 418 419 /* leaf 0 */ 420 if (param == 0) { 421 cpuid_count(func, param, regs); 422 423 /* Only leaf 0 is supported */ 424 regs[0] = 0; 425 426 /* 427 * Expose known-safe features. 428 */ 429 regs[1] &= CPUID_STDEXT_FSGSBASE | 430 CPUID_STDEXT_BMI1 | CPUID_STDEXT_HLE | 431 CPUID_STDEXT_AVX2 | CPUID_STDEXT_SMEP | 432 CPUID_STDEXT_BMI2 | 433 CPUID_STDEXT_ERMS | CPUID_STDEXT_RTM | 434 CPUID_STDEXT_AVX512F | 435 CPUID_STDEXT_AVX512DQ | 436 CPUID_STDEXT_RDSEED | 437 CPUID_STDEXT_SMAP | 438 CPUID_STDEXT_AVX512PF | 439 CPUID_STDEXT_AVX512ER | 440 CPUID_STDEXT_AVX512CD | CPUID_STDEXT_SHA | 441 CPUID_STDEXT_AVX512BW | 442 CPUID_STDEXT_AVX512VL; 443 regs[2] &= CPUID_STDEXT2_VAES | 444 CPUID_STDEXT2_VPCLMULQDQ; 445 regs[3] &= CPUID_STDEXT3_MD_CLEAR; 446 447 /* Advertise RDPID if it is enabled. */ 448 error = vm_get_capability(vcpu, VM_CAP_RDPID, 449 &enable_rdpid); 450 if (error == 0 && enable_rdpid) 451 regs[2] |= CPUID_STDEXT2_RDPID; 452 453 /* Advertise INVPCID if it is enabled. */ 454 error = vm_get_capability(vcpu, 455 VM_CAP_ENABLE_INVPCID, &enable_invpcid); 456 if (error == 0 && enable_invpcid) 457 regs[1] |= CPUID_STDEXT_INVPCID; 458 } 459 break; 460 461 case CPUID_0000_0006: 462 regs[0] = CPUTPM1_ARAT; 463 regs[1] = 0; 464 regs[2] = 0; 465 regs[3] = 0; 466 break; 467 468 case CPUID_0000_000A: 469 /* 470 * Handle the access, but report 0 for 471 * all options 472 */ 473 regs[0] = 0; 474 regs[1] = 0; 475 regs[2] = 0; 476 regs[3] = 0; 477 break; 478 479 case CPUID_0000_000B: 480 /* 481 * Intel processor topology enumeration 482 */ 483 if (vmm_is_intel()) { 484 vm_get_topology(vm, &sockets, &cores, &threads, 485 &maxcpus); 486 if (param == 0) { 487 logical_cpus = threads; 488 width = log2(logical_cpus); 489 level = CPUID_TYPE_SMT; 490 x2apic_id = vcpu_id; 491 } 492 493 if (param == 1) { 494 logical_cpus = threads * cores; 495 width = log2(logical_cpus); 496 level = CPUID_TYPE_CORE; 497 x2apic_id = vcpu_id; 498 } 499 500 if (!cpuid_leaf_b || param >= 2) { 501 width = 0; 502 logical_cpus = 0; 503 level = 0; 504 x2apic_id = 0; 505 } 506 507 regs[0] = width & 0x1f; 508 regs[1] = logical_cpus & 0xffff; 509 regs[2] = (level << 8) | (param & 0xff); 510 regs[3] = x2apic_id; 511 } else { 512 regs[0] = 0; 513 regs[1] = 0; 514 regs[2] = 0; 515 regs[3] = 0; 516 } 517 break; 518 519 case CPUID_0000_000D: 520 limits = vmm_get_xsave_limits(); 521 if (!limits->xsave_enabled) { 522 regs[0] = 0; 523 regs[1] = 0; 524 regs[2] = 0; 525 regs[3] = 0; 526 break; 527 } 528 529 cpuid_count(func, param, regs); 530 switch (param) { 531 case 0: 532 /* 533 * Only permit the guest to use bits 534 * that are active in the host in 535 * %xcr0. Also, claim that the 536 * maximum save area size is 537 * equivalent to the host's current 538 * save area size. Since this runs 539 * "inside" of vmrun(), it runs with 540 * the guest's xcr0, so the current 541 * save area size is correct as-is. 542 */ 543 regs[0] &= limits->xcr0_allowed; 544 regs[2] = limits->xsave_max_size; 545 regs[3] &= (limits->xcr0_allowed >> 32); 546 break; 547 case 1: 548 /* Only permit XSAVEOPT. */ 549 regs[0] &= CPUID_EXTSTATE_XSAVEOPT; 550 regs[1] = 0; 551 regs[2] = 0; 552 regs[3] = 0; 553 break; 554 default: 555 /* 556 * If the leaf is for a permitted feature, 557 * pass through as-is, otherwise return 558 * all zeroes. 559 */ 560 if (!(limits->xcr0_allowed & (1ul << param))) { 561 regs[0] = 0; 562 regs[1] = 0; 563 regs[2] = 0; 564 regs[3] = 0; 565 } 566 break; 567 } 568 break; 569 570 case CPUID_0000_000F: 571 case CPUID_0000_0010: 572 /* 573 * Do not report any Resource Director Technology 574 * capabilities. Exposing control of cache or memory 575 * controller resource partitioning to the guest is not 576 * at all sensible. 577 * 578 * This is already hidden at a high level by masking of 579 * leaf 0x7. Even still, a guest may look here for 580 * detailed capability information. 581 */ 582 regs[0] = 0; 583 regs[1] = 0; 584 regs[2] = 0; 585 regs[3] = 0; 586 break; 587 588 case CPUID_0000_0015: 589 /* 590 * Don't report CPU TSC/Crystal ratio and clock 591 * values since guests may use these to derive the 592 * local APIC frequency.. 593 */ 594 regs[0] = 0; 595 regs[1] = 0; 596 regs[2] = 0; 597 regs[3] = 0; 598 break; 599 600 case 0x40000000: 601 regs[0] = CPUID_VM_HIGH; 602 bcopy(bhyve_id, ®s[1], 4); 603 bcopy(bhyve_id + 4, ®s[2], 4); 604 bcopy(bhyve_id + 8, ®s[3], 4); 605 break; 606 607 default: 608 default_leaf: 609 /* 610 * The leaf value has already been clamped so 611 * simply pass this through, keeping count of 612 * how many unhandled leaf values have been seen. 613 */ 614 atomic_add_long(&bhyve_xcpuids, 1); 615 cpuid_count(func, param, regs); 616 break; 617 } 618 619 /* 620 * CPUID clears the upper 32-bits of the long-mode registers. 621 */ 622 *rax = regs[0]; 623 *rbx = regs[1]; 624 *rcx = regs[2]; 625 *rdx = regs[3]; 626 627 return (1); 628 } 629 630 bool 631 vm_cpuid_capability(struct vcpu *vcpu, enum vm_cpuid_capability cap) 632 { 633 bool rv; 634 635 KASSERT(cap > 0 && cap < VCC_LAST, ("%s: invalid vm_cpu_capability %d", 636 __func__, cap)); 637 638 /* 639 * Simply passthrough the capabilities of the host cpu for now. 640 */ 641 rv = false; 642 switch (cap) { 643 case VCC_NO_EXECUTE: 644 if (amd_feature & AMDID_NX) 645 rv = true; 646 break; 647 case VCC_FFXSR: 648 if (amd_feature & AMDID_FFXSR) 649 rv = true; 650 break; 651 case VCC_TCE: 652 if (amd_feature2 & AMDID2_TCE) 653 rv = true; 654 break; 655 default: 656 panic("%s: unknown vm_cpu_capability %d", __func__, cap); 657 } 658 return (rv); 659 } 660 661 int 662 vm_rdmtrr(struct vm_mtrr *mtrr, u_int num, uint64_t *val) 663 { 664 switch (num) { 665 case MSR_MTRRcap: 666 *val = MTRR_CAP_WC | MTRR_CAP_FIXED | VMM_MTRR_VAR_MAX; 667 break; 668 case MSR_MTRRdefType: 669 *val = mtrr->def_type; 670 break; 671 case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7: 672 *val = mtrr->fixed4k[num - MSR_MTRR4kBase]; 673 break; 674 case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1: 675 *val = mtrr->fixed16k[num - MSR_MTRR16kBase]; 676 break; 677 case MSR_MTRR64kBase: 678 *val = mtrr->fixed64k; 679 break; 680 case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1: { 681 u_int offset = num - MSR_MTRRVarBase; 682 if (offset % 2 == 0) { 683 *val = mtrr->var[offset / 2].base; 684 } else { 685 *val = mtrr->var[offset / 2].mask; 686 } 687 break; 688 } 689 default: 690 return (-1); 691 } 692 693 return (0); 694 } 695 696 int 697 vm_wrmtrr(struct vm_mtrr *mtrr, u_int num, uint64_t val) 698 { 699 switch (num) { 700 case MSR_MTRRcap: 701 /* MTRRCAP is read only */ 702 return (-1); 703 case MSR_MTRRdefType: 704 if (val & ~VMM_MTRR_DEF_MASK) { 705 /* generate #GP on writes to reserved fields */ 706 return (-1); 707 } 708 mtrr->def_type = val; 709 break; 710 case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7: 711 mtrr->fixed4k[num - MSR_MTRR4kBase] = val; 712 break; 713 case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1: 714 mtrr->fixed16k[num - MSR_MTRR16kBase] = val; 715 break; 716 case MSR_MTRR64kBase: 717 mtrr->fixed64k = val; 718 break; 719 case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1: { 720 u_int offset = num - MSR_MTRRVarBase; 721 if (offset % 2 == 0) { 722 if (val & ~VMM_MTRR_PHYSBASE_MASK) { 723 /* generate #GP on writes to reserved fields */ 724 return (-1); 725 } 726 mtrr->var[offset / 2].base = val; 727 } else { 728 if (val & ~VMM_MTRR_PHYSMASK_MASK) { 729 /* generate #GP on writes to reserved fields */ 730 return (-1); 731 } 732 mtrr->var[offset / 2].mask = val; 733 } 734 break; 735 } 736 default: 737 return (-1); 738 } 739 740 return (0); 741 } 742