* [PATCH] KVM: VMX: Allocate struct pt_desc only when PT is virtualized for guests @ 2026-11-06 7:34 Peng Hao 2026-11-06 16:39 ` Sean Christopherson 0 siblings, 1 reply; 3+ messages in thread From: Peng Hao @ 2026-11-06 7:34 UTC (permalink / raw) To: pbonzini, seanjc; +Cc: kvm struct pt_desc is only used when pt_mode is PT_MODE_HOST_GUEST, so allocate it on demand instead of embedding it in struct vcpu_vmx. This shrinks struct vcpu_vmx from 8384 to 8128 bytes. Signed-off-by: Peng Hao <flyingpeng@tencent.com> --- arch/x86/kvm/vmx/nested.c | 2 +- arch/x86/kvm/vmx/vmx.c | 167 ++++++++++++++++++++------------------ arch/x86/kvm/vmx/vmx.h | 3 +- 3 files changed, 91 insertions(+), 81 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index f0d75f5a968c..d6d068778741 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -5456,7 +5456,7 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu) vmx->nested.vmxon = true; if (vmx_pt_mode_is_host_guest()) { - vmx->pt_desc.guest.ctl = 0; + vmx->pt_desc->guest.ctl = 0; pt_update_intercept_for_msr(vcpu); } diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index b341af4c6057..bc046238e90c 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -1234,7 +1234,7 @@ static unsigned long segment_base(u16 selector) static inline bool pt_can_write_msr(struct vcpu_vmx *vmx) { return vmx_pt_mode_is_host_guest() && - !(vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN); + !(vmx->pt_desc->guest.ctl & RTIT_CTL_TRACEEN); } static inline bool pt_output_base_valid(struct kvm_vcpu *vcpu, u64 base) @@ -1280,11 +1280,11 @@ static void pt_guest_enter(struct vcpu_vmx *vmx) * GUEST_IA32_RTIT_CTL is already set in the VMCS. * Save host state before VM entry. */ - rdmsrq(MSR_IA32_RTIT_CTL, vmx->pt_desc.host.ctl); - if (vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN) { + rdmsrq(MSR_IA32_RTIT_CTL, vmx->pt_desc->host.ctl); + if (vmx->pt_desc->guest.ctl & RTIT_CTL_TRACEEN) { wrmsrq(MSR_IA32_RTIT_CTL, 0); - pt_save_msr(&vmx->pt_desc.host, vmx->pt_desc.num_address_ranges); - pt_load_msr(&vmx->pt_desc.guest, vmx->pt_desc.num_address_ranges); + pt_save_msr(&vmx->pt_desc->host, vmx->pt_desc->num_address_ranges); + pt_load_msr(&vmx->pt_desc->guest, vmx->pt_desc->num_address_ranges); } } @@ -1293,17 +1293,17 @@ static void pt_guest_exit(struct vcpu_vmx *vmx) if (vmx_pt_mode_is_system()) return; - if (vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN) { - pt_save_msr(&vmx->pt_desc.guest, vmx->pt_desc.num_address_ranges); - pt_load_msr(&vmx->pt_desc.host, vmx->pt_desc.num_address_ranges); + if (vmx->pt_desc->guest.ctl & RTIT_CTL_TRACEEN) { + pt_save_msr(&vmx->pt_desc->guest, vmx->pt_desc->num_address_ranges); + pt_load_msr(&vmx->pt_desc->host, vmx->pt_desc->num_address_ranges); } /* * KVM requires VM_EXIT_CLEAR_IA32_RTIT_CTL to expose PT to the guest, * i.e. RTIT_CTL is always cleared on VM-Exit. Restore it if necessary. */ - if (vmx->pt_desc.host.ctl) - wrmsrq(MSR_IA32_RTIT_CTL, vmx->pt_desc.host.ctl); + if (vmx->pt_desc->host.ctl) + wrmsrq(MSR_IA32_RTIT_CTL, vmx->pt_desc->host.ctl); } void vmx_set_host_fs_gs(struct vmcs_host_state *host, u16 fs_sel, u16 gs_sel, @@ -1717,16 +1717,16 @@ static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) * Any MSR write that attempts to change bits marked reserved will * case a #GP fault. */ - if (data & vmx->pt_desc.ctl_bitmask) + if (data & vmx->pt_desc->ctl_bitmask) return 1; /* * Any attempt to modify IA32_RTIT_CTL while TraceEn is set will * result in a #GP unless the same write also clears TraceEn. */ - if ((vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN) && + if ((vmx->pt_desc->guest.ctl & RTIT_CTL_TRACEEN) && (data & RTIT_CTL_TRACEEN) && - data != vmx->pt_desc.guest.ctl) + data != vmx->pt_desc->guest.ctl) return 1; /* @@ -1736,7 +1736,7 @@ static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) */ if ((data & RTIT_CTL_TRACEEN) && !(data & RTIT_CTL_TOPA) && !(data & RTIT_CTL_FABRIC_EN) && - !intel_pt_validate_cap(vmx->pt_desc.caps, + !intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_single_range_output)) return 1; @@ -1744,19 +1744,19 @@ static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) * MTCFreq, CycThresh and PSBFreq encodings check, any MSR write that * utilize encodings marked reserved will cause a #GP fault. */ - value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc_periods); - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc) && + value = intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_mtc_periods); + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_mtc) && !test_bit((data & RTIT_CTL_MTC_RANGE) >> RTIT_CTL_MTC_RANGE_OFFSET, &value)) return 1; - value = intel_pt_validate_cap(vmx->pt_desc.caps, + value = intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_cycle_thresholds); - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_psb_cyc) && !test_bit((data & RTIT_CTL_CYC_THRESH) >> RTIT_CTL_CYC_THRESH_OFFSET, &value)) return 1; - value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_periods); - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && + value = intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_psb_periods); + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_psb_cyc) && !test_bit((data & RTIT_CTL_PSB_FREQ) >> RTIT_CTL_PSB_FREQ_OFFSET, &value)) return 1; @@ -1766,16 +1766,16 @@ static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) * cause a #GP fault. */ value = (data & RTIT_CTL_ADDR0) >> RTIT_CTL_ADDR0_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 1)) || (value > 2)) + if ((value && (vmx->pt_desc->num_address_ranges < 1)) || (value > 2)) return 1; value = (data & RTIT_CTL_ADDR1) >> RTIT_CTL_ADDR1_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 2)) || (value > 2)) + if ((value && (vmx->pt_desc->num_address_ranges < 2)) || (value > 2)) return 1; value = (data & RTIT_CTL_ADDR2) >> RTIT_CTL_ADDR2_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 3)) || (value > 2)) + if ((value && (vmx->pt_desc->num_address_ranges < 3)) || (value > 2)) return 1; value = (data & RTIT_CTL_ADDR3) >> RTIT_CTL_ADDR3_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 4)) || (value > 2)) + if ((value && (vmx->pt_desc->num_address_ranges < 4)) || (value > 2)) return 1; return 0; @@ -2219,47 +2219,47 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_IA32_RTIT_CTL: if (!vmx_pt_mode_is_host_guest()) return 1; - msr_info->data = vmx->pt_desc.guest.ctl; + msr_info->data = vmx->pt_desc->guest.ctl; break; case MSR_IA32_RTIT_STATUS: if (!vmx_pt_mode_is_host_guest()) return 1; - msr_info->data = vmx->pt_desc.guest.status; + msr_info->data = vmx->pt_desc->guest.status; break; case MSR_IA32_RTIT_CR3_MATCH: if (!vmx_pt_mode_is_host_guest() || - !intel_pt_validate_cap(vmx->pt_desc.caps, + !intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_cr3_filtering)) return 1; - msr_info->data = vmx->pt_desc.guest.cr3_match; + msr_info->data = vmx->pt_desc->guest.cr3_match; break; case MSR_IA32_RTIT_OUTPUT_BASE: if (!vmx_pt_mode_is_host_guest() || - (!intel_pt_validate_cap(vmx->pt_desc.caps, + (!intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_topa_output) && - !intel_pt_validate_cap(vmx->pt_desc.caps, + !intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_single_range_output))) return 1; - msr_info->data = vmx->pt_desc.guest.output_base; + msr_info->data = vmx->pt_desc->guest.output_base; break; case MSR_IA32_RTIT_OUTPUT_MASK: if (!vmx_pt_mode_is_host_guest() || - (!intel_pt_validate_cap(vmx->pt_desc.caps, + (!intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_topa_output) && - !intel_pt_validate_cap(vmx->pt_desc.caps, + !intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_single_range_output))) return 1; - msr_info->data = vmx->pt_desc.guest.output_mask; + msr_info->data = vmx->pt_desc->guest.output_mask; break; case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; if (!vmx_pt_mode_is_host_guest() || - (index >= 2 * vmx->pt_desc.num_address_ranges)) + (index >= 2 * vmx->pt_desc->num_address_ranges)) return 1; if (index % 2) - msr_info->data = vmx->pt_desc.guest.addr_b[index / 2]; + msr_info->data = vmx->pt_desc->guest.addr_b[index / 2]; else - msr_info->data = vmx->pt_desc.guest.addr_a[index / 2]; + msr_info->data = vmx->pt_desc->guest.addr_a[index / 2]; break; case MSR_IA32_S_CET: msr_info->data = vmcs_readl(GUEST_S_CET); @@ -2535,7 +2535,7 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vmx->nested.vmxon) return 1; vmcs_write64(GUEST_IA32_RTIT_CTL, data); - vmx->pt_desc.guest.ctl = data; + vmx->pt_desc->guest.ctl = data; pt_update_intercept_for_msr(vcpu); break; case MSR_IA32_RTIT_STATUS: @@ -2543,50 +2543,50 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; if (data & MSR_IA32_RTIT_STATUS_MASK) return 1; - vmx->pt_desc.guest.status = data; + vmx->pt_desc->guest.status = data; break; case MSR_IA32_RTIT_CR3_MATCH: if (!pt_can_write_msr(vmx)) return 1; - if (!intel_pt_validate_cap(vmx->pt_desc.caps, + if (!intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_cr3_filtering)) return 1; - vmx->pt_desc.guest.cr3_match = data; + vmx->pt_desc->guest.cr3_match = data; break; case MSR_IA32_RTIT_OUTPUT_BASE: if (!pt_can_write_msr(vmx)) return 1; - if (!intel_pt_validate_cap(vmx->pt_desc.caps, + if (!intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_topa_output) && - !intel_pt_validate_cap(vmx->pt_desc.caps, + !intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_single_range_output)) return 1; if (!pt_output_base_valid(vcpu, data)) return 1; - vmx->pt_desc.guest.output_base = data; + vmx->pt_desc->guest.output_base = data; break; case MSR_IA32_RTIT_OUTPUT_MASK: if (!pt_can_write_msr(vmx)) return 1; - if (!intel_pt_validate_cap(vmx->pt_desc.caps, + if (!intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_topa_output) && - !intel_pt_validate_cap(vmx->pt_desc.caps, + !intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_single_range_output)) return 1; - vmx->pt_desc.guest.output_mask = data; + vmx->pt_desc->guest.output_mask = data; break; case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: if (!pt_can_write_msr(vmx)) return 1; index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; - if (index >= 2 * vmx->pt_desc.num_address_ranges) + if (index >= 2 * vmx->pt_desc->num_address_ranges) return 1; if (is_noncanonical_msr_address(data, vcpu)) return 1; if (index % 2) - vmx->pt_desc.guest.addr_b[index / 2] = data; + vmx->pt_desc->guest.addr_b[index / 2] = data; else - vmx->pt_desc.guest.addr_a[index / 2] = data; + vmx->pt_desc->guest.addr_a[index / 2] = data; break; case MSR_IA32_S_CET: vmcs_writel(GUEST_S_CET, data); @@ -4222,14 +4222,14 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); - bool flag = !(vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN); + bool flag = !(vmx->pt_desc->guest.ctl & RTIT_CTL_TRACEEN); u32 i; vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_STATUS, MSR_TYPE_RW, flag); vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_BASE, MSR_TYPE_RW, flag); vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_MASK, MSR_TYPE_RW, flag); vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_CR3_MATCH, MSR_TYPE_RW, flag); - for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) { + for (i = 0; i < vmx->pt_desc->num_address_ranges; i++) { vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_A + i * 2, MSR_TYPE_RW, flag); vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_B + i * 2, MSR_TYPE_RW, flag); } @@ -4995,9 +4995,9 @@ static void init_vmcs(struct kvm_vcpu *vcpu) vmx_write_encls_bitmap(vcpu, NULL); if (vmx_pt_mode_is_host_guest()) { - memset(&vmx->pt_desc, 0, sizeof(vmx->pt_desc)); + memset(vmx->pt_desc, 0, sizeof(*vmx->pt_desc)); /* Bit[6~0] are forced to 1, writes are ignored. */ - vmx->pt_desc.guest.output_mask = 0x7F; + vmx->pt_desc->guest.output_mask = 0x7F; vmcs_write64(GUEST_IA32_RTIT_CTL, 0); } @@ -7517,6 +7517,7 @@ void vmx_vcpu_free(struct kvm_vcpu *vcpu) nested_vmx_free_vcpu(vcpu); free_loaded_vmcs(vmx->loaded_vmcs); free_page((unsigned long)vmx->ve_info); + kfree(vmx->pt_desc); if (vmx_can_use_ipiv(vcpu)) WRITE_ONCE(to_kvm_vmx(vcpu->kvm)->pid_table[vcpu->vcpu_id], 0); @@ -7594,6 +7595,12 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu) } err = -ENOMEM; + if (vmx_pt_mode_is_host_guest()) { + vmx->pt_desc = kzalloc_obj(*vmx->pt_desc, GFP_KERNEL_ACCOUNT); + if (!vmx->pt_desc) + goto free_vmcs; + } + if (vmcs_config.cpu_based_2nd_exec_ctrl & SECONDARY_EXEC_EPT_VIOLATION_VE) { struct page *page; @@ -7602,7 +7609,7 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu) /* ve_info must be page aligned. */ page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); if (!page) - goto free_vmcs; + goto free_pt_desc; vmx->ve_info = page_to_virt(page); } @@ -7613,6 +7620,8 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu) return 0; +free_pt_desc: + kfree(vmx->pt_desc); free_vmcs: free_loaded_vmcs(vmx->loaded_vmcs); free_pml: @@ -7765,18 +7774,18 @@ static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) best = kvm_find_cpuid_entry_index(vcpu, 0x14, i); if (!best) return; - vmx->pt_desc.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM] = best->eax; - vmx->pt_desc.caps[CPUID_EBX + i*PT_CPUID_REGS_NUM] = best->ebx; - vmx->pt_desc.caps[CPUID_ECX + i*PT_CPUID_REGS_NUM] = best->ecx; - vmx->pt_desc.caps[CPUID_EDX + i*PT_CPUID_REGS_NUM] = best->edx; + vmx->pt_desc->caps[CPUID_EAX + i*PT_CPUID_REGS_NUM] = best->eax; + vmx->pt_desc->caps[CPUID_EBX + i*PT_CPUID_REGS_NUM] = best->ebx; + vmx->pt_desc->caps[CPUID_ECX + i*PT_CPUID_REGS_NUM] = best->ecx; + vmx->pt_desc->caps[CPUID_EDX + i*PT_CPUID_REGS_NUM] = best->edx; } /* Get the number of configurable Address Ranges for filtering */ - vmx->pt_desc.num_address_ranges = intel_pt_validate_cap(vmx->pt_desc.caps, + vmx->pt_desc->num_address_ranges = intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_num_address_ranges); /* Initialize and clear the no dependency bits */ - vmx->pt_desc.ctl_bitmask = ~(RTIT_CTL_TRACEEN | RTIT_CTL_OS | + vmx->pt_desc->ctl_bitmask = ~(RTIT_CTL_TRACEEN | RTIT_CTL_OS | RTIT_CTL_USR | RTIT_CTL_TSC_EN | RTIT_CTL_DISRETC | RTIT_CTL_BRANCH_EN); @@ -7784,44 +7793,44 @@ static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) * If CPUID.(EAX=14H,ECX=0):EBX[0]=1 CR3Filter can be set otherwise * will inject an #GP */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_cr3_filtering)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_CR3EN; + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_cr3_filtering)) + vmx->pt_desc->ctl_bitmask &= ~RTIT_CTL_CR3EN; /* * If CPUID.(EAX=14H,ECX=0):EBX[1]=1 CYCEn, CycThresh and * PSBFreq can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc)) - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_CYCLEACC | + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_psb_cyc)) + vmx->pt_desc->ctl_bitmask &= ~(RTIT_CTL_CYCLEACC | RTIT_CTL_CYC_THRESH | RTIT_CTL_PSB_FREQ); /* * If CPUID.(EAX=14H,ECX=0):EBX[3]=1 MTCEn and MTCFreq can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc)) - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_MTC_EN | + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_mtc)) + vmx->pt_desc->ctl_bitmask &= ~(RTIT_CTL_MTC_EN | RTIT_CTL_MTC_RANGE); /* If CPUID.(EAX=14H,ECX=0):EBX[4]=1 FUPonPTW and PTWEn can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_ptwrite)) - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_FUP_ON_PTW | + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_ptwrite)) + vmx->pt_desc->ctl_bitmask &= ~(RTIT_CTL_FUP_ON_PTW | RTIT_CTL_PTW_EN); /* If CPUID.(EAX=14H,ECX=0):EBX[5]=1 PwrEvEn can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_power_event_trace)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_PWR_EVT_EN; + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_power_event_trace)) + vmx->pt_desc->ctl_bitmask &= ~RTIT_CTL_PWR_EVT_EN; /* If CPUID.(EAX=14H,ECX=0):ECX[0]=1 ToPA can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_topa_output)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_TOPA; + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_topa_output)) + vmx->pt_desc->ctl_bitmask &= ~RTIT_CTL_TOPA; /* If CPUID.(EAX=14H,ECX=0):ECX[3]=1 FabricEn can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_output_subsys)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_FABRIC_EN; + if (intel_pt_validate_cap(vmx->pt_desc->caps, PT_CAP_output_subsys)) + vmx->pt_desc->ctl_bitmask &= ~RTIT_CTL_FABRIC_EN; /* unmask address range configure area */ - for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) - vmx->pt_desc.ctl_bitmask &= ~(0xfULL << (32 + i * 4)); + for (i = 0; i < vmx->pt_desc->num_address_ranges; i++) + vmx->pt_desc->ctl_bitmask &= ~(0xfULL << (32 + i * 4)); } void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) @@ -7854,8 +7863,8 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) if (guest_cpu_cap_has(vcpu, X86_FEATURE_VMX)) nested_vmx_cr_fixed1_bits_update(vcpu); - if (boot_cpu_has(X86_FEATURE_INTEL_PT) && - guest_cpu_cap_has(vcpu, X86_FEATURE_INTEL_PT)) + if (vmx_pt_mode_is_host_guest() && boot_cpu_has(X86_FEATURE_INTEL_PT) && + guest_cpu_cap_has(vcpu, X86_FEATURE_INTEL_PT)) update_intel_pt_cfg(vcpu); if (boot_cpu_has(X86_FEATURE_RTM)) { diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 6c9dd5b2365d..54de80390776 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -280,7 +280,8 @@ struct vcpu_vmx { u64 msr_ia32_mcu_opt_ctrl; bool disable_fb_clear; - struct pt_desc pt_desc; + /* Only allocated if pt_mode == PT_MODE_HOST_GUEST. */ + struct pt_desc *pt_desc; struct lbr_desc lbr_desc; /* ve_info must be page aligned. */ -- 2.43.7 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] KVM: VMX: Allocate struct pt_desc only when PT is virtualized for guests 2026-11-06 7:34 [PATCH] KVM: VMX: Allocate struct pt_desc only when PT is virtualized for guests Peng Hao @ 2026-11-06 16:39 ` Sean Christopherson 2026-11-11 1:54 ` Hao Peng 0 siblings, 1 reply; 3+ messages in thread From: Sean Christopherson @ 2026-11-06 16:39 UTC (permalink / raw) To: Peng Hao; +Cc: pbonzini, kvm On Fri, Nov 06, 2026, Peng Hao wrote: > struct pt_desc is only used when pt_mode is PT_MODE_HOST_GUEST, so > allocate it on demand instead of embedding it in struct vcpu_vmx. > > This shrinks struct vcpu_vmx from 8384 to 8128 bytes. Hrm, I don't think I want to do this to save "only" 256 bytes. I assume the goal is to get back down to an order-1 allocation, but vcpu_vmx is sooo close that shaving off 256 bytes is only going to keep it order-1 for a kernel release or two. And for "full" configs, e.g. with Xen support, vcpu_vmx is ~9088 bytes. If we want to get vCPUs back to order-1 allocation with any chance of keeping them there, I think we need to aim for ~6-7 KiB, but without adding unnecessary layers of indirection. E.g. kvm_vcpu.stats is a whopping 1104 bytes, but it's accessed in a ton of hot paths, so I wouldn't want to allocate that separately. Similarly, we could dynamically allocate vcpu_vmx.nested, but I don't know that I'd want to add that extra pointer chase just to get at a variety of flags. Potential ideas: 1. vcpu_vmx.rmode (208) - In practice, it's only used by CPUs launched before Westmere (2010), and like PT, it's guarded by a module param so it's easy to dynamically allocate. 2. kvm_vcpu_arch.pmu (912) - Can be allocated if and only if the vCPU has a PMU, e.g. in kvm_pmu_refresh(), or based on the module param. I'd be somehwat worried about regressing hot paths though. On the other hand, we could throw pt_desc and lbr_desc into kvm_pmu to get a total of 1208 bytes in savings. 3. kvm_vcpu_arch.xen (680) - Only impacts a limited set of configs, but at a glance, this one would be trivially easy to convert, and if distros enable CONFIG_KVM_XEN, would provide meaningful savings for a lot of deployments. 4. kvm_vcpu_arch.apf.gfns (512) - Would need to be unconditionally allocated, but its power-of-2 size makes it well suited for a kmem cache, and for many, many VMs, this will almost never be used. If we did all of the above, that would save 2608 bytes for CONFIG_KVM_XEN=y and 1928 bytes for CONFIG_KVM_XEN=n. That would put vcpu_vmx around 6.4KiB, which would give us some room before we cross the order-1 boundary (again). The other option for pt_desc in particular would be to simply rip out all of the code, as it's been buried behind CONFIG_BROKEN=y for a few years now. Actually, duh, the less committing thing to do is to compile out the code. I think it'd still be worth doing at least some of the above changes, but hard disabling PT virtualization seems like a no brainer. E.g. over a few patches to isolate the code movement. I'll post a miniseries. diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index 1719059570fb..243307328adf 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -151,6 +151,9 @@ config KVM_INTEL_TDX If unsure, say N. +config KVM_INTEL_PT + def_bool y if BROKEN + config KVM_AMD tristate "KVM for AMD processors support" depends on KVM && (CPU_SUP_AMD || CPU_SUP_HYGON) diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c index e91650af44c2..a69020c01afd 100644 --- a/arch/x86/kvm/msrs.c +++ b/arch/x86/kvm/msrs.c @@ -2596,6 +2596,7 @@ static void kvm_probe_msr_to_save(u32 msr_index) if (!kvm_cpu_cap_has(X86_FEATURE_WAITPKG)) return; break; +#ifdef CONFIG_KVM_INTEL_PT case MSR_IA32_RTIT_CTL: case MSR_IA32_RTIT_STATUS: if (!kvm_cpu_cap_has(X86_FEATURE_INTEL_PT)) @@ -2619,6 +2620,7 @@ static void kvm_probe_msr_to_save(u32 msr_index) intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2)) return; break; +#endif case MSR_ARCH_PERFMON_PERFCTR0 ... MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1: if (msr_index - MSR_ARCH_PERFMON_PERFCTR0 >= diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index 810119167f79..604cc376ca5a 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -17,11 +17,16 @@ extern bool __read_mostly enable_ept_ad_bits; extern bool __read_mostly enable_cet; extern bool __read_mostly enable_pml; extern bool __read_mostly enable_mbec; -extern int __read_mostly pt_mode; #define PT_MODE_SYSTEM 0 #define PT_MODE_HOST_GUEST 1 +#ifdef CONFIG_KVM_INTEL_PT +extern int __read_mostly pt_mode; +#else +#define pt_mode PT_MODE_SYSTEM +#endif + struct nested_vmx_msrs { /* * We only store the "true" versions of the VMX capability MSRs. We diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index f0d75f5a968c..4bc0c1b743d8 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -5455,10 +5455,12 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu) vmx->nested.vmcs02_initialized = false; vmx->nested.vmxon = true; +#ifdef CONFIG_KVM_INTEL_PT if (vmx_pt_mode_is_host_guest()) { vmx->pt_desc.guest.ctl = 0; pt_update_intercept_for_msr(vcpu); } +#endif return 0; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index e6a61514e797..bd1ea5f16c78 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -210,8 +210,8 @@ static unsigned int ple_window_max = KVM_VMX_DEFAULT_PLE_WINDOW_MAX; module_param(ple_window_max, uint, 0444); /* Default is SYSTEM mode, 1 for host-guest mode (which is BROKEN) */ +#ifdef CONFIG_KVM_INTEL_PT int __read_mostly pt_mode = PT_MODE_SYSTEM; -#ifdef CONFIG_BROKEN module_param(pt_mode, int, S_IRUGO); #endif @@ -1231,6 +1231,165 @@ static unsigned long segment_base(u16 selector) } #endif +#ifdef CONFIG_KVM_INTEL_PT +static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + unsigned long value; + + /* + * Any MSR write that attempts to change bits marked reserved will + * case a #GP fault. + */ + if (data & vmx->pt_desc.ctl_bitmask) + return 1; + + /* + * Any attempt to modify IA32_RTIT_CTL while TraceEn is set will + * result in a #GP unless the same write also clears TraceEn. + */ + if ((vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN) && + (data & RTIT_CTL_TRACEEN) && + data != vmx->pt_desc.guest.ctl) + return 1; + + /* + * WRMSR to IA32_RTIT_CTL that sets TraceEn but clears this bit + * and FabricEn would cause #GP, if + * CPUID.(EAX=14H, ECX=0):ECX.SNGLRGNOUT[bit 2] = 0 + */ + if ((data & RTIT_CTL_TRACEEN) && !(data & RTIT_CTL_TOPA) && + !(data & RTIT_CTL_FABRIC_EN) && + !intel_pt_validate_cap(vmx->pt_desc.caps, + PT_CAP_single_range_output)) + return 1; + + /* + * MTCFreq, CycThresh and PSBFreq encodings check, any MSR write that + * utilize encodings marked reserved will cause a #GP fault. + */ + value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc_periods); + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc) && + !test_bit((data & RTIT_CTL_MTC_RANGE) >> + RTIT_CTL_MTC_RANGE_OFFSET, &value)) + return 1; + value = intel_pt_validate_cap(vmx->pt_desc.caps, + PT_CAP_cycle_thresholds); + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && + !test_bit((data & RTIT_CTL_CYC_THRESH) >> + RTIT_CTL_CYC_THRESH_OFFSET, &value)) + return 1; + value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_periods); + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && + !test_bit((data & RTIT_CTL_PSB_FREQ) >> + RTIT_CTL_PSB_FREQ_OFFSET, &value)) + return 1; + + /* + * If ADDRx_CFG is reserved or the encodings is >2 will + * cause a #GP fault. + */ + value = (data & RTIT_CTL_ADDR0) >> RTIT_CTL_ADDR0_OFFSET; + if ((value && (vmx->pt_desc.num_address_ranges < 1)) || (value > 2)) + return 1; + value = (data & RTIT_CTL_ADDR1) >> RTIT_CTL_ADDR1_OFFSET; + if ((value && (vmx->pt_desc.num_address_ranges < 2)) || (value > 2)) + return 1; + value = (data & RTIT_CTL_ADDR2) >> RTIT_CTL_ADDR2_OFFSET; + if ((value && (vmx->pt_desc.num_address_ranges < 3)) || (value > 2)) + return 1; + value = (data & RTIT_CTL_ADDR3) >> RTIT_CTL_ADDR3_OFFSET; + if ((value && (vmx->pt_desc.num_address_ranges < 4)) || (value > 2)) + return 1; + + return 0; +} + +static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + struct kvm_cpuid_entry2 *best = NULL; + int i; + + for (i = 0; i < PT_CPUID_LEAVES; i++) { + best = kvm_find_cpuid_entry_index(vcpu, 0x14, i); + if (!best) + return; + vmx->pt_desc.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM] = best->eax; + vmx->pt_desc.caps[CPUID_EBX + i*PT_CPUID_REGS_NUM] = best->ebx; + vmx->pt_desc.caps[CPUID_ECX + i*PT_CPUID_REGS_NUM] = best->ecx; + vmx->pt_desc.caps[CPUID_EDX + i*PT_CPUID_REGS_NUM] = best->edx; + } + + /* Get the number of configurable Address Ranges for filtering */ + vmx->pt_desc.num_address_ranges = intel_pt_validate_cap(vmx->pt_desc.caps, + PT_CAP_num_address_ranges); + + /* Initialize and clear the no dependency bits */ + vmx->pt_desc.ctl_bitmask = ~(RTIT_CTL_TRACEEN | RTIT_CTL_OS | + RTIT_CTL_USR | RTIT_CTL_TSC_EN | RTIT_CTL_DISRETC | + RTIT_CTL_BRANCH_EN); + + /* + * If CPUID.(EAX=14H,ECX=0):EBX[0]=1 CR3Filter can be set otherwise + * will inject an #GP + */ + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_cr3_filtering)) + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_CR3EN; + + /* + * If CPUID.(EAX=14H,ECX=0):EBX[1]=1 CYCEn, CycThresh and + * PSBFreq can be set + */ + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc)) + vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_CYCLEACC | + RTIT_CTL_CYC_THRESH | RTIT_CTL_PSB_FREQ); + + /* + * If CPUID.(EAX=14H,ECX=0):EBX[3]=1 MTCEn and MTCFreq can be set + */ + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc)) + vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_MTC_EN | + RTIT_CTL_MTC_RANGE); + + /* If CPUID.(EAX=14H,ECX=0):EBX[4]=1 FUPonPTW and PTWEn can be set */ + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_ptwrite)) + vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_FUP_ON_PTW | + RTIT_CTL_PTW_EN); + + /* If CPUID.(EAX=14H,ECX=0):EBX[5]=1 PwrEvEn can be set */ + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_power_event_trace)) + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_PWR_EVT_EN; + + /* If CPUID.(EAX=14H,ECX=0):ECX[0]=1 ToPA can be set */ + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_topa_output)) + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_TOPA; + + /* If CPUID.(EAX=14H,ECX=0):ECX[3]=1 FabricEn can be set */ + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_output_subsys)) + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_FABRIC_EN; + + /* unmask address range configure area */ + for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) + vmx->pt_desc.ctl_bitmask &= ~(0xfULL << (32 + i * 4)); +} + +void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + bool flag = !(vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN); + u32 i; + + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_STATUS, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_BASE, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_MASK, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_CR3_MATCH, MSR_TYPE_RW, flag); + for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) { + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_A + i * 2, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_B + i * 2, MSR_TYPE_RW, flag); + } +} + static inline bool pt_can_write_msr(struct vcpu_vmx *vmx) { return vmx_pt_mode_is_host_guest() && @@ -1306,6 +1465,25 @@ static void pt_guest_exit(struct vcpu_vmx *vmx) wrmsrq(MSR_IA32_RTIT_CTL, vmx->pt_desc.host.ctl); } +static unsigned int vmx_handle_intel_pt_intr(void) +{ + struct kvm_vcpu *vcpu = kvm_get_running_vcpu(); + + /* '0' on failure so that the !PT case can use a RET0 static call. */ + if (!vcpu || !kvm_handling_nmi_from_guest(vcpu)) + return 0; + + kvm_make_request(KVM_REQ_PMI, vcpu); + __set_bit(MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT, + (unsigned long *)&vcpu->arch.pmu.global_status); + return 1; +} +#else +static void pt_guest_enter(struct vcpu_vmx *vmx) { } +static void pt_guest_exit(struct vcpu_vmx *vmx) { } +static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) { WARN_ON_ONCE(1); } +#endif /* CONFIG_KVM_INTEL_PT */ + void vmx_set_host_fs_gs(struct vmcs_host_state *host, u16 fs_sel, u16 gs_sel, unsigned long fs_base, unsigned long gs_base) { @@ -1708,79 +1886,6 @@ void vmx_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask) vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, interruptibility); } -static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) -{ - struct vcpu_vmx *vmx = to_vmx(vcpu); - unsigned long value; - - /* - * Any MSR write that attempts to change bits marked reserved will - * case a #GP fault. - */ - if (data & vmx->pt_desc.ctl_bitmask) - return 1; - - /* - * Any attempt to modify IA32_RTIT_CTL while TraceEn is set will - * result in a #GP unless the same write also clears TraceEn. - */ - if ((vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN) && - (data & RTIT_CTL_TRACEEN) && - data != vmx->pt_desc.guest.ctl) - return 1; - - /* - * WRMSR to IA32_RTIT_CTL that sets TraceEn but clears this bit - * and FabricEn would cause #GP, if - * CPUID.(EAX=14H, ECX=0):ECX.SNGLRGNOUT[bit 2] = 0 - */ - if ((data & RTIT_CTL_TRACEEN) && !(data & RTIT_CTL_TOPA) && - !(data & RTIT_CTL_FABRIC_EN) && - !intel_pt_validate_cap(vmx->pt_desc.caps, - PT_CAP_single_range_output)) - return 1; - - /* - * MTCFreq, CycThresh and PSBFreq encodings check, any MSR write that - * utilize encodings marked reserved will cause a #GP fault. - */ - value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc_periods); - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc) && - !test_bit((data & RTIT_CTL_MTC_RANGE) >> - RTIT_CTL_MTC_RANGE_OFFSET, &value)) - return 1; - value = intel_pt_validate_cap(vmx->pt_desc.caps, - PT_CAP_cycle_thresholds); - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && - !test_bit((data & RTIT_CTL_CYC_THRESH) >> - RTIT_CTL_CYC_THRESH_OFFSET, &value)) - return 1; - value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_periods); - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && - !test_bit((data & RTIT_CTL_PSB_FREQ) >> - RTIT_CTL_PSB_FREQ_OFFSET, &value)) - return 1; - - /* - * If ADDRx_CFG is reserved or the encodings is >2 will - * cause a #GP fault. - */ - value = (data & RTIT_CTL_ADDR0) >> RTIT_CTL_ADDR0_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 1)) || (value > 2)) - return 1; - value = (data & RTIT_CTL_ADDR1) >> RTIT_CTL_ADDR1_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 2)) || (value > 2)) - return 1; - value = (data & RTIT_CTL_ADDR2) >> RTIT_CTL_ADDR2_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 3)) || (value > 2)) - return 1; - value = (data & RTIT_CTL_ADDR3) >> RTIT_CTL_ADDR3_OFFSET; - if ((value && (vmx->pt_desc.num_address_ranges < 4)) || (value > 2)) - return 1; - - return 0; -} - int vmx_check_emulate_instruction(struct kvm_vcpu *vcpu, int emul_type, void *insn, int insn_len) { @@ -2130,7 +2235,6 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { struct vcpu_vmx *vmx = to_vmx(vcpu); struct vmx_uret_msr *msr; - u32 index; switch (msr_info->index) { #ifdef CONFIG_X86_64 @@ -2216,6 +2320,7 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) &msr_info->data); #endif break; +#ifdef CONFIG_KVM_INTEL_PT case MSR_IA32_RTIT_CTL: if (!vmx_pt_mode_is_host_guest()) return 1; @@ -2251,8 +2356,9 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; msr_info->data = vmx->pt_desc.guest.output_mask; break; - case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: - index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; + case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: { + u32 index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; + if (!vmx_pt_mode_is_host_guest() || (index >= 2 * vmx->pt_desc.num_address_ranges)) return 1; @@ -2261,6 +2367,8 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) else msr_info->data = vmx->pt_desc.guest.addr_a[index / 2]; break; + } +#endif case MSR_IA32_S_CET: msr_info->data = vmcs_readl(GUEST_S_CET); break; @@ -2339,7 +2447,6 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) int ret = 0; u32 msr_index = msr_info->index; u64 data = msr_info->data; - u32 index; switch (msr_index) { case MSR_EFER: @@ -2529,6 +2636,7 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if (!guest_cpu_cap_has(vcpu, X86_FEATURE_VMX)) return 1; return vmx_set_vmx_msr(vcpu, msr_index, data); +#ifdef CONFIG_KVM_INTEL_PT case MSR_IA32_RTIT_CTL: if (!vmx_pt_mode_is_host_guest() || vmx_rtit_ctl_check(vcpu, data) || @@ -2575,7 +2683,9 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; vmx->pt_desc.guest.output_mask = data; break; - case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: + case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: { + u32 index; + if (!pt_can_write_msr(vmx)) return 1; index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; @@ -2588,6 +2698,8 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) else vmx->pt_desc.guest.addr_a[index / 2] = data; break; + } +#endif case MSR_IA32_S_CET: vmcs_writel(GUEST_S_CET, data); break; @@ -4222,22 +4334,6 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) } } -void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) -{ - struct vcpu_vmx *vmx = to_vmx(vcpu); - bool flag = !(vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN); - u32 i; - - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_STATUS, MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_BASE, MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_MASK, MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_CR3_MATCH, MSR_TYPE_RW, flag); - for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) { - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_A + i * 2, MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_B + i * 2, MSR_TYPE_RW, flag); - } -} - static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) { u64 vm_exit_controls_bits = VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | @@ -4997,12 +5093,14 @@ static void init_vmcs(struct kvm_vcpu *vcpu) vmx_write_encls_bitmap(vcpu, NULL); +#ifdef CONFIG_KVM_INTEL_PT if (vmx_pt_mode_is_host_guest()) { memset(&vmx->pt_desc, 0, sizeof(vmx->pt_desc)); /* Bit[6~0] are forced to 1, writes are ignored. */ vmx->pt_desc.guest.output_mask = 0x7F; vmcs_write64(GUEST_IA32_RTIT_CTL, 0); } +#endif vmcs_write32(GUEST_SYSENTER_CS, 0); vmcs_writel(GUEST_SYSENTER_ESP, 0); @@ -7758,75 +7856,6 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) #undef cr4_fixed1_update } -static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) -{ - struct vcpu_vmx *vmx = to_vmx(vcpu); - struct kvm_cpuid_entry2 *best = NULL; - int i; - - for (i = 0; i < PT_CPUID_LEAVES; i++) { - best = kvm_find_cpuid_entry_index(vcpu, 0x14, i); - if (!best) - return; - vmx->pt_desc.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM] = best->eax; - vmx->pt_desc.caps[CPUID_EBX + i*PT_CPUID_REGS_NUM] = best->ebx; - vmx->pt_desc.caps[CPUID_ECX + i*PT_CPUID_REGS_NUM] = best->ecx; - vmx->pt_desc.caps[CPUID_EDX + i*PT_CPUID_REGS_NUM] = best->edx; - } - - /* Get the number of configurable Address Ranges for filtering */ - vmx->pt_desc.num_address_ranges = intel_pt_validate_cap(vmx->pt_desc.caps, - PT_CAP_num_address_ranges); - - /* Initialize and clear the no dependency bits */ - vmx->pt_desc.ctl_bitmask = ~(RTIT_CTL_TRACEEN | RTIT_CTL_OS | - RTIT_CTL_USR | RTIT_CTL_TSC_EN | RTIT_CTL_DISRETC | - RTIT_CTL_BRANCH_EN); - - /* - * If CPUID.(EAX=14H,ECX=0):EBX[0]=1 CR3Filter can be set otherwise - * will inject an #GP - */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_cr3_filtering)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_CR3EN; - - /* - * If CPUID.(EAX=14H,ECX=0):EBX[1]=1 CYCEn, CycThresh and - * PSBFreq can be set - */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc)) - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_CYCLEACC | - RTIT_CTL_CYC_THRESH | RTIT_CTL_PSB_FREQ); - - /* - * If CPUID.(EAX=14H,ECX=0):EBX[3]=1 MTCEn and MTCFreq can be set - */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc)) - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_MTC_EN | - RTIT_CTL_MTC_RANGE); - - /* If CPUID.(EAX=14H,ECX=0):EBX[4]=1 FUPonPTW and PTWEn can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_ptwrite)) - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_FUP_ON_PTW | - RTIT_CTL_PTW_EN); - - /* If CPUID.(EAX=14H,ECX=0):EBX[5]=1 PwrEvEn can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_power_event_trace)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_PWR_EVT_EN; - - /* If CPUID.(EAX=14H,ECX=0):ECX[0]=1 ToPA can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_topa_output)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_TOPA; - - /* If CPUID.(EAX=14H,ECX=0):ECX[3]=1 FabricEn can be set */ - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_output_subsys)) - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_FABRIC_EN; - - /* unmask address range configure area */ - for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) - vmx->pt_desc.ctl_bitmask &= ~(0xfULL << (32 + i * 4)); -} - void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); @@ -8471,20 +8500,6 @@ gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags return (sign_extend64(gva, lam_bit) & ~BIT_ULL(63)) | (gva & BIT_ULL(63)); } -static unsigned int vmx_handle_intel_pt_intr(void) -{ - struct kvm_vcpu *vcpu = kvm_get_running_vcpu(); - - /* '0' on failure so that the !PT case can use a RET0 static call. */ - if (!vcpu || !kvm_handling_nmi_from_guest(vcpu)) - return 0; - - kvm_make_request(KVM_REQ_PMI, vcpu); - __set_bit(MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT, - (unsigned long *)&vcpu->arch.pmu.global_status); - return 1; -} - static __init void vmx_setup_user_return_msrs(void) { @@ -8671,6 +8686,7 @@ __init int vmx_hardware_setup(void) kvm_caps.supported_mce_cap |= MCG_LMCE_P; kvm_caps.supported_mce_cap |= MCG_CMCI_P; +#ifdef CONFIG_KVM_INTEL_PT if (pt_mode != PT_MODE_SYSTEM && pt_mode != PT_MODE_HOST_GUEST) return -EINVAL; if (!enable_ept || !enable_pmu || !cpu_has_vmx_intel_pt()) @@ -8679,6 +8695,7 @@ __init int vmx_hardware_setup(void) vt_init_ops.handle_intel_pt_intr = vmx_handle_intel_pt_intr; else vt_init_ops.handle_intel_pt_intr = NULL; +#endif setup_default_sgx_lepubkeyhash(); diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 6c9dd5b2365d..a449a36fcd1c 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -46,6 +46,7 @@ enum segment_cache_field { SEG_FIELD_NR = 4 }; +#ifdef CONFIG_KVM_INTEL_PT #define RTIT_ADDR_RANGE 4 struct pt_ctx { @@ -65,6 +66,7 @@ struct pt_desc { struct pt_ctx host; struct pt_ctx guest; }; +#endif /* * The nested_vmx structure is part of vcpu_vmx, and holds information we need @@ -280,7 +282,9 @@ struct vcpu_vmx { u64 msr_ia32_mcu_opt_ctrl; bool disable_fb_clear; +#ifdef CONFIG_KVM_INTEL_PT struct pt_desc pt_desc; +#endif struct lbr_desc lbr_desc; /* ve_info must be page aligned. */ ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] KVM: VMX: Allocate struct pt_desc only when PT is virtualized for guests 2026-11-06 16:39 ` Sean Christopherson @ 2026-11-11 1:54 ` Hao Peng 0 siblings, 0 replies; 3+ messages in thread From: Hao Peng @ 2026-11-11 1:54 UTC (permalink / raw) To: Sean Christopherson; +Cc: pbonzini, kvm On Sat, Nov 7, 2026 at 12:39 AM Sean Christopherson <seanjc@google.com> wrote: > > On Fri, Nov 06, 2026, Peng Hao wrote: > > struct pt_desc is only used when pt_mode is PT_MODE_HOST_GUEST, so > > allocate it on demand instead of embedding it in struct vcpu_vmx. > > > > This shrinks struct vcpu_vmx from 8384 to 8128 bytes. > > Hrm, I don't think I want to do this to save "only" 256 bytes. I assume the goal > is to get back down to an order-1 allocation, but vcpu_vmx is sooo close that > shaving off 256 bytes is only going to keep it order-1 for a kernel release or two. > > And for "full" configs, e.g. with Xen support, vcpu_vmx is ~9088 bytes. > > If we want to get vCPUs back to order-1 allocation with any chance of keeping them > there, I think we need to aim for ~6-7 KiB, but without adding unnecessary layers > of indirection. E.g. kvm_vcpu.stats is a whopping 1104 bytes, but it's accessed in > a ton of hot paths, so I wouldn't want to allocate that separately. Similarly, > we could dynamically allocate vcpu_vmx.nested, but I don't know that I'd want to > add that extra pointer chase just to get at a variety of flags. > > Potential ideas: > > 1. vcpu_vmx.rmode (208) - In practice, it's only used by CPUs launched before > Westmere (2010), and like PT, it's guarded by a module param so it's easy to > dynamically allocate. > > 2. kvm_vcpu_arch.pmu (912) - Can be allocated if and only if the vCPU has a PMU, > e.g. in kvm_pmu_refresh(), or based on the module param. I'd be somehwat > worried about regressing hot paths though. On the other hand, we could throw > pt_desc and lbr_desc into kvm_pmu to get a total of 1208 bytes in savings. > > 3. kvm_vcpu_arch.xen (680) - Only impacts a limited set of configs, but at a > glance, this one would be trivially easy to convert, and if distros enable > CONFIG_KVM_XEN, would provide meaningful savings for a lot of deployments. > > 4. kvm_vcpu_arch.apf.gfns (512) - Would need to be unconditionally allocated, > but its power-of-2 size makes it well suited for a kmem cache, and for many, > many VMs, this will almost never be used. > > If we did all of the above, that would save 2608 bytes for CONFIG_KVM_XEN=y and > 1928 bytes for CONFIG_KVM_XEN=n. That would put vcpu_vmx around 6.4KiB, which > would give us some room before we cross the order-1 boundary (again). > > The other option for pt_desc in particular would be to simply rip out all of the > code, as it's been buried behind CONFIG_BROKEN=y for a few years now. > > Actually, duh, the less committing thing to do is to compile out the code. I > think it'd still be worth doing at least some of the above changes, but hard > disabling PT virtualization seems like a no brainer. E.g. over a few patches to > isolate the code movement. I'll post a miniseries. > For context on why I went with dynamic allocation: since commit aa0d42cacf09 ("KVM: VMX: Bury Intel PT virtualization (guest/host mode) behind CONFIG_BROKEN"), pt_mode can only be changed with CONFIG_BROKEN=y, but the PT code and pt_desc are still built unconditionally. I took that as the intended state and only tried to avoid paying for pt_desc when it can't be used, so I didn't consider adding a dedicated Kconfig. Splitting it back out into CONFIG_KVM_INTEL_PT as you did solves the problem more simply. > diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig > index 1719059570fb..243307328adf 100644 > --- a/arch/x86/kvm/Kconfig > +++ b/arch/x86/kvm/Kconfig > @@ -151,6 +151,9 @@ config KVM_INTEL_TDX > > If unsure, say N. > > +config KVM_INTEL_PT > + def_bool y if BROKEN > + > config KVM_AMD > tristate "KVM for AMD processors support" > depends on KVM && (CPU_SUP_AMD || CPU_SUP_HYGON) > diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c > index e91650af44c2..a69020c01afd 100644 > --- a/arch/x86/kvm/msrs.c > +++ b/arch/x86/kvm/msrs.c > @@ -2596,6 +2596,7 @@ static void kvm_probe_msr_to_save(u32 msr_index) > if (!kvm_cpu_cap_has(X86_FEATURE_WAITPKG)) > return; > break; > +#ifdef CONFIG_KVM_INTEL_PT > case MSR_IA32_RTIT_CTL: > case MSR_IA32_RTIT_STATUS: > if (!kvm_cpu_cap_has(X86_FEATURE_INTEL_PT)) > @@ -2619,6 +2620,7 @@ static void kvm_probe_msr_to_save(u32 msr_index) > intel_pt_validate_hw_cap(PT_CAP_num_address_ranges) * 2)) > return; > break; > +#endif > case MSR_ARCH_PERFMON_PERFCTR0 ... > MSR_ARCH_PERFMON_PERFCTR0 + KVM_MAX_NR_GP_COUNTERS - 1: > if (msr_index - MSR_ARCH_PERFMON_PERFCTR0 >= > diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h > index 810119167f79..604cc376ca5a 100644 > --- a/arch/x86/kvm/vmx/capabilities.h > +++ b/arch/x86/kvm/vmx/capabilities.h > @@ -17,11 +17,16 @@ extern bool __read_mostly enable_ept_ad_bits; > extern bool __read_mostly enable_cet; > extern bool __read_mostly enable_pml; > extern bool __read_mostly enable_mbec; > -extern int __read_mostly pt_mode; > > #define PT_MODE_SYSTEM 0 > #define PT_MODE_HOST_GUEST 1 > > +#ifdef CONFIG_KVM_INTEL_PT > +extern int __read_mostly pt_mode; > +#else > +#define pt_mode PT_MODE_SYSTEM > +#endif > + > struct nested_vmx_msrs { > /* > * We only store the "true" versions of the VMX capability MSRs. We > diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c > index f0d75f5a968c..4bc0c1b743d8 100644 > --- a/arch/x86/kvm/vmx/nested.c > +++ b/arch/x86/kvm/vmx/nested.c > @@ -5455,10 +5455,12 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu) > vmx->nested.vmcs02_initialized = false; > vmx->nested.vmxon = true; > > +#ifdef CONFIG_KVM_INTEL_PT > if (vmx_pt_mode_is_host_guest()) { > vmx->pt_desc.guest.ctl = 0; > pt_update_intercept_for_msr(vcpu); > } > +#endif > > return 0; > > diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c > index e6a61514e797..bd1ea5f16c78 100644 > --- a/arch/x86/kvm/vmx/vmx.c > +++ b/arch/x86/kvm/vmx/vmx.c > @@ -210,8 +210,8 @@ static unsigned int ple_window_max = KVM_VMX_DEFAULT_PLE_WINDOW_MAX; > module_param(ple_window_max, uint, 0444); > > /* Default is SYSTEM mode, 1 for host-guest mode (which is BROKEN) */ > +#ifdef CONFIG_KVM_INTEL_PT > int __read_mostly pt_mode = PT_MODE_SYSTEM; > -#ifdef CONFIG_BROKEN > module_param(pt_mode, int, S_IRUGO); > #endif > > @@ -1231,6 +1231,165 @@ static unsigned long segment_base(u16 selector) > } > #endif > > +#ifdef CONFIG_KVM_INTEL_PT > +static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) > +{ > + struct vcpu_vmx *vmx = to_vmx(vcpu); > + unsigned long value; > + > + /* > + * Any MSR write that attempts to change bits marked reserved will > + * case a #GP fault. > + */ > + if (data & vmx->pt_desc.ctl_bitmask) > + return 1; > + > + /* > + * Any attempt to modify IA32_RTIT_CTL while TraceEn is set will > + * result in a #GP unless the same write also clears TraceEn. > + */ > + if ((vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN) && > + (data & RTIT_CTL_TRACEEN) && > + data != vmx->pt_desc.guest.ctl) > + return 1; > + > + /* > + * WRMSR to IA32_RTIT_CTL that sets TraceEn but clears this bit > + * and FabricEn would cause #GP, if > + * CPUID.(EAX=14H, ECX=0):ECX.SNGLRGNOUT[bit 2] = 0 > + */ > + if ((data & RTIT_CTL_TRACEEN) && !(data & RTIT_CTL_TOPA) && > + !(data & RTIT_CTL_FABRIC_EN) && > + !intel_pt_validate_cap(vmx->pt_desc.caps, > + PT_CAP_single_range_output)) > + return 1; > + > + /* > + * MTCFreq, CycThresh and PSBFreq encodings check, any MSR write that > + * utilize encodings marked reserved will cause a #GP fault. > + */ > + value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc_periods); > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc) && > + !test_bit((data & RTIT_CTL_MTC_RANGE) >> > + RTIT_CTL_MTC_RANGE_OFFSET, &value)) > + return 1; > + value = intel_pt_validate_cap(vmx->pt_desc.caps, > + PT_CAP_cycle_thresholds); > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && > + !test_bit((data & RTIT_CTL_CYC_THRESH) >> > + RTIT_CTL_CYC_THRESH_OFFSET, &value)) > + return 1; > + value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_periods); > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && > + !test_bit((data & RTIT_CTL_PSB_FREQ) >> > + RTIT_CTL_PSB_FREQ_OFFSET, &value)) > + return 1; > + > + /* > + * If ADDRx_CFG is reserved or the encodings is >2 will > + * cause a #GP fault. > + */ > + value = (data & RTIT_CTL_ADDR0) >> RTIT_CTL_ADDR0_OFFSET; > + if ((value && (vmx->pt_desc.num_address_ranges < 1)) || (value > 2)) > + return 1; > + value = (data & RTIT_CTL_ADDR1) >> RTIT_CTL_ADDR1_OFFSET; > + if ((value && (vmx->pt_desc.num_address_ranges < 2)) || (value > 2)) > + return 1; > + value = (data & RTIT_CTL_ADDR2) >> RTIT_CTL_ADDR2_OFFSET; > + if ((value && (vmx->pt_desc.num_address_ranges < 3)) || (value > 2)) > + return 1; > + value = (data & RTIT_CTL_ADDR3) >> RTIT_CTL_ADDR3_OFFSET; > + if ((value && (vmx->pt_desc.num_address_ranges < 4)) || (value > 2)) > + return 1; > + > + return 0; > +} > + > +static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) > +{ > + struct vcpu_vmx *vmx = to_vmx(vcpu); > + struct kvm_cpuid_entry2 *best = NULL; > + int i; > + > + for (i = 0; i < PT_CPUID_LEAVES; i++) { > + best = kvm_find_cpuid_entry_index(vcpu, 0x14, i); > + if (!best) > + return; > + vmx->pt_desc.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM] = best->eax; > + vmx->pt_desc.caps[CPUID_EBX + i*PT_CPUID_REGS_NUM] = best->ebx; > + vmx->pt_desc.caps[CPUID_ECX + i*PT_CPUID_REGS_NUM] = best->ecx; > + vmx->pt_desc.caps[CPUID_EDX + i*PT_CPUID_REGS_NUM] = best->edx; > + } > + > + /* Get the number of configurable Address Ranges for filtering */ > + vmx->pt_desc.num_address_ranges = intel_pt_validate_cap(vmx->pt_desc.caps, > + PT_CAP_num_address_ranges); > + > + /* Initialize and clear the no dependency bits */ > + vmx->pt_desc.ctl_bitmask = ~(RTIT_CTL_TRACEEN | RTIT_CTL_OS | > + RTIT_CTL_USR | RTIT_CTL_TSC_EN | RTIT_CTL_DISRETC | > + RTIT_CTL_BRANCH_EN); > + > + /* > + * If CPUID.(EAX=14H,ECX=0):EBX[0]=1 CR3Filter can be set otherwise > + * will inject an #GP > + */ > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_cr3_filtering)) > + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_CR3EN; > + > + /* > + * If CPUID.(EAX=14H,ECX=0):EBX[1]=1 CYCEn, CycThresh and > + * PSBFreq can be set > + */ > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc)) > + vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_CYCLEACC | > + RTIT_CTL_CYC_THRESH | RTIT_CTL_PSB_FREQ); > + > + /* > + * If CPUID.(EAX=14H,ECX=0):EBX[3]=1 MTCEn and MTCFreq can be set > + */ > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc)) > + vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_MTC_EN | > + RTIT_CTL_MTC_RANGE); > + > + /* If CPUID.(EAX=14H,ECX=0):EBX[4]=1 FUPonPTW and PTWEn can be set */ > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_ptwrite)) > + vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_FUP_ON_PTW | > + RTIT_CTL_PTW_EN); > + > + /* If CPUID.(EAX=14H,ECX=0):EBX[5]=1 PwrEvEn can be set */ > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_power_event_trace)) > + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_PWR_EVT_EN; > + > + /* If CPUID.(EAX=14H,ECX=0):ECX[0]=1 ToPA can be set */ > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_topa_output)) > + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_TOPA; > + > + /* If CPUID.(EAX=14H,ECX=0):ECX[3]=1 FabricEn can be set */ > + if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_output_subsys)) > + vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_FABRIC_EN; > + > + /* unmask address range configure area */ > + for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) > + vmx->pt_desc.ctl_bitmask &= ~(0xfULL << (32 + i * 4)); > +} > + > +void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) > +{ > + struct vcpu_vmx *vmx = to_vmx(vcpu); > + bool flag = !(vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN); > + u32 i; > + > + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_STATUS, MSR_TYPE_RW, flag); > + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_BASE, MSR_TYPE_RW, flag); > + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_MASK, MSR_TYPE_RW, flag); > + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_CR3_MATCH, MSR_TYPE_RW, flag); > + for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) { > + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_A + i * 2, MSR_TYPE_RW, flag); > + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_B + i * 2, MSR_TYPE_RW, flag); > + } > +} > + > static inline bool pt_can_write_msr(struct vcpu_vmx *vmx) > { > return vmx_pt_mode_is_host_guest() && > @@ -1306,6 +1465,25 @@ static void pt_guest_exit(struct vcpu_vmx *vmx) > wrmsrq(MSR_IA32_RTIT_CTL, vmx->pt_desc.host.ctl); > } > > +static unsigned int vmx_handle_intel_pt_intr(void) > +{ > + struct kvm_vcpu *vcpu = kvm_get_running_vcpu(); > + > + /* '0' on failure so that the !PT case can use a RET0 static call. */ > + if (!vcpu || !kvm_handling_nmi_from_guest(vcpu)) > + return 0; > + > + kvm_make_request(KVM_REQ_PMI, vcpu); > + __set_bit(MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT, > + (unsigned long *)&vcpu->arch.pmu.global_status); > + return 1; > +} > +#else > +static void pt_guest_enter(struct vcpu_vmx *vmx) { } > +static void pt_guest_exit(struct vcpu_vmx *vmx) { } > +static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) { WARN_ON_ONCE(1); } > +#endif /* CONFIG_KVM_INTEL_PT */ > + > void vmx_set_host_fs_gs(struct vmcs_host_state *host, u16 fs_sel, u16 gs_sel, > unsigned long fs_base, unsigned long gs_base) > { > @@ -1708,79 +1886,6 @@ void vmx_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask) > vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, interruptibility); > } > > -static int vmx_rtit_ctl_check(struct kvm_vcpu *vcpu, u64 data) > -{ > - struct vcpu_vmx *vmx = to_vmx(vcpu); > - unsigned long value; > - > - /* > - * Any MSR write that attempts to change bits marked reserved will > - * case a #GP fault. > - */ > - if (data & vmx->pt_desc.ctl_bitmask) > - return 1; > - > - /* > - * Any attempt to modify IA32_RTIT_CTL while TraceEn is set will > - * result in a #GP unless the same write also clears TraceEn. > - */ > - if ((vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN) && > - (data & RTIT_CTL_TRACEEN) && > - data != vmx->pt_desc.guest.ctl) > - return 1; > - > - /* > - * WRMSR to IA32_RTIT_CTL that sets TraceEn but clears this bit > - * and FabricEn would cause #GP, if > - * CPUID.(EAX=14H, ECX=0):ECX.SNGLRGNOUT[bit 2] = 0 > - */ > - if ((data & RTIT_CTL_TRACEEN) && !(data & RTIT_CTL_TOPA) && > - !(data & RTIT_CTL_FABRIC_EN) && > - !intel_pt_validate_cap(vmx->pt_desc.caps, > - PT_CAP_single_range_output)) > - return 1; > - > - /* > - * MTCFreq, CycThresh and PSBFreq encodings check, any MSR write that > - * utilize encodings marked reserved will cause a #GP fault. > - */ > - value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc_periods); > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc) && > - !test_bit((data & RTIT_CTL_MTC_RANGE) >> > - RTIT_CTL_MTC_RANGE_OFFSET, &value)) > - return 1; > - value = intel_pt_validate_cap(vmx->pt_desc.caps, > - PT_CAP_cycle_thresholds); > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && > - !test_bit((data & RTIT_CTL_CYC_THRESH) >> > - RTIT_CTL_CYC_THRESH_OFFSET, &value)) > - return 1; > - value = intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_periods); > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc) && > - !test_bit((data & RTIT_CTL_PSB_FREQ) >> > - RTIT_CTL_PSB_FREQ_OFFSET, &value)) > - return 1; > - > - /* > - * If ADDRx_CFG is reserved or the encodings is >2 will > - * cause a #GP fault. > - */ > - value = (data & RTIT_CTL_ADDR0) >> RTIT_CTL_ADDR0_OFFSET; > - if ((value && (vmx->pt_desc.num_address_ranges < 1)) || (value > 2)) > - return 1; > - value = (data & RTIT_CTL_ADDR1) >> RTIT_CTL_ADDR1_OFFSET; > - if ((value && (vmx->pt_desc.num_address_ranges < 2)) || (value > 2)) > - return 1; > - value = (data & RTIT_CTL_ADDR2) >> RTIT_CTL_ADDR2_OFFSET; > - if ((value && (vmx->pt_desc.num_address_ranges < 3)) || (value > 2)) > - return 1; > - value = (data & RTIT_CTL_ADDR3) >> RTIT_CTL_ADDR3_OFFSET; > - if ((value && (vmx->pt_desc.num_address_ranges < 4)) || (value > 2)) > - return 1; > - > - return 0; > -} > - > int vmx_check_emulate_instruction(struct kvm_vcpu *vcpu, int emul_type, > void *insn, int insn_len) > { > @@ -2130,7 +2235,6 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > { > struct vcpu_vmx *vmx = to_vmx(vcpu); > struct vmx_uret_msr *msr; > - u32 index; > > switch (msr_info->index) { > #ifdef CONFIG_X86_64 > @@ -2216,6 +2320,7 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > &msr_info->data); > #endif > break; > +#ifdef CONFIG_KVM_INTEL_PT > case MSR_IA32_RTIT_CTL: > if (!vmx_pt_mode_is_host_guest()) > return 1; > @@ -2251,8 +2356,9 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > return 1; > msr_info->data = vmx->pt_desc.guest.output_mask; > break; > - case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: > - index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; > + case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: { > + u32 index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; > + > if (!vmx_pt_mode_is_host_guest() || > (index >= 2 * vmx->pt_desc.num_address_ranges)) > return 1; > @@ -2261,6 +2367,8 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > else > msr_info->data = vmx->pt_desc.guest.addr_a[index / 2]; > break; > + } > +#endif > case MSR_IA32_S_CET: > msr_info->data = vmcs_readl(GUEST_S_CET); > break; > @@ -2339,7 +2447,6 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > int ret = 0; > u32 msr_index = msr_info->index; > u64 data = msr_info->data; > - u32 index; > > switch (msr_index) { > case MSR_EFER: > @@ -2529,6 +2636,7 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > if (!guest_cpu_cap_has(vcpu, X86_FEATURE_VMX)) > return 1; > return vmx_set_vmx_msr(vcpu, msr_index, data); > +#ifdef CONFIG_KVM_INTEL_PT > case MSR_IA32_RTIT_CTL: > if (!vmx_pt_mode_is_host_guest() || > vmx_rtit_ctl_check(vcpu, data) || > @@ -2575,7 +2683,9 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > return 1; > vmx->pt_desc.guest.output_mask = data; > break; > - case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: > + case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: { > + u32 index; > + > if (!pt_can_write_msr(vmx)) > return 1; > index = msr_info->index - MSR_IA32_RTIT_ADDR0_A; > @@ -2588,6 +2698,8 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) > else > vmx->pt_desc.guest.addr_a[index / 2] = data; > break; > + } > +#endif > case MSR_IA32_S_CET: > vmcs_writel(GUEST_S_CET, data); > break; > @@ -4222,22 +4334,6 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) > } > } > > -void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) > -{ > - struct vcpu_vmx *vmx = to_vmx(vcpu); > - bool flag = !(vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN); > - u32 i; > - > - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_STATUS, MSR_TYPE_RW, flag); > - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_BASE, MSR_TYPE_RW, flag); > - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_MASK, MSR_TYPE_RW, flag); > - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_CR3_MATCH, MSR_TYPE_RW, flag); > - for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) { > - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_A + i * 2, MSR_TYPE_RW, flag); > - vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_B + i * 2, MSR_TYPE_RW, flag); > - } > -} > - > static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) > { > u64 vm_exit_controls_bits = VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | > @@ -4997,12 +5093,14 @@ static void init_vmcs(struct kvm_vcpu *vcpu) > > vmx_write_encls_bitmap(vcpu, NULL); > > +#ifdef CONFIG_KVM_INTEL_PT > if (vmx_pt_mode_is_host_guest()) { > memset(&vmx->pt_desc, 0, sizeof(vmx->pt_desc)); > /* Bit[6~0] are forced to 1, writes are ignored. */ > vmx->pt_desc.guest.output_mask = 0x7F; > vmcs_write64(GUEST_IA32_RTIT_CTL, 0); > } > +#endif > > vmcs_write32(GUEST_SYSENTER_CS, 0); > vmcs_writel(GUEST_SYSENTER_ESP, 0); > @@ -7758,75 +7856,6 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) > #undef cr4_fixed1_update > } > > -static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) > -{ > - struct vcpu_vmx *vmx = to_vmx(vcpu); > - struct kvm_cpuid_entry2 *best = NULL; > - int i; > - > - for (i = 0; i < PT_CPUID_LEAVES; i++) { > - best = kvm_find_cpuid_entry_index(vcpu, 0x14, i); > - if (!best) > - return; > - vmx->pt_desc.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM] = best->eax; > - vmx->pt_desc.caps[CPUID_EBX + i*PT_CPUID_REGS_NUM] = best->ebx; > - vmx->pt_desc.caps[CPUID_ECX + i*PT_CPUID_REGS_NUM] = best->ecx; > - vmx->pt_desc.caps[CPUID_EDX + i*PT_CPUID_REGS_NUM] = best->edx; > - } > - > - /* Get the number of configurable Address Ranges for filtering */ > - vmx->pt_desc.num_address_ranges = intel_pt_validate_cap(vmx->pt_desc.caps, > - PT_CAP_num_address_ranges); > - > - /* Initialize and clear the no dependency bits */ > - vmx->pt_desc.ctl_bitmask = ~(RTIT_CTL_TRACEEN | RTIT_CTL_OS | > - RTIT_CTL_USR | RTIT_CTL_TSC_EN | RTIT_CTL_DISRETC | > - RTIT_CTL_BRANCH_EN); > - > - /* > - * If CPUID.(EAX=14H,ECX=0):EBX[0]=1 CR3Filter can be set otherwise > - * will inject an #GP > - */ > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_cr3_filtering)) > - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_CR3EN; > - > - /* > - * If CPUID.(EAX=14H,ECX=0):EBX[1]=1 CYCEn, CycThresh and > - * PSBFreq can be set > - */ > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_psb_cyc)) > - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_CYCLEACC | > - RTIT_CTL_CYC_THRESH | RTIT_CTL_PSB_FREQ); > - > - /* > - * If CPUID.(EAX=14H,ECX=0):EBX[3]=1 MTCEn and MTCFreq can be set > - */ > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_mtc)) > - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_MTC_EN | > - RTIT_CTL_MTC_RANGE); > - > - /* If CPUID.(EAX=14H,ECX=0):EBX[4]=1 FUPonPTW and PTWEn can be set */ > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_ptwrite)) > - vmx->pt_desc.ctl_bitmask &= ~(RTIT_CTL_FUP_ON_PTW | > - RTIT_CTL_PTW_EN); > - > - /* If CPUID.(EAX=14H,ECX=0):EBX[5]=1 PwrEvEn can be set */ > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_power_event_trace)) > - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_PWR_EVT_EN; > - > - /* If CPUID.(EAX=14H,ECX=0):ECX[0]=1 ToPA can be set */ > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_topa_output)) > - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_TOPA; > - > - /* If CPUID.(EAX=14H,ECX=0):ECX[3]=1 FabricEn can be set */ > - if (intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_output_subsys)) > - vmx->pt_desc.ctl_bitmask &= ~RTIT_CTL_FABRIC_EN; > - > - /* unmask address range configure area */ > - for (i = 0; i < vmx->pt_desc.num_address_ranges; i++) > - vmx->pt_desc.ctl_bitmask &= ~(0xfULL << (32 + i * 4)); > -} > - > void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) > { > struct vcpu_vmx *vmx = to_vmx(vcpu); > @@ -8471,20 +8500,6 @@ gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags > return (sign_extend64(gva, lam_bit) & ~BIT_ULL(63)) | (gva & BIT_ULL(63)); > } > > -static unsigned int vmx_handle_intel_pt_intr(void) > -{ > - struct kvm_vcpu *vcpu = kvm_get_running_vcpu(); > - > - /* '0' on failure so that the !PT case can use a RET0 static call. */ > - if (!vcpu || !kvm_handling_nmi_from_guest(vcpu)) > - return 0; > - > - kvm_make_request(KVM_REQ_PMI, vcpu); > - __set_bit(MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI_BIT, > - (unsigned long *)&vcpu->arch.pmu.global_status); > - return 1; > -} > - > static __init void vmx_setup_user_return_msrs(void) > { > > @@ -8671,6 +8686,7 @@ __init int vmx_hardware_setup(void) > kvm_caps.supported_mce_cap |= MCG_LMCE_P; > kvm_caps.supported_mce_cap |= MCG_CMCI_P; > > +#ifdef CONFIG_KVM_INTEL_PT > if (pt_mode != PT_MODE_SYSTEM && pt_mode != PT_MODE_HOST_GUEST) > return -EINVAL; > if (!enable_ept || !enable_pmu || !cpu_has_vmx_intel_pt()) > @@ -8679,6 +8695,7 @@ __init int vmx_hardware_setup(void) > vt_init_ops.handle_intel_pt_intr = vmx_handle_intel_pt_intr; > else > vt_init_ops.handle_intel_pt_intr = NULL; > +#endif > > setup_default_sgx_lepubkeyhash(); > > diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h > index 6c9dd5b2365d..a449a36fcd1c 100644 > --- a/arch/x86/kvm/vmx/vmx.h > +++ b/arch/x86/kvm/vmx/vmx.h > @@ -46,6 +46,7 @@ enum segment_cache_field { > SEG_FIELD_NR = 4 > }; > > +#ifdef CONFIG_KVM_INTEL_PT > #define RTIT_ADDR_RANGE 4 > > struct pt_ctx { > @@ -65,6 +66,7 @@ struct pt_desc { > struct pt_ctx host; > struct pt_ctx guest; > }; > +#endif > > /* > * The nested_vmx structure is part of vcpu_vmx, and holds information we need > @@ -280,7 +282,9 @@ struct vcpu_vmx { > u64 msr_ia32_mcu_opt_ctrl; > bool disable_fb_clear; > > +#ifdef CONFIG_KVM_INTEL_PT > struct pt_desc pt_desc; > +#endif > struct lbr_desc lbr_desc; > > /* ve_info must be page aligned. */ > ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-11 1:54 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-06 7:34 [PATCH] KVM: VMX: Allocate struct pt_desc only when PT is virtualized for guests Peng Hao 2026-11-06 16:39 ` Sean Christopherson 2026-11-11 1:54 ` Hao Peng
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox