mirror of
https://github.com/freebsd/freebsd-src
synced 2024-07-22 02:37:15 +00:00
arm64/vmm: Add breakpoint and single-stepping support
This will be used to implement parts of bhyve's gdb stub. Three VM capabilities are added, similar to amd64 without monitor mode. Two cause breakpoint and single-step exceptions to be raised to EL2 and then down to bhyve. One lets the gdb stub mask hardware interrupts while single-stepping, since otherwise the guest will handle a timer interrupt before executing the target instruction and thus fail to make progress. Reviewed by: bnovkov, andrew Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D44739
This commit is contained in:
parent
afa166be99
commit
75cb949228
|
@ -295,9 +295,11 @@ struct vre {
|
|||
*/
|
||||
enum vm_cap_type {
|
||||
VM_CAP_HALT_EXIT,
|
||||
VM_CAP_MTRAP_EXIT,
|
||||
VM_CAP_PAUSE_EXIT,
|
||||
VM_CAP_UNRESTRICTED_GUEST,
|
||||
VM_CAP_BRK_EXIT,
|
||||
VM_CAP_SS_EXIT,
|
||||
VM_CAP_MASK_HWINTR,
|
||||
VM_CAP_MAX
|
||||
};
|
||||
|
||||
|
@ -312,6 +314,8 @@ enum vm_exitcode {
|
|||
VM_EXITCODE_PAGING,
|
||||
VM_EXITCODE_SMCCC,
|
||||
VM_EXITCODE_DEBUG,
|
||||
VM_EXITCODE_BRK,
|
||||
VM_EXITCODE_SS,
|
||||
VM_EXITCODE_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
struct vgic_v3;
|
||||
struct vgic_v3_cpu;
|
||||
|
||||
/*
|
||||
* Per-vCPU hypervisor state.
|
||||
*/
|
||||
struct hypctx {
|
||||
struct trapframe tf;
|
||||
|
||||
|
@ -104,6 +107,12 @@ struct hypctx {
|
|||
|
||||
struct vtimer_cpu vtimer_cpu;
|
||||
|
||||
uint64_t setcaps; /* Currently enabled capabilities. */
|
||||
|
||||
/* vCPU state used to handle guest debugging. */
|
||||
uint64_t debug_spsr; /* Saved guest SPSR */
|
||||
uint64_t debug_mdscr; /* Saved guest MDSCR */
|
||||
|
||||
struct vgic_v3_regs vgic_v3_regs;
|
||||
struct vgic_v3_cpu *vgic_cpu;
|
||||
bool has_exception;
|
||||
|
|
|
@ -700,7 +700,14 @@ handle_el1_sync_excp(struct hypctx *hypctx, struct vm_exit *vme_ret,
|
|||
arm64_gen_reg_emul_data(esr_iss, vme_ret);
|
||||
vme_ret->exitcode = VM_EXITCODE_REG_EMUL;
|
||||
break;
|
||||
|
||||
case EXCP_BRK:
|
||||
vmm_stat_incr(hypctx->vcpu, VMEXIT_BRK, 1);
|
||||
vme_ret->exitcode = VM_EXITCODE_BRK;
|
||||
break;
|
||||
case EXCP_SOFTSTP_EL0:
|
||||
vmm_stat_incr(hypctx->vcpu, VMEXIT_SS, 1);
|
||||
vme_ret->exitcode = VM_EXITCODE_SS;
|
||||
break;
|
||||
case EXCP_INSN_ABORT_L:
|
||||
case EXCP_DATA_ABORT_L:
|
||||
vmm_stat_incr(hypctx->vcpu, esr_ec == EXCP_DATA_ABORT_L ?
|
||||
|
@ -1313,6 +1320,7 @@ vmmops_exception(void *vcpui, uint64_t esr, uint64_t far)
|
|||
int
|
||||
vmmops_getcap(void *vcpui, int num, int *retval)
|
||||
{
|
||||
struct hypctx *hypctx = vcpui;
|
||||
int ret;
|
||||
|
||||
ret = ENOENT;
|
||||
|
@ -1322,6 +1330,11 @@ vmmops_getcap(void *vcpui, int num, int *retval)
|
|||
*retval = 1;
|
||||
ret = 0;
|
||||
break;
|
||||
case VM_CAP_BRK_EXIT:
|
||||
case VM_CAP_SS_EXIT:
|
||||
case VM_CAP_MASK_HWINTR:
|
||||
*retval = (hypctx->setcaps & (1ul << num)) != 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1332,6 +1345,68 @@ vmmops_getcap(void *vcpui, int num, int *retval)
|
|||
int
|
||||
vmmops_setcap(void *vcpui, int num, int val)
|
||||
{
|
||||
struct hypctx *hypctx = vcpui;
|
||||
int ret;
|
||||
|
||||
return (ENOENT);
|
||||
ret = 0;
|
||||
|
||||
switch (num) {
|
||||
case VM_CAP_BRK_EXIT:
|
||||
if ((val != 0) == (hypctx->setcaps & (1ul << num)) != 0)
|
||||
break;
|
||||
if (val != 0)
|
||||
hypctx->mdcr_el2 |= MDCR_EL2_TDE;
|
||||
else
|
||||
hypctx->mdcr_el2 &= ~MDCR_EL2_TDE;
|
||||
break;
|
||||
case VM_CAP_SS_EXIT:
|
||||
if ((val != 0) == (hypctx->setcaps & (1ul << num)) != 0)
|
||||
break;
|
||||
|
||||
if (val != 0) {
|
||||
hypctx->debug_spsr |= (hypctx->tf.tf_spsr & PSR_SS);
|
||||
hypctx->debug_mdscr |= hypctx->mdscr_el1 &
|
||||
(MDSCR_SS | MDSCR_KDE);
|
||||
|
||||
hypctx->tf.tf_spsr |= PSR_SS;
|
||||
hypctx->mdscr_el1 |= MDSCR_SS | MDSCR_KDE;
|
||||
hypctx->mdcr_el2 |= MDCR_EL2_TDE;
|
||||
} else {
|
||||
hypctx->tf.tf_spsr &= ~PSR_SS;
|
||||
hypctx->tf.tf_spsr |= hypctx->debug_spsr;
|
||||
hypctx->debug_spsr &= ~PSR_SS;
|
||||
hypctx->mdscr_el1 &= ~(MDSCR_SS | MDSCR_KDE);
|
||||
hypctx->mdscr_el1 |= hypctx->debug_mdscr;
|
||||
hypctx->debug_mdscr &= ~(MDSCR_SS | MDSCR_KDE);
|
||||
hypctx->mdcr_el2 &= ~MDCR_EL2_TDE;
|
||||
}
|
||||
break;
|
||||
case VM_CAP_MASK_HWINTR:
|
||||
if ((val != 0) == (hypctx->setcaps & (1ul << num)) != 0)
|
||||
break;
|
||||
|
||||
if (val != 0) {
|
||||
hypctx->debug_spsr |= (hypctx->tf.tf_spsr &
|
||||
(PSR_I | PSR_F));
|
||||
hypctx->tf.tf_spsr |= PSR_I | PSR_F;
|
||||
} else {
|
||||
hypctx->tf.tf_spsr &= ~(PSR_I | PSR_F);
|
||||
hypctx->tf.tf_spsr |= (hypctx->debug_spsr &
|
||||
(PSR_I | PSR_F));
|
||||
hypctx->debug_spsr &= ~(PSR_I | PSR_F);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (val == 0)
|
||||
hypctx->setcaps &= ~(1ul << num);
|
||||
else
|
||||
hypctx->setcaps |= (1ul << num);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
|
|
@ -161,5 +161,7 @@ VMM_STAT(VMEXIT_INSN_ABORT, "number of vmexits for an instruction abort");
|
|||
VMM_STAT(VMEXIT_UNHANDLED_SYNC, "number of vmexits for an unhandled synchronous exception");
|
||||
VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq");
|
||||
VMM_STAT(VMEXIT_FIQ, "number of vmexits for an interrupt");
|
||||
VMM_STAT(VMEXIT_BRK, "number of vmexits for a breakpoint exception");
|
||||
VMM_STAT(VMEXIT_SS, "number of vmexits for a single-step exception");
|
||||
VMM_STAT(VMEXIT_UNHANDLED_EL2, "number of vmexits for an unhandled EL2 exception");
|
||||
VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception");
|
||||
|
|
|
@ -140,6 +140,8 @@ VMM_STAT_DECLARE(VMEXIT_INSN_ABORT);
|
|||
VMM_STAT_DECLARE(VMEXIT_UNHANDLED_SYNC);
|
||||
VMM_STAT_DECLARE(VMEXIT_IRQ);
|
||||
VMM_STAT_DECLARE(VMEXIT_FIQ);
|
||||
VMM_STAT_DECLARE(VMEXIT_BRK);
|
||||
VMM_STAT_DECLARE(VMEXIT_SS);
|
||||
VMM_STAT_DECLARE(VMEXIT_UNHANDLED_EL2);
|
||||
VMM_STAT_DECLARE(VMEXIT_UNHANDLED);
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue