The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=75cb949228bb97e49f35fd2b2585293a6b9212aa

commit 75cb949228bb97e49f35fd2b2585293a6b9212aa
Author:     Mark Johnston <ma...@freebsd.org>
AuthorDate: 2024-06-04 18:58:08 +0000
Commit:     Mark Johnston <ma...@freebsd.org>
CommitDate: 2024-06-04 18:58:08 +0000

    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
---
 sys/arm64/include/vmm.h   |  6 +++-
 sys/arm64/vmm/arm64.h     |  9 ++++++
 sys/arm64/vmm/vmm_arm64.c | 79 +++++++++++++++++++++++++++++++++++++++++++++--
 sys/arm64/vmm/vmm_stat.c  |  2 ++
 sys/arm64/vmm/vmm_stat.h  |  2 ++
 5 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/sys/arm64/include/vmm.h b/sys/arm64/include/vmm.h
index 8e2c9c868635..c06d2ad947e4 100644
--- a/sys/arm64/include/vmm.h
+++ b/sys/arm64/include/vmm.h
@@ -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
 };
 
diff --git a/sys/arm64/vmm/arm64.h b/sys/arm64/vmm/arm64.h
index 43459d14e143..8cfe77dcde6f 100644
--- a/sys/arm64/vmm/arm64.h
+++ b/sys/arm64/vmm/arm64.h
@@ -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;
diff --git a/sys/arm64/vmm/vmm_arm64.c b/sys/arm64/vmm/vmm_arm64.c
index e71761f9ccef..e0547bcef914 100644
--- a/sys/arm64/vmm/vmm_arm64.c
+++ b/sys/arm64/vmm/vmm_arm64.c
@@ -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;
+
+       ret = 0;
 
-       return (ENOENT);
+       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);
 }
diff --git a/sys/arm64/vmm/vmm_stat.c b/sys/arm64/vmm/vmm_stat.c
index 858ce980843a..05ece6f30579 100644
--- a/sys/arm64/vmm/vmm_stat.c
+++ b/sys/arm64/vmm/vmm_stat.c
@@ -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");
diff --git a/sys/arm64/vmm/vmm_stat.h b/sys/arm64/vmm/vmm_stat.h
index b0a06ef79253..402fa2f1b1e4 100644
--- a/sys/arm64/vmm/vmm_stat.h
+++ b/sys/arm64/vmm/vmm_stat.h
@@ -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

Reply via email to