~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/kvm/x86_64/debug_regs.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * KVM guest debug register tests
  4  *
  5  * Copyright (C) 2020, Red Hat, Inc.
  6  */
  7 #include <stdio.h>
  8 #include <string.h>
  9 #include "kvm_util.h"
 10 #include "processor.h"
 11 #include "apic.h"
 12 
 13 #define DR6_BD          (1 << 13)
 14 #define DR7_GD          (1 << 13)
 15 
 16 #define IRQ_VECTOR 0xAA
 17 
 18 /* For testing data access debug BP */
 19 uint32_t guest_value;
 20 
 21 extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start;
 22 
 23 static void guest_code(void)
 24 {
 25         /* Create a pending interrupt on current vCPU */
 26         x2apic_enable();
 27         x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT |
 28                          APIC_DM_FIXED | IRQ_VECTOR);
 29 
 30         /*
 31          * Software BP tests.
 32          *
 33          * NOTE: sw_bp need to be before the cmd here, because int3 is an
 34          * exception rather than a normal trap for KVM_SET_GUEST_DEBUG (we
 35          * capture it using the vcpu exception bitmap).
 36          */
 37         asm volatile("sw_bp: int3");
 38 
 39         /* Hardware instruction BP test */
 40         asm volatile("hw_bp: nop");
 41 
 42         /* Hardware data BP test */
 43         asm volatile("mov $1234,%%rax;\n\t"
 44                      "mov %%rax,%0;\n\t write_data:"
 45                      : "=m" (guest_value) : : "rax");
 46 
 47         /*
 48          * Single step test, covers 2 basic instructions and 2 emulated
 49          *
 50          * Enable interrupts during the single stepping to see that
 51          * pending interrupt we raised is not handled due to KVM_GUESTDBG_BLOCKIRQ
 52          */
 53         asm volatile("ss_start: "
 54                      "sti\n\t"
 55                      "xor %%eax,%%eax\n\t"
 56                      "cpuid\n\t"
 57                      "movl $0x1a0,%%ecx\n\t"
 58                      "rdmsr\n\t"
 59                      "cli\n\t"
 60                      : : : "eax", "ebx", "ecx", "edx");
 61 
 62         /* DR6.BD test */
 63         asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax");
 64         GUEST_DONE();
 65 }
 66 
 67 #define  CAST_TO_RIP(v)  ((unsigned long long)&(v))
 68 
 69 static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len)
 70 {
 71         struct kvm_regs regs;
 72 
 73         vcpu_regs_get(vcpu, &regs);
 74         regs.rip += insn_len;
 75         vcpu_regs_set(vcpu, &regs);
 76 }
 77 
 78 int main(void)
 79 {
 80         struct kvm_guest_debug debug;
 81         unsigned long long target_dr6, target_rip;
 82         struct kvm_vcpu *vcpu;
 83         struct kvm_run *run;
 84         struct kvm_vm *vm;
 85         struct ucall uc;
 86         uint64_t cmd;
 87         int i;
 88         /* Instruction lengths starting at ss_start */
 89         int ss_size[6] = {
 90                 1,              /* sti*/
 91                 2,              /* xor */
 92                 2,              /* cpuid */
 93                 5,              /* mov */
 94                 2,              /* rdmsr */
 95                 1,              /* cli */
 96         };
 97 
 98         TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG));
 99 
100         vm = vm_create_with_one_vcpu(&vcpu, guest_code);
101         run = vcpu->run;
102 
103         /* Test software BPs - int3 */
104         memset(&debug, 0, sizeof(debug));
105         debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
106         vcpu_guest_debug_set(vcpu, &debug);
107         vcpu_run(vcpu);
108         TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
109                     run->debug.arch.exception == BP_VECTOR &&
110                     run->debug.arch.pc == CAST_TO_RIP(sw_bp),
111                     "INT3: exit %d exception %d rip 0x%llx (should be 0x%llx)",
112                     run->exit_reason, run->debug.arch.exception,
113                     run->debug.arch.pc, CAST_TO_RIP(sw_bp));
114         vcpu_skip_insn(vcpu, 1);
115 
116         /* Test instruction HW BP over DR[0-3] */
117         for (i = 0; i < 4; i++) {
118                 memset(&debug, 0, sizeof(debug));
119                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
120                 debug.arch.debugreg[i] = CAST_TO_RIP(hw_bp);
121                 debug.arch.debugreg[7] = 0x400 | (1UL << (2*i+1));
122                 vcpu_guest_debug_set(vcpu, &debug);
123                 vcpu_run(vcpu);
124                 target_dr6 = 0xffff0ff0 | (1UL << i);
125                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
126                             run->debug.arch.exception == DB_VECTOR &&
127                             run->debug.arch.pc == CAST_TO_RIP(hw_bp) &&
128                             run->debug.arch.dr6 == target_dr6,
129                             "INS_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
130                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
131                             i, run->exit_reason, run->debug.arch.exception,
132                             run->debug.arch.pc, CAST_TO_RIP(hw_bp),
133                             run->debug.arch.dr6, target_dr6);
134         }
135         /* Skip "nop" */
136         vcpu_skip_insn(vcpu, 1);
137 
138         /* Test data access HW BP over DR[0-3] */
139         for (i = 0; i < 4; i++) {
140                 memset(&debug, 0, sizeof(debug));
141                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
142                 debug.arch.debugreg[i] = CAST_TO_RIP(guest_value);
143                 debug.arch.debugreg[7] = 0x00000400 | (1UL << (2*i+1)) |
144                     (0x000d0000UL << (4*i));
145                 vcpu_guest_debug_set(vcpu, &debug);
146                 vcpu_run(vcpu);
147                 target_dr6 = 0xffff0ff0 | (1UL << i);
148                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
149                             run->debug.arch.exception == DB_VECTOR &&
150                             run->debug.arch.pc == CAST_TO_RIP(write_data) &&
151                             run->debug.arch.dr6 == target_dr6,
152                             "DATA_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
153                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
154                             i, run->exit_reason, run->debug.arch.exception,
155                             run->debug.arch.pc, CAST_TO_RIP(write_data),
156                             run->debug.arch.dr6, target_dr6);
157                 /* Rollback the 4-bytes "mov" */
158                 vcpu_skip_insn(vcpu, -7);
159         }
160         /* Skip the 4-bytes "mov" */
161         vcpu_skip_insn(vcpu, 7);
162 
163         /* Test single step */
164         target_rip = CAST_TO_RIP(ss_start);
165         target_dr6 = 0xffff4ff0ULL;
166         for (i = 0; i < (sizeof(ss_size) / sizeof(ss_size[0])); i++) {
167                 target_rip += ss_size[i];
168                 memset(&debug, 0, sizeof(debug));
169                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP |
170                                 KVM_GUESTDBG_BLOCKIRQ;
171                 debug.arch.debugreg[7] = 0x00000400;
172                 vcpu_guest_debug_set(vcpu, &debug);
173                 vcpu_run(vcpu);
174                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
175                             run->debug.arch.exception == DB_VECTOR &&
176                             run->debug.arch.pc == target_rip &&
177                             run->debug.arch.dr6 == target_dr6,
178                             "SINGLE_STEP[%d]: exit %d exception %d rip 0x%llx "
179                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
180                             i, run->exit_reason, run->debug.arch.exception,
181                             run->debug.arch.pc, target_rip, run->debug.arch.dr6,
182                             target_dr6);
183         }
184 
185         /* Finally test global disable */
186         memset(&debug, 0, sizeof(debug));
187         debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
188         debug.arch.debugreg[7] = 0x400 | DR7_GD;
189         vcpu_guest_debug_set(vcpu, &debug);
190         vcpu_run(vcpu);
191         target_dr6 = 0xffff0ff0 | DR6_BD;
192         TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
193                     run->debug.arch.exception == DB_VECTOR &&
194                     run->debug.arch.pc == CAST_TO_RIP(bd_start) &&
195                     run->debug.arch.dr6 == target_dr6,
196                             "DR7.GD: exit %d exception %d rip 0x%llx "
197                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
198                             run->exit_reason, run->debug.arch.exception,
199                             run->debug.arch.pc, target_rip, run->debug.arch.dr6,
200                             target_dr6);
201 
202         /* Disable all debug controls, run to the end */
203         memset(&debug, 0, sizeof(debug));
204         vcpu_guest_debug_set(vcpu, &debug);
205 
206         vcpu_run(vcpu);
207         TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
208         cmd = get_ucall(vcpu, &uc);
209         TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE");
210 
211         kvm_vm_free(vm);
212 
213         return 0;
214 }
215 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php