[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [XEN v8 2/2] xen/arm64: io: Support instructions (for which ISS is not valid) on emulated MMIO region using MMIO/ioreq handler
Hi,
On 12/02/2022 23:34, Ayan Kumar Halder wrote:
xen/arch/arm/arm32/traps.c | 7 +++
xen/arch/arm/arm64/traps.c | 47 +++++++++++++++
xen/arch/arm/decode.c | 1 +
xen/arch/arm/include/asm/domain.h | 4 ++
xen/arch/arm/include/asm/mmio.h | 15 ++++-
xen/arch/arm/include/asm/traps.h | 2 +
xen/arch/arm/io.c | 98 ++++++++++++++++++++-----------
xen/arch/arm/ioreq.c | 7 ++-
xen/arch/arm/traps.c | 80 +++++++++++++++++++++----
xen/arch/x86/include/asm/ioreq.h | 3 +
This change technically needs an ack from the x86 maintainers. And...
xen/include/xen/sched.h | 2 +
this one for anyone from THE REST (Stefano and I are part of it). Please
use scripts/add_maintainers.pl to automatically add the relevant
maintainers in CC.
11 files changed, 217 insertions(+), 49 deletions(-)
diff --git a/xen/arch/arm/arm32/traps.c b/xen/arch/arm/arm32/traps.c
index 9c9790a6d1..70c6238196 100644
--- a/xen/arch/arm/arm32/traps.c
+++ b/xen/arch/arm/arm32/traps.c
@@ -18,9 +18,11 @@
#include <xen/lib.h>
#include <xen/kernel.h>
+#include <xen/sched.h>
#include <public/xen.h>
+#include <asm/mmio.h>
#include <asm/processor.h>
#include <asm/traps.h>
@@ -82,6 +84,11 @@ void do_trap_data_abort(struct cpu_user_regs *regs)
do_unexpected_trap("Data Abort", regs);
}
+void post_increment_register(const struct instr_details *instr)
+{
+ domain_crash(current->domain);
Please add a comment explaning why this is resulting to a domain crash.
AFAICT, this is because this should not be reachable (yet) for 32-bit.
+}
+
/*
* Local variables:
* mode: C
diff --git a/xen/arch/arm/arm64/traps.c b/xen/arch/arm/arm64/traps.c
index 9113a15c7a..a6766689b3 100644
--- a/xen/arch/arm/arm64/traps.c
+++ b/xen/arch/arm/arm64/traps.c
@@ -23,6 +23,7 @@
#include <asm/processor.h>
#include <public/xen.h>
+#include <xen/sched.h>
The headers should ordered so <xen/*.h> are first, then <asm/*.h>, then
<public/*.h>. They should then be ordered alphabetically within each of
the category.
So, this new header should be included right after <xen/lib.h>
[...]
diff --git a/xen/arch/arm/include/asm/mmio.h b/xen/arch/arm/include/asm/mmio.h
index 3354d9c635..745130b7fe 100644
--- a/xen/arch/arm/include/asm/mmio.h
+++ b/xen/arch/arm/include/asm/mmio.h
@@ -26,12 +26,22 @@
#define MAX_IO_HANDLER 16
+enum instr_decode_state
+{
+ INSTR_ERROR, /* Error encountered while decoding instr
*/
+ INSTR_VALID, /* ISS is valid, so no need to decode */
+ INSTR_LDR_STR_POSTINDEXING, /* Instruction is decoded successfully.
+ It is ldr/str post indexing */
Coding style: multiple-line comments for Xen should be:
/*
* ...
* ...
*/
In this case, I would simply move the comment on top.
[...]
diff --git a/xen/arch/arm/io.c b/xen/arch/arm/io.c
index a289d393f9..203466b869 100644
--- a/xen/arch/arm/io.c
+++ b/xen/arch/arm/io.c
@@ -95,57 +95,87 @@ static const struct mmio_handler *find_mmio_handler(struct
domain *d,
return handler;
}
+void try_decode_instruction(const struct cpu_user_regs *regs,
+ mmio_info_t *info)
+{
+ int rc;
+
+ /*
+ * Erratum 766422: Thumb store translation fault to Hypervisor may
+ * not have correct HSR Rt value.
+ */
+ if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
+ info->dabt.write )
+ {
+ rc = decode_instruction(regs, info);
+ if ( rc )
+ {
+ gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
+ info->dabt_instr.state = INSTR_ERROR;
+ return;
+ }
+ }
At the moment, the errata would only be handled when the ISS is valid.
Now, you are moving it before we know if it is valid. Can you explain why?
[...]
enum io_state try_handle_mmio(struct cpu_user_regs *regs,
- const union hsr hsr,
- paddr_t gpa)
+ mmio_info_t *info)
{
struct vcpu *v = current;
const struct mmio_handler *handler = NULL;
- const struct hsr_dabt dabt = hsr.dabt;
- mmio_info_t info = {
- .gpa = gpa,
- .dabt = dabt
- };
+ int rc;
- ASSERT(hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
+ ASSERT(info->dabt.ec == HSR_EC_DATA_ABORT_LOWER_EL);
- handler = find_mmio_handler(v->domain, info.gpa);
+ handler = find_mmio_handler(v->domain, info->gpa);
if ( !handler )
{
- int rc;
-
- rc = try_fwd_ioserv(regs, v, &info);
+ rc = try_fwd_ioserv(regs, v, info);
if ( rc == IO_HANDLED )
return handle_ioserv(regs, v);
return rc;
}
- /* All the instructions used on emulated MMIO region should be valid */
- if ( !dabt.valid )
- return IO_ABORT;
-
AFAIU, the assumption is now try_handle_mmio() and try_fwd_ioserv() will
always be called when dabt.valid == 1. I think it would still be good to
check that assumption.
So I would move the check at the beginning of try_handle_mmio() and add
an ASSERT_UNREACHABLE in the if(). Something like:
if ( !dabt.valid )
{
ASSERT_UNREACHABLE();
return IO_ABORT;
}
/*
- * Erratum 766422: Thumb store translation fault to Hypervisor may
- * not have correct HSR Rt value.
+ * At this point, we know that the instruction is either valid or has been
+ * decoded successfully. Thus, Xen should be allowed to execute the
+ * instruction on the emulated MMIO region.
*/
- if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
- dabt.write )
- {
- int rc;
-
- rc = decode_instruction(regs, &info);
- if ( rc )
- {
- gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
- return IO_ABORT;
- }
- }
-
- if ( info.dabt.write )
- return handle_write(handler, v, &info);
+ if ( info->dabt.write )
+ rc = handle_write(handler, v, info);
else
- return handle_read(handler, v, &info);
+ rc = handle_read(handler, v, info);
+
+ return rc;
It looks like there are some left-over of the previous approach. It is
fine to return directly from each branch.
}
void register_mmio_handler(struct domain *d,
diff --git a/xen/arch/arm/ioreq.c b/xen/arch/arm/ioreq.c
index 308650b400..3c0a935ccf 100644
--- a/xen/arch/arm/ioreq.c
+++ b/xen/arch/arm/ioreq.c
@@ -47,6 +47,7 @@ enum io_state try_fwd_ioserv(struct cpu_user_regs *regs,
struct vcpu *v, mmio_info_t *info)
{
struct vcpu_io *vio = &v->io;
+ struct dabt_instr instr = info->dabt_instr;
ioreq_t p = {
.type = IOREQ_TYPE_COPY,
.addr = info->gpa,
@@ -76,10 +77,8 @@ enum io_state try_fwd_ioserv(struct cpu_user_regs *regs,
if ( !s )
return IO_UNHANDLED;
- if ( !info->dabt.valid )
- return IO_ABORT;
-
For this one, I would switch to ASSERT(dabt.valid);
vio->req = p;
+ vio->info.dabt_instr = instr;
rc = ioreq_send(s, &p, 0);
if ( rc != IO_RETRY || v->domain->is_shutting_down )
@@ -95,6 +94,7 @@ enum io_state try_fwd_ioserv(struct cpu_user_regs *regs,
bool arch_ioreq_complete_mmio(void)
{
struct vcpu *v = current;
+ struct instr_details dabt_instr = v->io.info.dabt_instr;
struct cpu_user_regs *regs = guest_cpu_user_regs();
const union hsr hsr = { .bits = regs->hsr };
@@ -106,6 +106,7 @@ bool arch_ioreq_complete_mmio(void)
if ( handle_ioserv(regs, v) == IO_HANDLED )
{
+ post_increment_register(&dabt_instr);
advance_pc(regs, hsr);
return true;
}
diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 9339d12f58..455e51cdbe 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -1893,6 +1893,21 @@ static bool try_map_mmio(gfn_t gfn)
return !map_regions_p2mt(d, gfn, 1, mfn, p2m_mmio_direct_c);
}
+static inline bool check_p2m(bool is_data, paddr_t gpa)
+{
+ /*
+ * First check if the translation fault can be resolved by the P2M
subsystem.
+ * If that's the case nothing else to do.
+ */
+ if ( p2m_resolve_translation_fault(current->domain,gaddr_to_gfn(gpa)) )
Coding style: missing space before and after the comma.
+ return true;
+
+ if ( is_data && try_map_mmio(gaddr_to_gfn(gpa)) )
+ return true;
+
+ return false;
+}
+
static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs,
const union hsr hsr)
{
@@ -1906,6 +1921,7 @@ static void do_trap_stage2_abort_guest(struct
cpu_user_regs *regs,
paddr_t gpa;
uint8_t fsc = xabt.fsc & ~FSC_LL_MASK;
bool is_data = (hsr.ec == HSR_EC_DATA_ABORT_LOWER_EL);
+ mmio_info_t info;
/*
* If this bit has been set, it means that this stage-2 abort is caused
@@ -1959,6 +1975,25 @@ static void do_trap_stage2_abort_guest(struct
cpu_user_regs *regs,
return;
}
case FSC_FLT_TRANS:
+ {
+ info.gpa = gpa;
+ info.dabt = hsr.dabt;
+
+ /* Check that the ISS is invalid and it is not data abort. */
This comment looks a bit pointless. You are writing literally what the
check is doing. But you don't really explain why. I think you want to
move some of the commint with the if here.
However,...
+ if ( !hsr.dabt.valid && !is_data )
... this code can be reached by Instruction Abort and Data Abort. So you
can't use hsr.dabt. Instead, you should use xabt (or check is_data first).
If you use xabt, you will notice that the 'valid' bit is not existent
because the instruction syndrome only exists for data abort.
But then, I don't understand why this is only restricted to instruction
abort. As I wrote in the previous versions and on IRC, there are valid
use cases to trap a data abort with invalid syndrome. Below...
+ {
+
+ /*
+ * Assumption :- Most of the times when we get a translation fault
+ * and the ISS is invalid, the underlying cause is that the page
+ * tables have not been set up correctly.
+ */
+ if ( check_p2m(is_data, gpa) )
+ return;
+ else
+ goto inject_abt;
+ }
+
/*
* Attempt first to emulate the MMIO as the data abort will
* likely happen in an emulated region.
@@ -1967,13 +2002,45 @@ static void do_trap_stage2_abort_guest(struct
cpu_user_regs *regs,
*/
if ( is_data )
{
- enum io_state state = try_handle_mmio(regs, hsr, gpa);
+ enum io_state state;
+
+ try_decode_instruction(regs, &info);
+
+ /*
+ * If Xen could not decode the instruction for any reason, then it
+ * should ask the caller to abort the guest.
+ */
+ if ( info.dabt_instr.state == INSTR_ERROR )
+ goto inject_abt;
... this will inject a data abort to the guest when we can't decode.
This is not what we want. We should check whether this is a P2M
translation fault or we need to map an MMIO region.
In pseudo-code, this would look like:
if ( !is_data || hsr.dabt.valid )
{
if ( check_p2m() )
return;
if ( !is_data )
goto inject_dabt;
decode_instruction();
if ( !dabt.invalid )
goto inject_dabt;
}
try_handle_mmio();
if ( instruction was not decoded )
check_p2m();
Cheers,
--
Julien Grall
|