mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/arm: arm64: Add emulation of Debug Data Transfer Registers
Currently, if user enables HVC_DCC config option in Linux, it invokes access to debug data transfer registers (i.e. DBGDTRTX_EL0 on arm64, DBGDTRTXINT on arm32). As these registers are not emulated, Xen injects an undefined exception to the guest and Linux crashes. To prevent this crash, introduce a partial emulation of DBGDTR[TR]X_EL0 (these registers share the same encoding) as RAZ/WI and MDCCSR_EL0 as TXfull. Refer ARM DDI 0487J.a ID042523, D19.3.8, DBGDTRTX_EL0 "If TXfull is set to 1, set DTRRX and DTRTX to UNKNOWN". Thus, any OS is expected to read MDCCSR_EL0 and check for TXfull before using DBGDTRTX_EL0. Linux does it via hvc_dcc_init() ---> hvc_dcc_check(), and returns -ENODEV in case TXfull bit is still set after writing a test character. This way we prevent the guest from making use of HVC DCC as a console. Signed-off-by: Michal Orzel <michal.orzel@amd.com> Signed-off-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com> Acked-by: Julien Grall <jgrall@amazon.com>
This commit is contained in:
1 parent
bd06cf74fc
commit
43c416d0d8
3 files changed
+56
-21
No files matched your search
@@ -111,6 +111,11 @@ that would normally trigger a fault injection.
|
|||||||
|
|
||||||
Only the following system registers are security supported:
|
Only the following system registers are security supported:
|
||||||
|
|
||||||
|
MDCCSR_EL0
|
||||||
|
DBGDTR_EL0
|
||||||
|
DBGDTRTX_EL0
|
||||||
|
DBGDTRRX_EL0
|
||||||
|
|
||||||
### ARM Scalable Vector Extension (SVE/SVE2)
|
### ARM Scalable Vector Extension (SVE/SVE2)
|
||||||
|
|
||||||
Arm64 domains can use Scalable Vector Extension (SVE/SVE2).
|
Arm64 domains can use Scalable Vector Extension (SVE/SVE2).
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ TVM_REG(CONTEXTIDR_EL1)
|
|||||||
void do_sysreg(struct cpu_user_regs *regs,
|
void do_sysreg(struct cpu_user_regs *regs,
|
||||||
const union hsr hsr)
|
const union hsr hsr)
|
||||||
{
|
{
|
||||||
|
const struct hsr_sysreg sysreg = hsr.sysreg;
|
||||||
int regidx = hsr.sysreg.reg;
|
int regidx = hsr.sysreg.reg;
|
||||||
struct vcpu *v = current;
|
struct vcpu *v = current;
|
||||||
|
|
||||||
@@ -159,9 +160,6 @@ void do_sysreg(struct cpu_user_regs *regs,
|
|||||||
*
|
*
|
||||||
* Unhandled:
|
* Unhandled:
|
||||||
* MDCCINT_EL1
|
* MDCCINT_EL1
|
||||||
* DBGDTR_EL0
|
|
||||||
* DBGDTRRX_EL0
|
|
||||||
* DBGDTRTX_EL0
|
|
||||||
* OSDTRRX_EL1
|
* OSDTRRX_EL1
|
||||||
* OSDTRTX_EL1
|
* OSDTRTX_EL1
|
||||||
* OSECCR_EL1
|
* OSECCR_EL1
|
||||||
@@ -171,12 +169,42 @@ void do_sysreg(struct cpu_user_regs *regs,
|
|||||||
*/
|
*/
|
||||||
case HSR_SYSREG_MDSCR_EL1:
|
case HSR_SYSREG_MDSCR_EL1:
|
||||||
return handle_raz_wi(regs, regidx, hsr.sysreg.read, hsr, 1);
|
return handle_raz_wi(regs, regidx, hsr.sysreg.read, hsr, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Xen doesn't expose a real (or emulated) Debug Communications Channel
|
||||||
|
* (DCC) to a domain. Yet the Arm ARM implies this is not an optional
|
||||||
|
* feature. So some domains may start to probe it. For instance, the
|
||||||
|
* HVC_DCC driver in Linux (since f377775dc083 and at least up to v6.7),
|
||||||
|
* will try to write some characters and check if the transmit buffer
|
||||||
|
* has emptied.
|
||||||
|
*/
|
||||||
case HSR_SYSREG_MDCCSR_EL0:
|
case HSR_SYSREG_MDCCSR_EL0:
|
||||||
/*
|
/*
|
||||||
|
* By setting TX status bit (only if partial emulation is enabled) to
|
||||||
|
* indicate the transmit buffer is full, we would hint the OS that the
|
||||||
|
* DCC is probably not working.
|
||||||
|
*
|
||||||
|
* Bit 29: TX full
|
||||||
|
*
|
||||||
* Accessible at EL0 only if MDSCR_EL1.TDCC is set to 0. We emulate that
|
* Accessible at EL0 only if MDSCR_EL1.TDCC is set to 0. We emulate that
|
||||||
* register as RAZ/WI above. So RO at both EL0 and EL1.
|
* register as RAZ/WI above. So RO at both EL0 and EL1.
|
||||||
*/
|
*/
|
||||||
return handle_ro_raz(regs, regidx, hsr.sysreg.read, hsr, 0);
|
return handle_ro_read_val(regs, regidx, hsr.sysreg.read, hsr, 0,
|
||||||
|
partial_emulation ? (1U << 29) : 0);
|
||||||
|
|
||||||
|
case HSR_SYSREG_DBGDTR_EL0:
|
||||||
|
/* DBGDTR[TR]X_EL0 share the same encoding */
|
||||||
|
case HSR_SYSREG_DBGDTRTX_EL0:
|
||||||
|
/*
|
||||||
|
* Emulate as RAZ/WI (only if partial emulation is enabled) to prevent
|
||||||
|
* injecting undefined exception.
|
||||||
|
* Accessible at EL0 only if MDSCR_EL1.TDCC is set to 0. We emulate that
|
||||||
|
* register as RAZ/WI.
|
||||||
|
*/
|
||||||
|
if ( !partial_emulation )
|
||||||
|
goto fail;
|
||||||
|
return handle_raz_wi(regs, regidx, hsr.sysreg.read, hsr, 0);
|
||||||
|
|
||||||
HSR_SYSREG_DBG_CASES(DBGBVR):
|
HSR_SYSREG_DBG_CASES(DBGBVR):
|
||||||
HSR_SYSREG_DBG_CASES(DBGBCR):
|
HSR_SYSREG_DBG_CASES(DBGBCR):
|
||||||
HSR_SYSREG_DBG_CASES(DBGWVR):
|
HSR_SYSREG_DBG_CASES(DBGWVR):
|
||||||
@@ -394,26 +422,25 @@ void do_sysreg(struct cpu_user_regs *regs,
|
|||||||
* And all other unknown registers.
|
* And all other unknown registers.
|
||||||
*/
|
*/
|
||||||
default:
|
default:
|
||||||
{
|
goto fail;
|
||||||
const struct hsr_sysreg sysreg = hsr.sysreg;
|
|
||||||
|
|
||||||
gdprintk(XENLOG_ERR,
|
|
||||||
"%s %d, %d, c%d, c%d, %d %s x%d @ 0x%"PRIregister"\n",
|
|
||||||
sysreg.read ? "mrs" : "msr",
|
|
||||||
sysreg.op0, sysreg.op1,
|
|
||||||
sysreg.crn, sysreg.crm,
|
|
||||||
sysreg.op2,
|
|
||||||
sysreg.read ? "=>" : "<=",
|
|
||||||
sysreg.reg, regs->pc);
|
|
||||||
gdprintk(XENLOG_ERR,
|
|
||||||
"unhandled 64-bit sysreg access %#"PRIregister"\n",
|
|
||||||
hsr.bits & HSR_SYSREG_REGS_MASK);
|
|
||||||
inject_undef_exception(regs, hsr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
regs->pc += 4;
|
regs->pc += 4;
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
gdprintk(XENLOG_ERR,
|
||||||
|
"%s %d, %d, c%d, c%d, %d %s x%d @ 0x%"PRIregister"\n",
|
||||||
|
sysreg.read ? "mrs" : "msr",
|
||||||
|
sysreg.op0, sysreg.op1,
|
||||||
|
sysreg.crn, sysreg.crm,
|
||||||
|
sysreg.op2,
|
||||||
|
sysreg.read ? "=>" : "<=",
|
||||||
|
sysreg.reg, regs->pc);
|
||||||
|
gdprintk(XENLOG_ERR,
|
||||||
|
"unhandled 64-bit sysreg access %#"PRIregister"\n",
|
||||||
|
hsr.bits & HSR_SYSREG_REGS_MASK);
|
||||||
|
inject_undef_exception(regs, hsr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -47,6 +47,9 @@
|
|||||||
#define HSR_SYSREG_OSDLR_EL1 HSR_SYSREG(2,0,c1,c3,4)
|
#define HSR_SYSREG_OSDLR_EL1 HSR_SYSREG(2,0,c1,c3,4)
|
||||||
#define HSR_SYSREG_DBGPRCR_EL1 HSR_SYSREG(2,0,c1,c4,4)
|
#define HSR_SYSREG_DBGPRCR_EL1 HSR_SYSREG(2,0,c1,c4,4)
|
||||||
#define HSR_SYSREG_MDCCSR_EL0 HSR_SYSREG(2,3,c0,c1,0)
|
#define HSR_SYSREG_MDCCSR_EL0 HSR_SYSREG(2,3,c0,c1,0)
|
||||||
|
#define HSR_SYSREG_DBGDTR_EL0 HSR_SYSREG(2,3,c0,c4,0)
|
||||||
|
#define HSR_SYSREG_DBGDTRTX_EL0 HSR_SYSREG(2,3,c0,c5,0)
|
||||||
|
#define HSR_SYSREG_DBGDTRRX_EL0 HSR_SYSREG(2,3,c0,c5,0)
|
||||||
|
|
||||||
#define HSR_SYSREG_DBGBVRn_EL1(n) HSR_SYSREG(2,0,c0,c##n,4)
|
#define HSR_SYSREG_DBGBVRn_EL1(n) HSR_SYSREG(2,0,c0,c##n,4)
|
||||||
#define HSR_SYSREG_DBGBCRn_EL1(n) HSR_SYSREG(2,0,c0,c##n,5)
|
#define HSR_SYSREG_DBGBCRn_EL1(n) HSR_SYSREG(2,0,c0,c##n,5)
|
||||||
|
|||||||