|
22 | 22 | #define SPSR_SS (1 << 21)
|
23 | 23 |
|
24 | 24 | extern unsigned char sw_bp, sw_bp2, hw_bp, hw_bp2, bp_svc, bp_brk, hw_wp, ss_start;
|
| 25 | +extern unsigned char iter_ss_begin, iter_ss_end; |
25 | 26 | static volatile uint64_t sw_bp_addr, hw_bp_addr;
|
26 | 27 | static volatile uint64_t wp_addr, wp_data_addr;
|
27 | 28 | static volatile uint64_t svc_addr;
|
@@ -238,6 +239,46 @@ static void guest_svc_handler(struct ex_regs *regs)
|
238 | 239 | svc_addr = regs->pc;
|
239 | 240 | }
|
240 | 241 |
|
| 242 | +enum single_step_op { |
| 243 | + SINGLE_STEP_ENABLE = 0, |
| 244 | + SINGLE_STEP_DISABLE = 1, |
| 245 | +}; |
| 246 | + |
| 247 | +static void guest_code_ss(int test_cnt) |
| 248 | +{ |
| 249 | + uint64_t i; |
| 250 | + uint64_t bvr, wvr, w_bvr, w_wvr; |
| 251 | + |
| 252 | + for (i = 0; i < test_cnt; i++) { |
| 253 | + /* Bits [1:0] of dbg{b,w}vr are RES0 */ |
| 254 | + w_bvr = i << 2; |
| 255 | + w_wvr = i << 2; |
| 256 | + |
| 257 | + /* Enable Single Step execution */ |
| 258 | + GUEST_SYNC(SINGLE_STEP_ENABLE); |
| 259 | + |
| 260 | + /* |
| 261 | + * The userspace will veriry that the pc is as expected during |
| 262 | + * single step execution between iter_ss_begin and iter_ss_end. |
| 263 | + */ |
| 264 | + asm volatile("iter_ss_begin:nop\n"); |
| 265 | + |
| 266 | + write_sysreg(w_bvr, dbgbvr0_el1); |
| 267 | + write_sysreg(w_wvr, dbgwvr0_el1); |
| 268 | + bvr = read_sysreg(dbgbvr0_el1); |
| 269 | + wvr = read_sysreg(dbgwvr0_el1); |
| 270 | + |
| 271 | + asm volatile("iter_ss_end:\n"); |
| 272 | + |
| 273 | + /* Disable Single Step execution */ |
| 274 | + GUEST_SYNC(SINGLE_STEP_DISABLE); |
| 275 | + |
| 276 | + GUEST_ASSERT(bvr == w_bvr); |
| 277 | + GUEST_ASSERT(wvr == w_wvr); |
| 278 | + } |
| 279 | + GUEST_DONE(); |
| 280 | +} |
| 281 | + |
241 | 282 | static int debug_version(struct kvm_vcpu *vcpu)
|
242 | 283 | {
|
243 | 284 | uint64_t id_aa64dfr0;
|
@@ -293,16 +334,106 @@ static void test_guest_debug_exceptions(void)
|
293 | 334 | kvm_vm_free(vm);
|
294 | 335 | }
|
295 | 336 |
|
| 337 | +void test_single_step_from_userspace(int test_cnt) |
| 338 | +{ |
| 339 | + struct kvm_vcpu *vcpu; |
| 340 | + struct kvm_vm *vm; |
| 341 | + struct ucall uc; |
| 342 | + struct kvm_run *run; |
| 343 | + uint64_t pc, cmd; |
| 344 | + uint64_t test_pc = 0; |
| 345 | + bool ss_enable = false; |
| 346 | + struct kvm_guest_debug debug = {}; |
| 347 | + |
| 348 | + vm = vm_create_with_one_vcpu(&vcpu, guest_code_ss); |
| 349 | + ucall_init(vm, NULL); |
| 350 | + run = vcpu->run; |
| 351 | + vcpu_args_set(vcpu, 1, test_cnt); |
| 352 | + |
| 353 | + while (1) { |
| 354 | + vcpu_run(vcpu); |
| 355 | + if (run->exit_reason != KVM_EXIT_DEBUG) { |
| 356 | + cmd = get_ucall(vcpu, &uc); |
| 357 | + if (cmd == UCALL_ABORT) { |
| 358 | + REPORT_GUEST_ASSERT(uc); |
| 359 | + /* NOT REACHED */ |
| 360 | + } else if (cmd == UCALL_DONE) { |
| 361 | + break; |
| 362 | + } |
| 363 | + |
| 364 | + TEST_ASSERT(cmd == UCALL_SYNC, |
| 365 | + "Unexpected ucall cmd 0x%lx", cmd); |
| 366 | + |
| 367 | + if (uc.args[1] == SINGLE_STEP_ENABLE) { |
| 368 | + debug.control = KVM_GUESTDBG_ENABLE | |
| 369 | + KVM_GUESTDBG_SINGLESTEP; |
| 370 | + ss_enable = true; |
| 371 | + } else { |
| 372 | + debug.control = SINGLE_STEP_DISABLE; |
| 373 | + ss_enable = false; |
| 374 | + } |
| 375 | + |
| 376 | + vcpu_guest_debug_set(vcpu, &debug); |
| 377 | + continue; |
| 378 | + } |
| 379 | + |
| 380 | + TEST_ASSERT(ss_enable, "Unexpected KVM_EXIT_DEBUG"); |
| 381 | + |
| 382 | + /* Check if the current pc is expected. */ |
| 383 | + vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &pc); |
| 384 | + TEST_ASSERT(!test_pc || pc == test_pc, |
| 385 | + "Unexpected pc 0x%lx (expected 0x%lx)", |
| 386 | + pc, test_pc); |
| 387 | + |
| 388 | + /* |
| 389 | + * If the current pc is between iter_ss_bgin and |
| 390 | + * iter_ss_end, the pc for the next KVM_EXIT_DEBUG should |
| 391 | + * be the current pc + 4. |
| 392 | + */ |
| 393 | + if ((pc >= (uint64_t)&iter_ss_begin) && |
| 394 | + (pc < (uint64_t)&iter_ss_end)) |
| 395 | + test_pc = pc + 4; |
| 396 | + else |
| 397 | + test_pc = 0; |
| 398 | + } |
| 399 | + |
| 400 | + kvm_vm_free(vm); |
| 401 | +} |
| 402 | + |
| 403 | +static void help(char *name) |
| 404 | +{ |
| 405 | + puts(""); |
| 406 | + printf("Usage: %s [-h] [-i iterations of the single step test]\n", name); |
| 407 | + puts(""); |
| 408 | + exit(0); |
| 409 | +} |
| 410 | + |
296 | 411 | int main(int argc, char *argv[])
|
297 | 412 | {
|
298 | 413 | struct kvm_vcpu *vcpu;
|
299 | 414 | struct kvm_vm *vm;
|
| 415 | + int opt; |
| 416 | + int ss_iteration = 10000; |
300 | 417 |
|
301 | 418 | vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
302 | 419 | __TEST_REQUIRE(debug_version(vcpu) >= 6,
|
303 | 420 | "Armv8 debug architecture not supported.");
|
304 | 421 | kvm_vm_free(vm);
|
| 422 | + |
| 423 | + while ((opt = getopt(argc, argv, "i:")) != -1) { |
| 424 | + switch (opt) { |
| 425 | + case 'i': |
| 426 | + ss_iteration = atoi(optarg); |
| 427 | + break; |
| 428 | + case 'h': |
| 429 | + default: |
| 430 | + help(argv[0]); |
| 431 | + break; |
| 432 | + } |
| 433 | + } |
| 434 | + |
305 | 435 | test_guest_debug_exceptions();
|
| 436 | + test_single_step_from_userspace(ss_iteration); |
306 | 437 |
|
307 | 438 | return 0;
|
308 | 439 | }
|
0 commit comments