cachepc

Prime+Probe cache-based side-channel attack on AMD SEV-SNP protected virtual machines
git clone https://git.sinitax.com/sinitax/cachepc
Log | Files | Refs | Submodules | README | sfeed.txt

commit 5617c30b0a876a79bf5c9b5d9e46a937ccab4a07
parent 9cce829d8a794848b0699c3f9a84b2a057221a90
Author: Louis Burda <quent.burda@gmail.com>
Date:   Thu, 29 Sep 2022 02:14:42 +0200

Add the page tracking to patch

Diffstat:
Mpatch.diff | 926++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 712 insertions(+), 214 deletions(-)

diff --git a/patch.diff b/patch.diff @@ -1,241 +1,426 @@ +diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h +index eb186bc57f6a..747bd841677f 100644 +--- a/arch/x86/include/asm/kvm_page_track.h ++++ b/arch/x86/include/asm/kvm_page_track.h +@@ -4,6 +4,10 @@ + + enum kvm_page_track_mode { + KVM_PAGE_TRACK_WRITE, ++ KVM_PAGE_TRACK_ACCESS, ++ KVM_PAGE_TRACK_RESET_ACCESSED, //TODO: hacky, as this is not really for page tracking ++ KVM_PAGE_TRACK_EXEC, ++ KVM_PAGE_TRACK_RESET_EXEC, + KVM_PAGE_TRACK_MAX, + }; + diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile -index b804444e16d4..e94fa8c02a1d 100644 +index fd4a4da28a5a..6d4a2a6530b6 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile -@@ -1,6 +1,6 @@ - # SPDX-License-Identifier: GPL-2.0 - --ccflags-y += -Iarch/x86/kvm -+ccflags-y += -Iarch/x86/kvm -O2 +@@ -3,6 +3,8 @@ + ccflags-y += -I $(srctree)/arch/x86/kvm -O2 ccflags-$(CONFIG_KVM_WERROR) += -Werror ++KBUILD_EXTRA_SYMBOLS := ../../../drivers/crypto/ccp/Module.symvers ++ ifeq ($(CONFIG_FRAME_POINTER),y) -@@ -10,7 +10,9 @@ endif - KVM := ../../../virt/kvm + OBJECT_FILES_NON_STANDARD_vmenter.o := y + endif +@@ -12,7 +14,7 @@ include $(srctree)/virt/kvm/Makefile.kvm + kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ + i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ + hyperv.o debugfs.o mmu/mmu.o mmu/page_track.o mmu/spte.o \ +- svm/cachepc/cachepc.o svm/cachepc/util.o svm/cachepc/kvm.o ++ sev-step.o userspace_page_track_signals.o svm/cachepc/cachepc.o svm/cachepc/util.o svm/cachepc/kvm.o - kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ -- $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o -+ $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \ -+ svm/cachepc/cachepc.o svm/cachepc/util.o svm/cachepc/kvm.o + ifdef CONFIG_HYPERV + kvm-y += kvm_onhyperv.o +diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c +index d871b8dee7b3..d9c79a6c5942 100644 +--- a/arch/x86/kvm/mmu/mmu.c ++++ b/arch/x86/kvm/mmu/mmu.c +@@ -56,6 +56,9 @@ + + #include "paging.h" + ++#include <linux/sev-step.h> ++#include <linux/userspace_page_track_signals.h> + - kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o + extern bool itlb_multihit_kvm_mitigation; - kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ -@@ -20,7 +22,8 @@ kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ - - kvm-intel-y += vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o \ - vmx/evmcs.o vmx/nested.o vmx/posted_intr.o --kvm-amd-y += svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o svm/sev.o -+kvm-amd-y += svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o svm/sev.o \ -+ svm/cachepc/cachepc.o svm/cachepc/util.o - - obj-$(CONFIG_KVM) += kvm.o - obj-$(CONFIG_KVM_INTEL) += kvm-intel.o -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 7b3cfbe8f7e3..241ce70885dc 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -2,6 +2,8 @@ - - #include <linux/kvm_host.h> - -+#include "cachepc/cachepc.h" -+ - #include "irq.h" - #include "mmu.h" - #include "kvm_cache_regs.h" -@@ -3728,6 +3730,8 @@ void __svm_vcpu_run(unsigned long vmcb_pa, unsigned long *regs); - static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, - struct vcpu_svm *svm) + int __read_mostly nx_huge_pages = -1; +@@ -1152,8 +1155,8 @@ static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep) + } + } + +-/* +- * Write-protect on the specified @sptep, @pt_protect indicates whether ++/* Apply the protection mode specified in @mode to the specified @sptep, ++ * @pt_protect indicates whether + * spte write-protection is caused by protecting shadow page table. + * + * Note: write protection is difference between dirty logging and spte +@@ -1165,9 +1168,10 @@ static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep) + * + * Return true if tlb need be flushed. + */ +-static bool spte_write_protect(u64 *sptep, bool pt_protect) ++static bool spte_protect(u64 *sptep, bool pt_protect, enum kvm_page_track_mode mode) + { + u64 spte = *sptep; ++ bool shouldFlush = false; + + if (!is_writable_pte(spte) && + !(pt_protect && is_mmu_writable_spte(spte))) +@@ -1175,22 +1179,45 @@ static bool spte_write_protect(u64 *sptep, bool pt_protect) + + rmap_printk("spte %p %llx\n", sptep, *sptep); + +- if (pt_protect) +- spte &= ~shadow_mmu_writable_mask; +- spte = spte & ~PT_WRITABLE_MASK; +- +- return mmu_spte_update(sptep, spte); ++ if (pt_protect){ ++ //spte &= ~shadow_mmu_writable_mask; ++ spte &= ~EPT_SPTE_MMU_WRITABLE; ++ } ++ //spte = spte & ~PT_WRITABLE_MASK; ++ if(mode == KVM_PAGE_TRACK_WRITE) { ++ spte = spte & ~PT_WRITABLE_MASK; ++ shouldFlush = true; ++ } else if( mode == KVM_PAGE_TRACK_RESET_ACCESSED) { ++ spte = spte & ~PT_ACCESSED_MASK; ++ } else if(mode == KVM_PAGE_TRACK_ACCESS) { ++ spte = spte & ~PT_PRESENT_MASK; ++ spte = spte & ~PT_WRITABLE_MASK; ++ spte = spte & ~PT_USER_MASK; ++ spte = spte | (0x1ULL << PT64_NX_SHIFT); ++ shouldFlush = true; ++ } else if( mode == KVM_PAGE_TRACK_EXEC) { ++ spte = spte | (0x1ULL << PT64_NX_SHIFT); //nx bit is set, to prevent execution, not removed ++ shouldFlush = true; ++ } else if (mode == KVM_PAGE_TRACK_RESET_EXEC) { ++ spte = spte & (~(0x1ULL << PT64_NX_SHIFT)); ++ shouldFlush = true; ++ } else { ++ printk(KERN_WARNING "spte_protect was called with invalid mode" ++ "parameter %d\n",mode); ++ } ++ shouldFlush |= mmu_spte_update(sptep, spte); ++ return shouldFlush; + } + +-static bool rmap_write_protect(struct kvm_rmap_head *rmap_head, +- bool pt_protect) ++static bool rmap_protect(struct kvm_rmap_head *rmap_head, bool pt_protect, enum kvm_page_track_mode mode) + { + u64 *sptep; + struct rmap_iterator iter; + bool flush = false; + +- for_each_rmap_spte(rmap_head, &iter, sptep) +- flush |= spte_write_protect(sptep, pt_protect); ++ for_each_rmap_spte(rmap_head, &iter, sptep) { ++ flush |= spte_protect(sptep, pt_protect, mode); ++ } + + return flush; + } +@@ -1263,7 +1290,7 @@ static void kvm_mmu_write_protect_pt_masked(struct kvm *kvm, + while (mask) { + rmap_head = gfn_to_rmap(slot->base_gfn + gfn_offset + __ffs(mask), + PG_LEVEL_4K, slot); +- rmap_write_protect(rmap_head, false); ++ rmap_protect(rmap_head, false, KVM_PAGE_TRACK_WRITE); + + /* clear the first set bit */ + mask &= mask - 1; +@@ -1354,26 +1381,29 @@ int kvm_cpu_dirty_log_size(void) + return kvm_x86_ops.cpu_dirty_log_size; + } + +-bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm, ++bool kvm_mmu_slot_gfn_protect(struct kvm *kvm, + struct kvm_memory_slot *slot, u64 gfn, +- int min_level) ++ int min_level, enum kvm_page_track_mode mode) { -+ int cpu; -+ - /* - * VMENTER enables interrupts (host state), but the kernel state is - * interrupts disabled when this is invoked. Also tell RCU about -@@ -3749,9 +3753,23 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, - lockdep_hardirqs_on(CALLER_ADDR0); - - if (sev_es_guest(svm->vcpu.kvm)) { -+ memset(cachepc_msrmts, 0, 64 * 2); -+ cpu = get_cpu(); -+ local_irq_disable(); -+ WARN_ON(cpu != 2); - __svm_sev_es_vcpu_run(svm->vmcb_pa); -+ cachepc_save_msrmts(cachepc_ds); -+ local_irq_enable(); -+ put_cpu(); - } else { -+ memset(cachepc_msrmts, 0, 64 * 2); -+ cpu = get_cpu(); -+ local_irq_disable(); -+ WARN_ON(cpu != 2); - __svm_vcpu_run(svm->vmcb_pa, (unsigned long *)&svm->vcpu.arch.regs); -+ cachepc_save_msrmts(cachepc_ds); -+ local_irq_enable(); -+ put_cpu(); - - #ifdef CONFIG_X86_64 - native_wrmsrl(MSR_GS_BASE, svm->host.gs_base); -@@ -3785,8 +3803,12 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, - - static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu) + struct kvm_rmap_head *rmap_head; + int i; +- bool write_protected = false; ++ //bool write_protected = false; ++ bool protected = false; + + if (kvm_memslots_have_rmaps(kvm)) { + for (i = min_level; i <= KVM_MAX_HUGEPAGE_LEVEL; ++i) { + rmap_head = gfn_to_rmap(gfn, i, slot); +- write_protected |= rmap_write_protect(rmap_head, true); ++ //write_protected |= rmap_write_protect(rmap_head, true); ++ protected |= rmap_protect(kvm, rmap_head, true, mode); + } + } + + if (is_tdp_mmu_enabled(kvm)) +- write_protected |= ++ //write_protected |= ++ protected |= + kvm_tdp_mmu_write_protect_gfn(kvm, slot, gfn, min_level); + +- return write_protected; ++ return protected; + } + + static bool kvm_vcpu_write_protect_gfn(struct kvm_vcpu *vcpu, u64 gfn) +@@ -3901,6 +3931,38 @@ static int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, bool direct) + static bool page_fault_handle_page_track(struct kvm_vcpu *vcpu, + struct kvm_page_fault *fault) { -- struct vcpu_svm *svm = to_svm(vcpu); -+ struct vcpu_svm *svm; ++ int send_err; ++ uint64_t current_rip; ++ int have_rip; ++ int i; ++ bool was_tracked; ++ int modes[] = {KVM_PAGE_TRACK_WRITE,KVM_PAGE_TRACK_ACCESS,KVM_PAGE_TRACK_EXEC}; ++ was_tracked = false; ++ for( i = 0; i < sizeof(modes) / sizeof(modes[0]); i++ ) { ++ if(kvm_page_track_is_active(vcpu,gfn,modes[i])) { ++ __untrack_single_page(vcpu, gfn, modes[i]); ++ was_tracked = true; ++ } ++ } ++ if( was_tracked ) { ++ have_rip = false; ++ if( uspt_should_get_rip() ) { ++ //! because 0 indicates "no error" but have_rip should be one if successfull ++ have_rip = (!sev_step_get_rip_kvm_vcpu(vcpu,&current_rip)); ++ } ++ if( uspt_batch_tracking_in_progress() ) { ++ if( (send_err = uspt_batch_tracking_save(gfn << PAGE_SHIFT,error_code,have_rip,current_rip)) ) { ++ printk_ratelimited("uspt_batch_tracking_save failed with %d\n##########################\n",send_err); ++ } ++ uspt_batch_tracking_handle_retrack(vcpu,gfn); ++ uspt_batch_tracking_inc_event_idx(); ++ } else { ++ if( (send_err = uspt_send_and_block(gfn << PAGE_SHIFT,error_code,have_rip,current_rip)) ) { ++ printk("uspt_send_and_block failed with %d\n##########################\n",send_err); ++ } ++ } ++ } + -+ printk(KERN_WARNING "CachePC: svm_cpu_enter_exit()\n"); -+ WARN_ON(smp_processor_id() != 2); + if (unlikely(fault->rsvd)) + return false; -+ svm = to_svm(vcpu); - svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX]; - svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP]; - svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP]; -@@ -3888,7 +3910,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu) +@@ -3911,7 +3973,7 @@ static bool page_fault_handle_page_track(struct kvm_vcpu *vcpu, + * guest is writing the page which is write tracked which can + * not be fixed by page fault handler. + */ +- if (kvm_slot_page_track_is_active(vcpu->kvm, fault->slot, fault->gfn, KVM_PAGE_TRACK_WRITE)) ++ if (kvm_slot_page_track_is_active(vcpu->kvm, fault->slot, fault->gfn, KVM_PAGE_TRACK_WRITE) || kvm_slot_page_track_is_active(vcpu->kvm, fault->slot, fault->gfn, KVM_PAGE_TRACK_ACCESS)) + return true; + + return false; +@@ -5991,7 +6053,7 @@ static bool slot_rmap_write_protect(struct kvm *kvm, + struct kvm_rmap_head *rmap_head, + const struct kvm_memory_slot *slot) + { +- return rmap_write_protect(rmap_head, false); ++ return rmap_protect(rmap_head, false, KVM_PAGE_TRACK_WRITE); + } + + void kvm_mmu_slot_remove_write_access(struct kvm *kvm, +diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h +index bd2a26897b97..aa57ab1b4c89 100644 +--- a/arch/x86/kvm/mmu/mmu_internal.h ++++ b/arch/x86/kvm/mmu/mmu_internal.h +@@ -133,9 +133,9 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, + + void kvm_mmu_gfn_disallow_lpage(const struct kvm_memory_slot *slot, gfn_t gfn); + void kvm_mmu_gfn_allow_lpage(const struct kvm_memory_slot *slot, gfn_t gfn); +-bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm, ++bool kvm_mmu_slot_gfn_protect(struct kvm *kvm, + struct kvm_memory_slot *slot, u64 gfn, +- int min_level); ++ int min_level, enum kvm_page_track_mode mode); + void kvm_flush_remote_tlbs_with_address(struct kvm *kvm, + u64 start_gfn, u64 pages); + unsigned int pte_list_count(struct kvm_rmap_head *rmap_head); +diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c +index 2e09d1b6249f..05c68f5bfca3 100644 +--- a/arch/x86/kvm/mmu/page_track.c ++++ b/arch/x86/kvm/mmu/page_track.c +@@ -131,9 +131,11 @@ void kvm_slot_page_track_add_page(struct kvm *kvm, + */ + kvm_mmu_gfn_disallow_lpage(slot, gfn); + +- if (mode == KVM_PAGE_TRACK_WRITE) +- if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn, PG_LEVEL_4K)) ++ //if (mode == KVM_PAGE_TRACK_WRITE) ++ // if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn, PG_LEVEL_4K)) ++ if (kvm_mmu_slot_gfn_protect(kvm, slot, gfn, mode)) { + kvm_flush_remote_tlbs(kvm); ++ } + } + EXPORT_SYMBOL_GPL(kvm_slot_page_track_add_page); - svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING; - vmcb_mark_all_clean(svm->vmcb); -- -+ printk(KERN_WARNING "Vincent: svm->vmcb exit code %d\n", svm->vmcb->control.exit_code); - /* if exit due to PF check for async PF */ - if (svm->vmcb->control.exit_code == SVM_EXIT_EXCP_BASE + PF_VECTOR) - svm->vcpu.arch.apf.host_apf_flags = diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S -index 6feb8c08f45a..60da3cff6c49 100644 +index 5e62795a4bc1..0626f3fdddfd 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S -@@ -27,14 +27,59 @@ +@@ -27,50 +27,53 @@ #define VCPU_R15 __VCPU_REGS_R15 * WORD_SIZE #endif ++.section .noinstr.text, "ax" ++ +.extern cachepc_msrmts ++.extern cachepc_regs_tmp ++.extern cachepc_regs_vm + - .section .noinstr.text, "ax" - -+.macro load_tmp off reg -+ mov cachepc_regs_tmp(off), reg -+.endm -+ -+.macro save_tmp off reg -+ mov reg, cachepc_regs_tmp(off) -+.endm -+ -+.macro load_vm off reg -+ mov cachepc_regs_vm(off), reg -+.endm -+ -+.macro save_vm off reg -+ mov reg, cachepc_regs_vm(off) -+.endm -+ -+.macro apply_regs func -+ func 0x00, %rax -+ func 0x08, %rbx -+ func 0x10, %rcx -+ func 0x18, %rdx -+ func 0x20, %rbp -+ func 0x28, %rsp -+ func 0x30, %rdi -+ func 0x38, %rsi -+ func 0x40, %r8 -+ func 0x48, %r9 -+ func 0x50, %r10 -+ func 0x58, %r11 -+ func 0x60, %r12 -+ func 0x68, %r13 -+ func 0x70, %r14 -+ func 0x78, %r15 -+.endm -+ -+.macro barrier + .macro load_tmp off reg +- mov cachepc_regs_tmp(off), reg ++ mov cachepc_regs_tmp+\off(%rip), \reg + .endm + + .macro save_tmp off reg +- mov reg, cachepc_regs_tmp(off) ++ mov \reg, cachepc_regs_tmp+\off(%rip) + .endm + + .macro load_vm off reg +- mov cachepc_regs_vm(off), reg ++ mov cachepc_regs_vm+\off(%rip), \reg + .endm + + .macro save_vm off reg +- mov reg, cachepc_regs_vm(off) ++ mov \reg, cachepc_regs_vm+\off(%rip) + .endm + + .macro apply_regs func +- func 0x00, %rax +- func 0x08, %rbx +- func 0x10, %rcx +- func 0x18, %rdx +- func 0x20, %rbp +- func 0x28, %rsp +- func 0x30, %rdi +- func 0x38, %rsi +- func 0x40, %r8 +- func 0x48, %r9 +- func 0x50, %r10 +- func 0x58, %r11 +- func 0x60, %r12 +- func 0x68, %r13 +- func 0x70, %r14 +- func 0x78, %r15 ++ \func 0x00, %rax ++ \func 0x08, %rbx ++ \func 0x10, %rcx ++ \func 0x18, %rdx ++ \func 0x20, %rbp ++ \func 0x28, %rsp ++ \func 0x30, %rdi ++ \func 0x38, %rsi ++ \func 0x40, %r8 ++ \func 0x48, %r9 ++ \func 0x50, %r10 ++ \func 0x58, %r11 ++ \func 0x60, %r12 ++ \func 0x68, %r13 ++ \func 0x70, %r14 ++ \func 0x78, %r15 + .endm + + .macro barrier +- mfence +- mov $0x80000005,%eax +- cpuid + mfence + mov $0x80000005,%eax + cpuid -+.endm -+ + .endm + +- +-.section .noinstr.text, "ax" +- /** * __svm_vcpu_run - Run a vCPU via a transition to SVM guest mode * @vmcb_pa: unsigned long - * @regs: unsigned long * (to guest registers) - */ - SYM_FUNC_START(__svm_vcpu_run) -+ apply_regs save_tmp -+ - push %_ASM_BP - #ifdef CONFIG_X86_64 - push %r15 -@@ -45,6 +90,7 @@ SYM_FUNC_START(__svm_vcpu_run) - push %edi - push %esi - #endif -+ - push %_ASM_BX - - /* Save @regs. */ -@@ -86,7 +132,28 @@ SYM_FUNC_START(__svm_vcpu_run) - ud2 - _ASM_EXTABLE(1b, 2b) +@@ -125,26 +128,26 @@ SYM_FUNC_START(__svm_vcpu_run) + sti --3: vmrun %_ASM_AX -+3: + 1: +- //apply_regs save_vm +- //apply_regs load_tmp +- //mov cachepc_ds, %rsi +- //mov 0x8(%rsi), %r15 +- //lea sev_prime_ret(%rip), %rdi +- //jmp cachepc_prime_vcall+1 // skip stack pushs +-//sev_prime_ret: +- //apply_regs save_tmp +- //apply_regs load_vm + apply_regs save_vm + apply_regs load_tmp + mov cachepc_ds, %rsi + mov 0x8(%rsi), %r15 + lea sev_prime_ret(%rip), %rdi -+ jmp cachepc_prime_vcall+1 // skip stack pushes ++ jmp cachepc_prime_vcall+5+1 // skip stack pushs +sev_prime_ret: + apply_regs save_tmp + apply_regs load_vm -+ -+ vmrun %_ASM_AX -+ + + vmrun %_ASM_AX + +-// apply_regs save_vm +-// apply_regs load_tmp +-// mov %r15, %rsi +-// lea sev_probe_ret(%rip), %rdi +-// jmp cachepc_probe_vcall+6 // skip stack pushs +-//sev_probe_ret: +-// apply_regs save_tmp +-// apply_regs load_vm + apply_regs save_vm + apply_regs load_tmp + mov %r15, %rsi + lea sev_probe_ret(%rip), %rdi -+ jmp cachepc_probe_vcall+6 // skip stack pushs ++ jmp cachepc_probe_vcall+5+8 // skip stack pushs +sev_probe_ret: + apply_regs save_tmp + apply_regs load_vm -+ - jmp 5f - 4: cmpb $0, kvm_rebooting - jne 5f -@@ -166,6 +233,11 @@ SYM_FUNC_START(__svm_vcpu_run) - pop %edi - #endif - pop %_ASM_BP -+ -+ # mov cachepc_msrmts(%rip), %rax -+ # mov $0x1, %edx -+ # mov %dx, (%rax) -+ - ret - SYM_FUNC_END(__svm_vcpu_run) -@@ -174,6 +246,8 @@ SYM_FUNC_END(__svm_vcpu_run) - * @vmcb_pa: unsigned long - */ - SYM_FUNC_START(__svm_sev_es_vcpu_run) -+ apply_regs save_tmp -+ - push %_ASM_BP - #ifdef CONFIG_X86_64 - push %r15 -@@ -190,7 +264,29 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) - mov %_ASM_ARG1, %_ASM_AX - sti + 2: cli --1: vmrun %_ASM_AX -+1: -+ +@@ -249,26 +252,26 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) + + 1: + +-// apply_regs save_vm +-// apply_regs load_tmp +-// mov cachepc_ds, %rsi +-// mov 0x8(%rsi), %r15 +-// lea sev_es_prime_ret(%rip), %rdi +-// jmp cachepc_prime_vcall+1 // skip stack pushes +-//sev_es_prime_ret: +-// apply_regs save_tmp +-// apply_regs load_vm +-// +- vmrun %_ASM_AX +-// +-// apply_regs save_vm +-// apply_regs load_tmp +-// mov %r15, %rsi +-// lea sev_es_probe_ret(%rip), %rdi +-// jmp cachepc_probe_vcall+6 // skip stack pushs +-//sev_es_probe_ret: +-// apply_regs save_tmp +-// apply_regs load_vm + apply_regs save_vm + apply_regs load_tmp + mov cachepc_ds, %rsi + mov 0x8(%rsi), %r15 + lea sev_es_prime_ret(%rip), %rdi -+ jmp cachepc_prime_vcall+1 // skip stack pushes ++ jmp cachepc_prime_vcall+5+1 // skip stack pushes +sev_es_prime_ret: + apply_regs save_tmp + apply_regs load_vm @@ -246,42 +431,355 @@ index 6feb8c08f45a..60da3cff6c49 100644 + apply_regs load_tmp + mov %r15, %rsi + lea sev_es_probe_ret(%rip), %rdi -+ jmp cachepc_probe_vcall+6 // skip stack pushs ++ jmp cachepc_probe_vcall+5+8 // skip stack pushs +sev_es_probe_ret: + apply_regs save_tmp + apply_regs load_vm + + 2: cli + +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index d9adf79124f9..3b619d1b4850 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -82,6 +82,9 @@ + #include <asm/sgx.h> + #include <clocksource/hyperv_timer.h> + ++#include <linux/sev-step.h> ++#include "mmu/mmu_internal.h" + - jmp 3f - 2: cmpb $0, kvm_rebooting - jne 3f -diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c -index 2541a17ff1c4..1345938d1d2b 100644 ---- a/virt/kvm/kvm_main.c -+++ b/virt/kvm/kvm_main.c -@@ -66,6 +66,8 @@ - /* Worst case buffer size needed for holding an integer. */ - #define ITOA_MAX_LEN 12 + #define CREATE_TRACE_POINTS + #include "trace.h" -+#include "../../arch/x86/kvm/svm/cachepc/kvm.h" +@@ -13083,6 +13086,173 @@ int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size, + : kvm_sev_es_outs(vcpu, size, port); + } + EXPORT_SYMBOL_GPL(kvm_sev_es_string_io); ++bool __untrack_single_page(struct kvm_vcpu *vcpu, gfn_t gfn, ++ enum kvm_page_track_mode mode) { ++ int idx; ++ bool ret; ++ struct kvm_memory_slot *slot; ++ ++ ret = false; ++ idx = srcu_read_lock(&vcpu->kvm->srcu); ++ if (mode == KVM_PAGE_TRACK_ACCESS) { ++ //printk("Removing gfn: %016llx from acess page track pool\n", gfn); ++ } ++ if (mode == KVM_PAGE_TRACK_WRITE) { ++ //printk("Removing gfn: %016llx from write page track pool\n", gfn); ++ } ++ slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); ++ ++ if (slot != NULL && kvm_page_track_is_active(vcpu, gfn, mode)) { ++ ++ write_lock(&vcpu->kvm->mmu_lock); ++ kvm_slot_page_track_remove_page(vcpu->kvm, slot, gfn, mode); ++ write_unlock(&vcpu->kvm->mmu_lock); ++ ret = true; ++ ++ } else { ++ ++ printk("Failed to untrack %016llx because ", gfn); ++ if (slot == NULL) { ++ printk(KERN_CONT "slot was null"); ++ } else if (!kvm_page_track_is_active(vcpu, gfn, mode)) { ++ printk(KERN_CONT "page track was not active"); ++ } ++ printk(KERN_CONT "\n"); ++ } ++ srcu_read_unlock(&vcpu->kvm->srcu, idx); ++ return ret; ++} ++EXPORT_SYMBOL(__untrack_single_page); ++ ++bool __reset_accessed_on_page(struct kvm_vcpu *vcpu, gfn_t gfn) { ++ int idx; ++ bool ret; ++ struct kvm_memory_slot *slot; ++ ++ ret = false; ++ idx = srcu_read_lock(&vcpu->kvm->srcu); ++ slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); ++ if( slot != NULL ) { ++ write_lock(&vcpu->kvm->mmu_lock); ++ kvm_mmu_slot_gfn_protect(vcpu->kvm,slot,gfn,KVM_PAGE_TRACK_RESET_ACCESSED); ++ write_unlock(&vcpu->kvm->mmu_lock); ++ ret = true; ++ } ++ srcu_read_unlock(&vcpu->kvm->srcu, idx); ++ return ret; ++} ++EXPORT_SYMBOL(__reset_accessed_on_page); ++ ++bool __clear_nx_on_page(struct kvm_vcpu *vcpu, gfn_t gfn) { ++ int idx; ++ bool ret; ++ struct kvm_memory_slot *slot; ++ ++ ret = false; ++ idx = srcu_read_lock(&vcpu->kvm->srcu); ++ slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); ++ if( slot != NULL ) { ++ write_lock(&vcpu->kvm->mmu_lock); ++ kvm_mmu_slot_gfn_protect(vcpu->kvm,slot,gfn,KVM_PAGE_TRACK_RESET_EXEC); ++ write_unlock(&vcpu->kvm->mmu_lock); ++ ret = true; ++ } ++ srcu_read_unlock(&vcpu->kvm->srcu, idx); ++ return ret; ++} ++EXPORT_SYMBOL(__clear_nx_on_page); ++ ++bool __track_single_page(struct kvm_vcpu *vcpu, gfn_t gfn, ++ enum kvm_page_track_mode mode) { ++ int idx; ++ bool ret; ++ struct kvm_memory_slot *slot; ++ ++ ret = false; ++ idx = srcu_read_lock(&vcpu->kvm->srcu); ++ if (mode == KVM_PAGE_TRACK_ACCESS) { ++ //printk_ratelimited("Adding gfn: %016llx to acess page track pool\n", gfn); ++ //printk("Adding gfn: %016llx to acess page track pool\n", gfn); ++ } ++ if (mode == KVM_PAGE_TRACK_WRITE) { ++ //printk_ratelimited("Adding gfn: %016llx to write page track pool\n", gfn); ++ } ++ slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); ++ if (slot != NULL && !kvm_page_track_is_active(vcpu, gfn, mode)) { ++ ++ write_lock(&vcpu->kvm->mmu_lock); ++ kvm_slot_page_track_add_page(vcpu->kvm, slot, gfn, mode); ++ write_unlock(&vcpu->kvm->mmu_lock); ++ ret = true; + - MODULE_AUTHOR("Qumranet"); - MODULE_LICENSE("GPL"); ++ } else { ++ ++ printk("Failed to track %016llx because ", gfn); ++ if (slot == NULL) { ++ printk(KERN_CONT "slot was null"); ++ } ++ if (kvm_page_track_is_active(vcpu, gfn, mode)) { ++ printk(KERN_CONT "page is already tracked"); ++ } ++ printk(KERN_CONT "\n"); ++ } ++ srcu_read_unlock(&vcpu->kvm->srcu, idx); ++ return ret; ++} ++EXPORT_SYMBOL(__track_single_page); ++ ++//track all pages; taken from severed repo ++long kvm_start_tracking(struct kvm_vcpu *vcpu,enum kvm_page_track_mode mode ) { ++ long count = 0; ++ u64 iterator, iterat_max; ++ struct kvm_memory_slot *slot; ++ int idx; ++ ++ ++ iterat_max = vcpu->kvm->memslots[0]->memslots[0].base_gfn ++ + vcpu->kvm->memslots[0]->memslots[0].npages; ++ for (iterator=0; iterator < iterat_max; iterator++) ++ { ++ idx = srcu_read_lock(&vcpu->kvm->srcu); ++ slot = kvm_vcpu_gfn_to_memslot(vcpu, iterator); ++ if ( slot != NULL && !kvm_page_track_is_active(vcpu, iterator, mode)) { ++ write_lock(&vcpu->kvm->mmu_lock); ++ kvm_slot_page_track_add_page(vcpu->kvm, slot, iterator, mode); ++ write_unlock(&vcpu->kvm->mmu_lock); ++ count++; ++ } ++ srcu_read_unlock(&vcpu->kvm->srcu, idx); ++ } ++ ++ return count; ++} ++EXPORT_SYMBOL(kvm_start_tracking); ++ ++//track all pages; taken from severed repo ++long kvm_stop_tracking(struct kvm_vcpu *vcpu,enum kvm_page_track_mode mode ) { ++ long count = 0; ++ u64 iterator, iterat_max; ++ struct kvm_memory_slot *slot; ++ int idx; ++ ++ iterat_max = vcpu->kvm->memslots[0]->memslots[0].base_gfn + ++ vcpu->kvm->memslots[0]->memslots[0].npages; ++ for (iterator=0; iterator < iterat_max; iterator++) ++ { ++ idx = srcu_read_lock(&vcpu->kvm->srcu); ++ slot = kvm_vcpu_gfn_to_memslot(vcpu, iterator); ++ if ( slot != NULL && kvm_page_track_is_active(vcpu, iterator, mode)) { ++ write_lock(&vcpu->kvm->mmu_lock); ++ kvm_slot_page_track_remove_page(vcpu->kvm, slot, iterator, mode); ++ write_unlock(&vcpu->kvm->mmu_lock); ++ count++; ++ } ++ srcu_read_unlock(&vcpu->kvm->srcu, idx); ++ } ++ ++ return count; ++} ++EXPORT_SYMBOL(kvm_stop_tracking); -@@ -4848,6 +4850,8 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, - r = kvm_vfio_ops_init(); - WARN_ON(r); + EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_entry); + EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit); +diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c +index e089fbf9017f..7899e1efe852 100644 +--- a/drivers/crypto/ccp/sev-dev.c ++++ b/drivers/crypto/ccp/sev-dev.c +@@ -87,7 +87,7 @@ static void *sev_init_ex_buffer; + static size_t sev_es_tmr_size = SEV_ES_TMR_SIZE; -+ cachepc_kvm_init(); -+ - return 0; + static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret); +-static int sev_do_cmd(int cmd, void *data, int *psp_ret); ++int sev_do_cmd(int cmd, void *data, int *psp_ret); - out_unreg: -@@ -4872,6 +4876,8 @@ EXPORT_SYMBOL_GPL(kvm_init); + static inline bool sev_version_greater_or_equal(u8 maj, u8 min) + { +@@ -865,7 +865,7 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret) + return ret; + } + +-static int sev_do_cmd(int cmd, void *data, int *psp_ret) ++int sev_do_cmd(int cmd, void *data, int *psp_ret) + { + int rc; + +@@ -875,6 +875,7 @@ static int sev_do_cmd(int cmd, void *data, int *psp_ret) - void kvm_exit(void) + return rc; + } ++EXPORT_SYMBOL(sev_do_cmd); + + static int __sev_init_locked(int *error) { -+ cachepc_kvm_exit(); +diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h +index f288b421b603..81b232132f66 100644 +--- a/include/uapi/linux/kvm.h ++++ b/include/uapi/linux/kvm.h +@@ -16,6 +16,78 @@ + + #define KVM_API_VERSION 12 + ++#define KVM_USPT_POLL_EVENT_NO_EVENT 1000 ++#define KVM_USPT_POLL_EVENT_GOT_EVENT 0 ++ + - debugfs_remove_recursive(kvm_debugfs_dir); - misc_deregister(&kvm_dev); - kmem_cache_destroy(kvm_vcpu_cache); ++typedef struct { ++ uint64_t id; //filled automatically ++ uint64_t faulted_gpa; ++ uint32_t error_code; ++ bool have_rip_info; ++ uint64_t rip; ++ uint64_t ns_timestamp; ++ bool have_retired_instructions; ++ uint64_t retired_instructions; ++} page_fault_event_t; ++ ++typedef struct { ++ int tracking_type; ++ uint64_t expected_events; ++ int perf_cpu; ++ bool retrack; ++} batch_track_config_t; ++ ++typedef struct { ++ uint64_t event_count; ++} batch_track_event_count_t; ++ ++typedef struct { ++ page_fault_event_t* out_buf; ++ uint64_t len; ++ bool error_during_batch; ++} batch_track_stop_and_get_t; ++ ++typedef struct { ++ int cpu; //cpu on which we want to read the counter ++ uint64_t retired_instruction_count; //result param ++} retired_instr_perf_t; ++ ++typedef struct { ++ int cpu; //cpu on which counter should be programmed ++} retired_instr_perf_config_t; ++ ++typedef struct { ++ uint64_t gpa; ++ uint64_t len; ++ bool decrypt_with_host_key; ++ int wbinvd_cpu; //-1: do not flush; else logical cpu on which we flush ++ void* output_buffer; ++}read_guest_memory_t; ++ ++typedef struct { ++ int pid; ++ bool get_rip; ++} userspace_ctx_t; ++ ++ ++typedef struct { ++ uint64_t id; ++} ack_event_t; ++ ++ ++typedef struct { ++ uint64_t gpa; ++ int track_mode; ++} track_page_param_t; ++ ++ ++typedef struct { ++ int track_mode; ++} track_all_pages_t; ++ ++ ++ + /* *** Deprecated interfaces *** */ + + #define KVM_TRC_SHIFT 16 +@@ -921,6 +993,29 @@ struct kvm_ppc_resize_hpt { + #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2) + #define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list) + ++ ++// ++// SNP ATTACK IOCTLS ++// ++ ++#define KVM_TRACK_PAGE _IOWR(KVMIO, 0x20, track_page_param_t) ++#define KVM_USPT_REGISTER_PID _IOWR(KVMIO, 0x21, userspace_ctx_t) ++#define KVM_USPT_WAIT_AND_SEND _IO(KVMIO, 0x22) ++#define KVM_USPT_POLL_EVENT _IOWR(KVMIO, 0x23, page_fault_event_t) ++#define KVM_USPT_ACK_EVENT _IOWR(KVMIO, 0x24, ack_event_t) ++#define KVM_READ_GUEST_MEMORY _IOWR(KVMIO, 0x25, read_guest_memory_t) ++#define KVM_USPT_RESET _IO(KVMIO, 0x26) ++#define KVM_USPT_TRACK_ALL _IOWR(KVMIO, 0x27, track_all_pages_t) ++#define KVM_USPT_UNTRACK_ALL _IOWR(KVMIO, 0x28, track_all_pages_t) ++#define KVM_USPT_SETUP_RETINSTR_PERF _IOWR(KVMIO, 0x30,retired_instr_perf_config_t) ++#define KVM_USPT_READ_RETINSTR_PERF _IOWR(KVMIO,0x31, retired_instr_perf_t) ++#define KVM_USPT_BATCH_TRACK_START _IOWR(KVMIO,0x32,batch_track_config_t) ++#define KVM_USPT_BATCH_TRACK_STOP _IOWR(KVMIO,0x33,batch_track_stop_and_get_t) ++#define KVM_USPT_BATCH_TRACK_EVENT_COUNT _IOWR(KVMIO,0x34,batch_track_event_count_t) ++ ++ ++ ++ + /* + * Extension capability list. + */ +diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c +index 259cc0b63c87..ac5fc6c64b7e 100644 +--- a/virt/kvm/kvm_main.c ++++ b/virt/kvm/kvm_main.c +@@ -67,6 +67,9 @@ + + #include <linux/kvm_dirty_ring.h> + ++#include <linux/sev-step.h> ++#include <linux/userspace_page_track_signals.h> ++ + /* Worst case buffer size needed for holding an integer. */ + #define ITOA_MAX_LEN 12 +