From: Andrew Cooper <andrew.cooper3@citrix.com>
To: Xen-devel <xen-devel@lists.xen.org>
Cc: "Kevin Tian" <kevin.tian@intel.com>,
"Tamas K Lengyel" <tamas@tklengyel.com>,
"Wei Liu" <wei.liu2@citrix.com>,
"Jan Beulich" <JBeulich@suse.com>,
"Razvan Cojocaru" <rcojocaru@bitdefender.com>,
"Andrew Cooper" <andrew.cooper3@citrix.com>,
"Jun Nakajima" <jun.nakajima@intel.com>,
"Boris Ostrovsky" <boris.ostrovsky@oracle.com>,
"Brian Woods" <brian.woods@amd.com>,
"Suravee Suthikulpanit" <suravee.suthikulpanit@amd.com>,
"Roger Pau Monné" <roger.pau@citrix.com>
Subject: [PATCH 08/11] x86/hvm: RFC - PROBABLY BROKEN - Defer all debugging/monitor actions to {svm, vmx}_inject_event()
Date: Mon, 4 Jun 2018 14:59:12 +0100 [thread overview]
Message-ID: <1528120755-17455-9-git-send-email-andrew.cooper3@citrix.com> (raw)
In-Reply-To: <1528120755-17455-1-git-send-email-andrew.cooper3@citrix.com>
Currently, there is a lot of functionality in the #DB intercepts, and some
repeated functionality in the *_inject_event() logic.
The gdbsx code is implemented at both levels (albeit differently for #BP,
which is presumably due to the fact that the old emulator behaviour used to be
to move %rip forwards for traps), while the monitor behaviour only exists at
the intercept level.
Updating of %dr6 is implemented (buggily) at both levels, but having it at
both levels is problematic to implement correctly.
Rearrange the logic to have nothing interesting at the intercept level, and
everything implemented at the injection level. Amongst other things, this
means that the monitor subsystem will pick up debug actions from emulated
events.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Wei Liu <wei.liu2@citrix.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
CC: Jun Nakajima <jun.nakajima@intel.com>
CC: Kevin Tian <kevin.tian@intel.com>
CC: Boris Ostrovsky <boris.ostrovsky@oracle.com>
CC: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
CC: Brian Woods <brian.woods@amd.com>
CC: Razvan Cojocaru <rcojocaru@bitdefender.com>
CC: Tamas K Lengyel <tamas@tklengyel.com>
This is RFC because it probably breaks introspection, as injection replies
from the introspection engine will (probably, but I haven't confirmed) trigger
new monitor events.
First of all, monitoring emulated debug events is a change in behaviour,
although IMO it is more of a bugfix than a new feature. Also, similar changes
will happen to other monitored events as we try to unify the
intercept/emulation paths.
As for the recursive triggering of monitor events, I was considering extending
the monitor infrastructure to have a "in monitor reply" boolean which causes
hvm_monitor_debug() to ignore the recursive request.
Does this plan sound ok, or have I missed something more subtle with monitor
handling?
---
xen/arch/x86/hvm/svm/svm.c | 127 ++++++++++++++++++++++++---------------------
xen/arch/x86/hvm/vmx/vmx.c | 102 +++++++++++++++---------------------
2 files changed, 110 insertions(+), 119 deletions(-)
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index c06bd68..df5f9ed 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -1437,19 +1437,49 @@ static void svm_inject_event(const struct x86_event *event)
switch ( _event.vector | -(_event.type == X86_EVENTTYPE_SW_INTERRUPT) )
{
case TRAP_debug:
- if ( regs->eflags & X86_EFLAGS_TF )
- {
- __restore_debug_registers(vmcb, curr);
- vmcb_set_dr6(vmcb, vmcb_get_dr6(vmcb) | DR_STEP);
- }
+ /*
+ * On AMD hardware, a #DB exception:
+ * 1) Merges new status bits into %dr6
+ * 2) Clears %dr7.gd and MSR_DEBUGCTL.{LBR,BTF}
+ *
+ * Item 1 is done by hardware before a #DB intercepted vmexit, but we
+ * may end up here from emulation so have to repeat it ourselves.
+ * Item 2 is done by hardware when injecting a #DB exception.
+ */
+ __restore_debug_registers(vmcb, curr);
+ vmcb_set_dr6(vmcb, vmcb_get_dr6(vmcb) | event->pending_dbg);
+
/* fall through */
case TRAP_int3:
if ( curr->domain->debugger_attached )
{
/* Debug/Int3: Trap to debugger. */
+ if ( _event.vector == TRAP_int3 )
+ {
+ /* N.B. Can't use __update_guest_eip() for risk of recusion. */
+ regs->rip += _event.insn_len;
+ regs->eflags &= ~X86_EFLAGS_RF;
+ curr->arch.gdbsx_vcpu_event = TRAP_int3;
+ }
+
domain_pause_for_debugger();
return;
}
+ else
+ {
+ int rc = hvm_monitor_debug(regs->rip,
+ _event.vector == TRAP_debug
+ ? HVM_MONITOR_DEBUG_EXCEPTION
+ : HVM_MONITOR_SOFTWARE_BREAKPOINT,
+ _event.type, _event.insn_len);
+ if ( rc < 0 )
+ {
+ gprintk(XENLOG_ERR, "Monitor debug error %d\n", rc);
+ return svm_crash_or_fault(curr);
+ }
+ if ( rc )
+ return; /* VCPU paused. Wait for monitor. */
+ }
break;
case TRAP_page_fault:
@@ -2775,67 +2805,46 @@ void svm_vmexit_handler(struct cpu_user_regs *regs)
case VMEXIT_ICEBP:
case VMEXIT_EXCEPTION_DB:
- if ( !v->domain->debugger_attached )
+ case VMEXIT_EXCEPTION_BP:
+ {
+ unsigned int vec, type, len, extra;
+
+ switch ( exit_reason )
{
- int rc;
- unsigned int trap_type;
+ case VMEXIT_ICEBP:
+ vec = TRAP_debug;
+ type = X86_EVENTTYPE_PRI_SW_EXCEPTION;
+ len = __get_instruction_length(v, INSTR_ICEBP);
+ extra = 0;
+ break;
- if ( likely(exit_reason != VMEXIT_ICEBP) )
- {
- trap_type = X86_EVENTTYPE_HW_EXCEPTION;
- inst_len = 0;
- }
- else
- {
- trap_type = X86_EVENTTYPE_PRI_SW_EXCEPTION;
- inst_len = __get_instruction_length(v, INSTR_ICEBP);
- }
+ case VMEXIT_EXCEPTION_DB:
+ vec = TRAP_debug;
+ type = X86_EVENTTYPE_HW_EXCEPTION;
+ len = 0;
+ /* #DB - Hardware has already updated %dr6 for us. */
+ extra = vmcb_get_dr6(vmcb) ^ X86_DR6_DEFAULT;
+ break;
- rc = hvm_monitor_debug(regs->rip,
- HVM_MONITOR_DEBUG_EXCEPTION,
- trap_type, inst_len);
- if ( rc < 0 )
- goto unexpected_exit_type;
- if ( !rc )
- hvm_inject_exception(TRAP_debug,
- trap_type, inst_len, X86_EVENT_NO_EC,
- exit_reason == VMEXIT_ICEBP ? 0 :
- /* #DB - Hardware already updated dr6. */
- vmcb_get_dr6(vmcb) ^ X86_DR6_DEFAULT);
- }
- else
- domain_pause_for_debugger();
- break;
+ case VMEXIT_EXCEPTION_BP:
+ vec = TRAP_int3;
+ type = X86_EVENTTYPE_SW_EXCEPTION;
+ len = __get_instruction_length(v, INSTR_INT3);
+ extra = 0; /* N/A */
+ break;
- case VMEXIT_EXCEPTION_BP:
- inst_len = __get_instruction_length(v, INSTR_INT3);
+ default:
+ ASSERT_UNREACHABLE();
+ goto unexpected_exit_type;
+ }
- if ( inst_len == 0 )
- break;
+ /* __get_instruction_length() failure. #GP queued up. */
+ if ( type >= X86_EVENTTYPE_SW_INTERRUPT && !len )
+ break;
- if ( v->domain->debugger_attached )
- {
- /* AMD Vol2, 15.11: INT3, INTO, BOUND intercepts do not update RIP. */
- __update_guest_eip(regs, inst_len);
- current->arch.gdbsx_vcpu_event = TRAP_int3;
- domain_pause_for_debugger();
- }
- else
- {
- int rc;
-
- rc = hvm_monitor_debug(regs->rip,
- HVM_MONITOR_SOFTWARE_BREAKPOINT,
- X86_EVENTTYPE_SW_EXCEPTION,
- inst_len);
- if ( rc < 0 )
- goto unexpected_exit_type;
- if ( !rc )
- hvm_inject_exception(TRAP_int3,
- X86_EVENTTYPE_SW_EXCEPTION,
- inst_len, X86_EVENT_NO_EC, 0 /* N/A */);
- }
+ hvm_inject_exception(vec, type, len, X86_EVENT_NO_EC, extra);
break;
+ }
case VMEXIT_EXCEPTION_NM:
svm_fpu_dirty_intercept();
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 39c9ddc..f59ef88 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -1778,15 +1778,21 @@ static void vmx_inject_event(const struct x86_event *event)
unsigned long intr_info;
struct vcpu *curr = current;
struct x86_event _event = *event;
+ struct cpu_user_regs *regs = guest_cpu_user_regs();
switch ( _event.vector | -(_event.type == X86_EVENTTYPE_SW_INTERRUPT) )
{
case TRAP_debug:
- if ( guest_cpu_user_regs()->eflags & X86_EFLAGS_TF )
- {
- __restore_debug_registers(curr);
- write_debugreg(6, read_debugreg(6) | DR_STEP);
- }
+ /*
+ * On Intel hardware, a #DB exception:
+ * 1) Merges new status bits into %dr6
+ * 2) Clears %dr7.gd and MSR_DEBUGCTL.LBR
+ *
+ * All actions are left up to the hypervisor to perform.
+ */
+ __restore_debug_registers(curr);
+ write_debugreg(6, read_debugreg(6) | event->pending_dbg);
+
if ( !nestedhvm_vcpu_in_guestmode(curr) ||
!nvmx_intercepts_exception(curr, TRAP_debug, _event.error_code) )
{
@@ -1797,16 +1803,39 @@ static void vmx_inject_event(const struct x86_event *event)
__vmread(GUEST_IA32_DEBUGCTL, &val);
__vmwrite(GUEST_IA32_DEBUGCTL, val & ~IA32_DEBUGCTLMSR_LBR);
}
- if ( cpu_has_monitor_trap_flag )
- break;
+
/* fall through */
case TRAP_int3:
if ( curr->domain->debugger_attached )
{
/* Debug/Int3: Trap to debugger. */
+ if ( _event.vector == TRAP_int3 )
+ {
+ /* N.B. Can't use __update_guest_eip() for risk of recusion. */
+ regs->rip += _event.insn_len;
+ regs->eflags &= ~X86_EFLAGS_RF;
+ curr->arch.gdbsx_vcpu_event = TRAP_int3;
+ }
+
domain_pause_for_debugger();
return;
}
+ else
+ {
+ int rc = hvm_monitor_debug(regs->rip,
+ _event.vector == TRAP_debug
+ ? HVM_MONITOR_DEBUG_EXCEPTION
+ : HVM_MONITOR_SOFTWARE_BREAKPOINT,
+ _event.type, _event.insn_len);
+ if ( rc < 0 )
+ {
+ gprintk(XENLOG_ERR, "Monitor debug error %d\n", rc);
+ domain_crash(curr->domain);
+ return;
+ }
+ if ( rc )
+ return; /* VCPU paused. Wait for monitor. */
+ }
break;
case TRAP_page_fault:
@@ -3693,61 +3722,17 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
switch ( vector )
{
case TRAP_debug:
- /*
- * Updates DR6 where debugger can peek (See 3B 23.2.1,
- * Table 23-1, "Exit Qualification for Debug Exceptions").
- */
__vmread(EXIT_QUALIFICATION, &exit_qualification);
HVMTRACE_1D(TRAP_DEBUG, exit_qualification);
- __restore_debug_registers(v);
- write_debugreg(6, exit_qualification | DR_STATUS_RESERVED_ONE);
- if ( !v->domain->debugger_attached )
- {
- unsigned long insn_len = 0;
- int rc;
- unsigned long trap_type = MASK_EXTR(intr_info,
- INTR_INFO_INTR_TYPE_MASK);
-
- if ( trap_type >= X86_EVENTTYPE_SW_INTERRUPT )
- __vmread(VM_EXIT_INSTRUCTION_LEN, &insn_len);
-
- rc = hvm_monitor_debug(regs->rip,
- HVM_MONITOR_DEBUG_EXCEPTION,
- trap_type, insn_len);
-
- if ( rc < 0 )
- goto exit_and_crash;
- if ( !rc )
- vmx_propagate_intr(intr_info, exit_qualification);
- }
- else
- domain_pause_for_debugger();
+ vmx_propagate_intr(intr_info, exit_qualification);
break;
+
case TRAP_int3:
+ case TRAP_alignment_check:
HVMTRACE_1D(TRAP, vector);
- if ( !v->domain->debugger_attached )
- {
- unsigned long insn_len;
- int rc;
-
- __vmread(VM_EXIT_INSTRUCTION_LEN, &insn_len);
- rc = hvm_monitor_debug(regs->rip,
- HVM_MONITOR_SOFTWARE_BREAKPOINT,
- X86_EVENTTYPE_SW_EXCEPTION,
- insn_len);
-
- if ( rc < 0 )
- goto exit_and_crash;
- if ( !rc )
- vmx_propagate_intr(intr_info, 0 /* N/A */);
- }
- else
- {
- update_guest_eip(); /* Safe: INT3 */
- v->arch.gdbsx_vcpu_event = TRAP_int3;
- domain_pause_for_debugger();
- }
+ vmx_propagate_intr(intr_info, 0 /* N/A */);
break;
+
case TRAP_no_device:
HVMTRACE_1D(TRAP, vector);
vmx_fpu_dirty_intercept();
@@ -3777,10 +3762,7 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
hvm_inject_page_fault(regs->error_code, exit_qualification);
break;
- case TRAP_alignment_check:
- HVMTRACE_1D(TRAP, vector);
- vmx_propagate_intr(intr_info, 0 /* N/A */);
- break;
+
case TRAP_nmi:
if ( MASK_EXTR(intr_info, INTR_INFO_INTR_TYPE_MASK) !=
X86_EVENTTYPE_NMI )
--
2.1.4
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
next prev parent reply other threads:[~2018-06-04 13:59 UTC|newest]
Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-06-04 13:59 [PATCH 00/11] Fixes to debugging facilities Andrew Cooper
2018-06-04 13:59 ` [PATCH 01/11] x86/svm Fixes and cleanup to svm_inject_event() Andrew Cooper
2018-06-06 13:37 ` Jan Beulich
2018-07-16 13:33 ` Andrew Cooper
2018-07-17 2:01 ` Boris Ostrovsky
2018-06-04 13:59 ` [PATCH 02/11] x86/vmx: Don't clobber %dr6 while debugging state is lazy Andrew Cooper
2018-06-06 10:16 ` Roger Pau Monné
2018-06-06 13:50 ` Jan Beulich
2018-06-06 14:16 ` Andrew Cooper
2018-06-07 11:05 ` Jan Beulich
2018-06-08 15:58 ` Andrew Cooper
2018-06-08 16:10 ` Jan Beulich
2018-07-17 9:28 ` Andrew Cooper
2018-07-19 2:14 ` Tian, Kevin
2018-06-04 13:59 ` [PATCH 03/11] x86: Initialise debug registers correctly Andrew Cooper
2018-06-06 10:34 ` Roger Pau Monné
2018-06-08 15:23 ` Andrew Cooper
2018-06-06 13:56 ` Jan Beulich
2018-06-08 15:42 ` Andrew Cooper
2018-06-08 16:14 ` Jan Beulich
2018-06-04 13:59 ` [PATCH 04/11] x86: Fix calculation of %dr6/7 reserved bits Andrew Cooper
2018-06-06 14:16 ` Jan Beulich
2018-06-06 14:50 ` Andrew Cooper
2018-06-06 14:52 ` Andrew Cooper
2018-06-06 15:11 ` Jan Beulich
2018-06-06 15:49 ` Roger Pau Monné
2018-06-06 15:59 ` Andrew Cooper
2018-06-06 17:36 ` Roger Pau Monné
2018-06-04 13:59 ` [PATCH 05/11] x86/emul: Unfold %cr4.de handling in x86emul_read_dr() Andrew Cooper
2018-06-06 14:20 ` Jan Beulich
2018-06-08 16:03 ` Andrew Cooper
2018-06-08 16:16 ` Jan Beulich
2018-06-06 15:54 ` Roger Pau Monné
2018-06-04 13:59 ` [PATCH 06/11] x86: Reorganise and rename debug register fields in struct vcpu Andrew Cooper
2018-06-06 15:00 ` Jan Beulich
2018-06-06 15:21 ` Andrew Cooper
2018-06-07 10:59 ` Jan Beulich
2018-06-06 16:22 ` Roger Pau Monné
2018-06-04 13:59 ` [PATCH 07/11] x86/emul: Add pending_dbg field to x86_event Andrew Cooper
2018-06-06 16:46 ` Roger Pau Monné
2018-06-06 16:50 ` Andrew Cooper
2018-06-06 17:03 ` Roger Pau Monné
2018-06-08 12:34 ` Jan Beulich
2018-06-08 12:48 ` Andrew Cooper
2018-06-04 13:59 ` Andrew Cooper [this message]
2018-06-04 14:53 ` [PATCH 08/11] x86/hvm: RFC - PROBABLY BROKEN - Defer all debugging/monitor actions to {svm, vmx}_inject_event() Razvan Cojocaru
2018-06-04 15:07 ` Razvan Cojocaru
2018-06-06 17:02 ` Roger Pau Monné
2018-06-08 13:00 ` Jan Beulich
2018-06-08 13:13 ` Andrew Cooper
2018-06-04 13:59 ` [PATCH 09/11] x86: Fix merging of new status bits into %dr6 Andrew Cooper
2018-06-06 17:09 ` Roger Pau Monné
2018-06-08 13:09 ` Jan Beulich
2018-06-04 13:59 ` [PATCH 10/11] x86/vmx: Work around VMEntry failure when Single Stepping in an STI shadow Andrew Cooper
2018-09-03 10:39 ` Ping VT-x: " Andrew Cooper
2018-09-04 5:27 ` Tian, Kevin
2018-06-04 13:59 ` [PATCH 11/11] x86/dbg: Cleanup of legacy dr6 constants Andrew Cooper
2018-06-06 17:10 ` Roger Pau Monné
2018-06-08 13:12 ` Jan Beulich
2018-06-04 15:39 ` [PATCH 00/11] Fixes to debugging facilities Andrew Cooper
2018-06-04 17:09 ` Razvan Cojocaru
2018-06-04 17:18 ` Andrew Cooper
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1528120755-17455-9-git-send-email-andrew.cooper3@citrix.com \
--to=andrew.cooper3@citrix.com \
--cc=JBeulich@suse.com \
--cc=boris.ostrovsky@oracle.com \
--cc=brian.woods@amd.com \
--cc=jun.nakajima@intel.com \
--cc=kevin.tian@intel.com \
--cc=rcojocaru@bitdefender.com \
--cc=roger.pau@citrix.com \
--cc=suravee.suthikulpanit@amd.com \
--cc=tamas@tklengyel.com \
--cc=wei.liu2@citrix.com \
--cc=xen-devel@lists.xen.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).