* [XEN PATCH v2] x86/monitor: Add new monitor event to catch I/O instructions
@ 2023-03-15 18:54 Dmitry Isaykin
2023-03-16 12:32 ` Tamas K Lengyel
2023-03-17 11:13 ` Andrew Cooper
0 siblings, 2 replies; 3+ messages in thread
From: Dmitry Isaykin @ 2023-03-15 18:54 UTC (permalink / raw)
To: xen-devel
Cc: Dmitry Isaykin, Wei Liu, Anthony PERARD, Juergen Gross,
Andrew Cooper, George Dunlap, Jan Beulich, Julien Grall,
Stefano Stabellini, Tamas K Lengyel, Alexandru Isaila,
Petre Pircalabu, Roger Pau Monné,
Jun Nakajima, Kevin Tian, Anton Belousov
Adds monitor support for I/O instructions.
Signed-off-by: Dmitry Isaykin <isaikin-dmitry@yandex.ru>
Signed-off-by: Anton Belousov <abelousov@ptsecurity.com>
---
Changes in v2:
* Handled INS and OUTS instructions too
* Added I/O monitoring support for AMD
* Rename functions and structures (remove "_instruction" part)
* Reorder parameters of hvm_monitor_io to match handle_pio's order
* Change type of string_ins parameter to bool
* Change vm_event_io structure
* Handle monitor_traps's return status
---
tools/include/xenctrl.h | 1 +
tools/libs/ctrl/xc_monitor.c | 13 ++++++++
xen/arch/x86/hvm/monitor.c | 21 +++++++++++++
xen/arch/x86/hvm/svm/svm.c | 25 +++++++++++-----
xen/arch/x86/hvm/vmx/vmx.c | 41 ++++++++++++++++++--------
xen/arch/x86/include/asm/domain.h | 1 +
xen/arch/x86/include/asm/hvm/monitor.h | 3 ++
xen/arch/x86/include/asm/monitor.h | 3 +-
xen/arch/x86/monitor.c | 13 ++++++++
xen/include/public/domctl.h | 1 +
xen/include/public/vm_event.h | 10 +++++++
11 files changed, 110 insertions(+), 22 deletions(-)
diff --git a/tools/include/xenctrl.h b/tools/include/xenctrl.h
index 23037874d3..05967ecc92 100644
--- a/tools/include/xenctrl.h
+++ b/tools/include/xenctrl.h
@@ -2102,6 +2102,7 @@ int xc_monitor_emul_unimplemented(xc_interface *xch, uint32_t domain_id,
bool enable);
int xc_monitor_vmexit(xc_interface *xch, uint32_t domain_id, bool enable,
bool sync);
+int xc_monitor_io(xc_interface *xch, uint32_t domain_id, bool enable);
/**
* This function enables / disables emulation for each REP for a
* REP-compatible instruction.
diff --git a/tools/libs/ctrl/xc_monitor.c b/tools/libs/ctrl/xc_monitor.c
index c5fa62ff30..3cb96f444f 100644
--- a/tools/libs/ctrl/xc_monitor.c
+++ b/tools/libs/ctrl/xc_monitor.c
@@ -261,6 +261,19 @@ int xc_monitor_vmexit(xc_interface *xch, uint32_t domain_id, bool enable,
return do_domctl(xch, &domctl);
}
+int xc_monitor_io(xc_interface *xch, uint32_t domain_id, bool enable)
+{
+ DECLARE_DOMCTL;
+
+ domctl.cmd = XEN_DOMCTL_monitor_op;
+ domctl.domain = domain_id;
+ domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
+ : XEN_DOMCTL_MONITOR_OP_DISABLE;
+ domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_IO;
+
+ return do_domctl(xch, &domctl);
+}
+
/*
* Local variables:
* mode: C
diff --git a/xen/arch/x86/hvm/monitor.c b/xen/arch/x86/hvm/monitor.c
index a11cd76f4d..ff958b6c05 100644
--- a/xen/arch/x86/hvm/monitor.c
+++ b/xen/arch/x86/hvm/monitor.c
@@ -346,6 +346,27 @@ int hvm_monitor_vmexit(unsigned long exit_reason,
return monitor_traps(curr, ad->monitor.vmexit_sync, &req);
}
+int hvm_monitor_io(uint16_t port, unsigned int bytes,
+ int dir, bool string_ins)
+{
+ struct vcpu *curr = current;
+ struct arch_domain *ad = &curr->domain->arch;
+ vm_event_request_t req = {};
+
+ if ( !ad->monitor.io_enabled )
+ return 0;
+
+ req.reason = VM_EVENT_REASON_IO_INSTRUCTION;
+ req.u.io.data_size = bytes;
+ req.u.io.port = port;
+ req.u.io.dir = dir;
+ req.u.io.string_ins = string_ins;
+
+ set_npt_base(curr, &req);
+
+ return monitor_traps(curr, true, &req);
+}
+
/*
* Local variables:
* mode: C
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index a43bcf2e92..49225f48a7 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -2939,17 +2939,26 @@ void svm_vmexit_handler(void)
break;
case VMEXIT_IOIO:
- if ( (vmcb->exitinfo1 & (1u<<2)) == 0 )
+ {
+ uint16_t port = (vmcb->exitinfo1 >> 16) & 0xFFFF;
+ int bytes = ((vmcb->exitinfo1 >> 4) & 0x07);
+ int dir = (vmcb->exitinfo1 & 1) ? IOREQ_READ : IOREQ_WRITE;
+ bool string_ins = (vmcb->exitinfo1 & (1u<<2));
+ int rc = hvm_monitor_io(port, bytes, dir, string_ins);
+ if ( rc < 0 )
+ goto unexpected_exit_type;
+ if ( !rc )
{
- uint16_t port = (vmcb->exitinfo1 >> 16) & 0xFFFF;
- int bytes = ((vmcb->exitinfo1 >> 4) & 0x07);
- int dir = (vmcb->exitinfo1 & 1) ? IOREQ_READ : IOREQ_WRITE;
- if ( handle_pio(port, bytes, dir) )
- __update_guest_eip(regs, vmcb->exitinfo2 - vmcb->rip);
+ if ( !string_ins )
+ {
+ if ( handle_pio(port, bytes, dir) )
+ __update_guest_eip(regs, vmcb->exitinfo2 - vmcb->rip);
+ }
+ else if ( !hvm_emulate_one_insn(x86_insn_is_portio, "port I/O") )
+ hvm_inject_hw_exception(TRAP_gp_fault, 0);
}
- else if ( !hvm_emulate_one_insn(x86_insn_is_portio, "port I/O") )
- hvm_inject_hw_exception(TRAP_gp_fault, 0);
break;
+ }
case VMEXIT_CR0_READ ... VMEXIT_CR15_READ:
case VMEXIT_CR0_WRITE ... VMEXIT_CR15_WRITE:
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 278b829f73..a7d7f288fb 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -4566,23 +4566,38 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
break;
case EXIT_REASON_IO_INSTRUCTION:
+ {
+ uint16_t port;
+ int bytes, dir;
+ bool string_ins;
+ int rc;
+
__vmread(EXIT_QUALIFICATION, &exit_qualification);
- if ( exit_qualification & 0x10 )
- {
- /* INS, OUTS */
- if ( !hvm_emulate_one_insn(x86_insn_is_portio, "port I/O") )
- hvm_inject_hw_exception(TRAP_gp_fault, 0);
- }
- else
+
+ port = (exit_qualification >> 16) & 0xFFFF;
+ bytes = (exit_qualification & 0x07) + 1;
+ dir = (exit_qualification & 0x08) ? IOREQ_READ : IOREQ_WRITE;
+ string_ins = (exit_qualification & 0x10);
+ rc = hvm_monitor_io(port, bytes, dir, string_ins);
+ if ( rc < 0 )
+ goto exit_and_crash;
+ if ( !rc )
{
- /* IN, OUT */
- uint16_t port = (exit_qualification >> 16) & 0xFFFF;
- int bytes = (exit_qualification & 0x07) + 1;
- int dir = (exit_qualification & 0x08) ? IOREQ_READ : IOREQ_WRITE;
- if ( handle_pio(port, bytes, dir) )
- update_guest_eip(); /* Safe: IN, OUT */
+ if ( exit_qualification & 0x10 )
+ {
+ /* INS, OUTS */
+ if ( !hvm_emulate_one_insn(x86_insn_is_portio, "port I/O") )
+ hvm_inject_hw_exception(TRAP_gp_fault, 0);
+ }
+ else
+ {
+ /* IN, OUT */
+ if ( handle_pio(port, bytes, dir) )
+ update_guest_eip(); /* Safe: IN, OUT */
+ }
}
break;
+ }
case EXIT_REASON_INVD:
case EXIT_REASON_WBINVD:
diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h
index b5354c3677..86bd3493e6 100644
--- a/xen/arch/x86/include/asm/domain.h
+++ b/xen/arch/x86/include/asm/domain.h
@@ -428,6 +428,7 @@ struct arch_domain
unsigned int descriptor_access_enabled : 1;
unsigned int guest_request_userspace_enabled : 1;
unsigned int emul_unimplemented_enabled : 1;
+ unsigned int io_enabled : 1;
/*
* By default all events are sent.
* This is used to filter out pagefaults.
diff --git a/xen/arch/x86/include/asm/hvm/monitor.h b/xen/arch/x86/include/asm/hvm/monitor.h
index 639f6dfa37..fc35490a8d 100644
--- a/xen/arch/x86/include/asm/hvm/monitor.h
+++ b/xen/arch/x86/include/asm/hvm/monitor.h
@@ -54,6 +54,9 @@ bool hvm_monitor_check_p2m(unsigned long gla, gfn_t gfn, uint32_t pfec,
int hvm_monitor_vmexit(unsigned long exit_reason,
unsigned long exit_qualification);
+int hvm_monitor_io(uint16_t port, unsigned int bytes,
+ int dir, bool string_ins);
+
#endif /* __ASM_X86_HVM_MONITOR_H__ */
/*
diff --git a/xen/arch/x86/include/asm/monitor.h b/xen/arch/x86/include/asm/monitor.h
index d8d54c5f23..96e6a9d0d8 100644
--- a/xen/arch/x86/include/asm/monitor.h
+++ b/xen/arch/x86/include/asm/monitor.h
@@ -90,7 +90,8 @@ static inline uint32_t arch_monitor_get_capabilities(struct domain *d)
(1U << XEN_DOMCTL_MONITOR_EVENT_WRITE_CTRLREG) |
(1U << XEN_DOMCTL_MONITOR_EVENT_EMUL_UNIMPLEMENTED) |
(1U << XEN_DOMCTL_MONITOR_EVENT_INGUEST_PAGEFAULT) |
- (1U << XEN_DOMCTL_MONITOR_EVENT_VMEXIT));
+ (1U << XEN_DOMCTL_MONITOR_EVENT_VMEXIT) |
+ (1U << XEN_DOMCTL_MONITOR_EVENT_IO));
if ( hvm_is_singlestep_supported() )
capabilities |= (1U << XEN_DOMCTL_MONITOR_EVENT_SINGLESTEP);
diff --git a/xen/arch/x86/monitor.c b/xen/arch/x86/monitor.c
index 30ca71432c..d4857faf8a 100644
--- a/xen/arch/x86/monitor.c
+++ b/xen/arch/x86/monitor.c
@@ -346,6 +346,19 @@ int arch_monitor_domctl_event(struct domain *d,
break;
}
+ case XEN_DOMCTL_MONITOR_EVENT_IO:
+ {
+ bool old_status = ad->monitor.io_enabled;
+
+ if ( unlikely(old_status == requested_status) )
+ return -EEXIST;
+
+ domain_pause(d);
+ ad->monitor.io_enabled = requested_status;
+ domain_unpause(d);
+ break;
+ }
+
default:
/*
* Should not be reached unless arch_monitor_get_capabilities() is
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 51be28c3de..7280e9f968 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -1063,6 +1063,7 @@ struct xen_domctl_psr_cmt_op {
/* Enabled by default */
#define XEN_DOMCTL_MONITOR_EVENT_INGUEST_PAGEFAULT 11
#define XEN_DOMCTL_MONITOR_EVENT_VMEXIT 12
+#define XEN_DOMCTL_MONITOR_EVENT_IO 13
struct xen_domctl_monitor_op {
uint32_t op; /* XEN_DOMCTL_MONITOR_OP_* */
diff --git a/xen/include/public/vm_event.h b/xen/include/public/vm_event.h
index 0035c26e12..1e4b6063f5 100644
--- a/xen/include/public/vm_event.h
+++ b/xen/include/public/vm_event.h
@@ -160,6 +160,8 @@
#define VM_EVENT_REASON_EMUL_UNIMPLEMENTED 14
/* VMEXIT */
#define VM_EVENT_REASON_VMEXIT 15
+/* IN/OUT Instruction executed */
+#define VM_EVENT_REASON_IO_INSTRUCTION 16
/* Supported values for the vm_event_write_ctrlreg index. */
#define VM_EVENT_X86_CR0 0
@@ -388,6 +390,13 @@ struct vm_event_vmexit {
} arch;
};
+struct vm_event_io {
+ uint32_t data_size;
+ uint16_t port;
+ uint8_t dir; /* IOREQ_READ or IOREQ_WRITE */
+ uint8_t string_ins;
+};
+
typedef struct vm_event_st {
uint32_t version; /* VM_EVENT_INTERFACE_VERSION */
uint32_t flags; /* VM_EVENT_FLAG_* */
@@ -409,6 +418,7 @@ typedef struct vm_event_st {
struct vm_event_debug debug_exception;
struct vm_event_cpuid cpuid;
struct vm_event_vmexit vmexit;
+ struct vm_event_io io;
union {
struct vm_event_interrupt_x86 x86;
} interrupt;
--
2.39.2
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [XEN PATCH v2] x86/monitor: Add new monitor event to catch I/O instructions
2023-03-15 18:54 [XEN PATCH v2] x86/monitor: Add new monitor event to catch I/O instructions Dmitry Isaykin
@ 2023-03-16 12:32 ` Tamas K Lengyel
2023-03-17 11:13 ` Andrew Cooper
1 sibling, 0 replies; 3+ messages in thread
From: Tamas K Lengyel @ 2023-03-16 12:32 UTC (permalink / raw)
To: Dmitry Isaykin
Cc: xen-devel, Wei Liu, Anthony PERARD, Juergen Gross, Andrew Cooper,
George Dunlap, Jan Beulich, Julien Grall, Stefano Stabellini,
Alexandru Isaila, Petre Pircalabu, Roger Pau Monné,
Jun Nakajima, Kevin Tian, Anton Belousov
[-- Attachment #1: Type: text/plain, Size: 308 bytes --]
On Wed, Mar 15, 2023 at 2:55 PM Dmitry Isaykin <isaikin-dmitry@yandex.ru>
wrote:
>
> Adds monitor support for I/O instructions.
>
> Signed-off-by: Dmitry Isaykin <isaikin-dmitry@yandex.ru>
> Signed-off-by: Anton Belousov <abelousov@ptsecurity.com>
Acked-by: Tamas K Lengyel <tamas@tklengyel.com>
[-- Attachment #2: Type: text/html, Size: 594 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [XEN PATCH v2] x86/monitor: Add new monitor event to catch I/O instructions
2023-03-15 18:54 [XEN PATCH v2] x86/monitor: Add new monitor event to catch I/O instructions Dmitry Isaykin
2023-03-16 12:32 ` Tamas K Lengyel
@ 2023-03-17 11:13 ` Andrew Cooper
1 sibling, 0 replies; 3+ messages in thread
From: Andrew Cooper @ 2023-03-17 11:13 UTC (permalink / raw)
To: Dmitry Isaykin, xen-devel
Cc: Wei Liu, Anthony PERARD, Juergen Gross, George Dunlap,
Jan Beulich, Julien Grall, Stefano Stabellini, Tamas K Lengyel,
Alexandru Isaila, Petre Pircalabu, Roger Pau Monné,
Jun Nakajima, Kevin Tian, Anton Belousov
On 15/03/2023 6:54 pm, Dmitry Isaykin wrote:
> diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
> index a43bcf2e92..49225f48a7 100644
> --- a/xen/arch/x86/hvm/svm/svm.c
> +++ b/xen/arch/x86/hvm/svm/svm.c
> @@ -2939,17 +2939,26 @@ void svm_vmexit_handler(void)
> break;
>
> case VMEXIT_IOIO:
> - if ( (vmcb->exitinfo1 & (1u<<2)) == 0 )
> + {
> + uint16_t port = (vmcb->exitinfo1 >> 16) & 0xFFFF;
> + int bytes = ((vmcb->exitinfo1 >> 4) & 0x07);
> + int dir = (vmcb->exitinfo1 & 1) ? IOREQ_READ : IOREQ_WRITE;
> + bool string_ins = (vmcb->exitinfo1 & (1u<<2));
> + int rc = hvm_monitor_io(port, bytes, dir, string_ins);
> + if ( rc < 0 )
> + goto unexpected_exit_type;
> + if ( !rc )
> {
> - uint16_t port = (vmcb->exitinfo1 >> 16) & 0xFFFF;
> - int bytes = ((vmcb->exitinfo1 >> 4) & 0x07);
> - int dir = (vmcb->exitinfo1 & 1) ? IOREQ_READ : IOREQ_WRITE;
> - if ( handle_pio(port, bytes, dir) )
> - __update_guest_eip(regs, vmcb->exitinfo2 - vmcb->rip);
> + if ( !string_ins )
> + {
> + if ( handle_pio(port, bytes, dir) )
> + __update_guest_eip(regs, vmcb->exitinfo2 - vmcb->rip);
> + }
> + else if ( !hvm_emulate_one_insn(x86_insn_is_portio, "port I/O") )
> + hvm_inject_hw_exception(TRAP_gp_fault, 0);
> }
> - else if ( !hvm_emulate_one_insn(x86_insn_is_portio, "port I/O") )
> - hvm_inject_hw_exception(TRAP_gp_fault, 0);
> break;
> + }
There are a few style issues, but it's also a mess because of the manual
exitinfo decoding, so I went ahead and did
https://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=df9369154aa010b2322e3f3e0727a242784cfd4f
to clean it up. The rebased version of this hunk is now:
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index bfe03316def6..17ac99f6cd56 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -2939,6 +2939,15 @@ void svm_vmexit_handler(void)
break;
case VMEXIT_IOIO:
+ rc = hvm_monitor_io(vmcb->ei.io.port,
+ vmcb->ei.io.bytes,
+ vmcb->ei.io.in ? IOREQ_READ : IOREQ_WRITE,
+ vmcb->ei.io.str);
+ if ( rc < 0 )
+ goto unexpected_exit_type;
+ if ( rc )
+ break;
+
if ( !vmcb->ei.io.str )
{
if ( handle_pio(vmcb->ei.io.port,
which I hope you'll agree is much more simple to follow.
I'm also trying to sort out a similar cleanup on the Intel side, but
haven't managed to post that yet.
~Andrew
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-03-17 11:14 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-15 18:54 [XEN PATCH v2] x86/monitor: Add new monitor event to catch I/O instructions Dmitry Isaykin
2023-03-16 12:32 ` Tamas K Lengyel
2023-03-17 11:13 ` Andrew Cooper
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.