From 769efc903eed18839641a003aa63ce64de27bb5f Mon Sep 17 00:00:00 2001 From: Michal Orzel Date: Wed, 3 Jan 2024 14:44:22 +0000 Subject: [XEN v5 2/3] xen/arm: arm64: Add emulation of Debug Data Transfer Registers To: xen-devel@xxxxxxxxxxxxxxxxxxxx Cc: sstabellini@xxxxxxxxxx, stefano.stabellini@xxxxxxx, julien@xxxxxxx, Volodymyr_Babchuk@xxxxxxxx, bertrand.marquis@xxxxxxx, michal.orzel@xxxxxxx 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 Signed-off-by: Ayan Kumar Halder Reviewed-by: Michal Orzel --- xen/arch/arm/arm64/vsysreg.c | 69 +++++++++++++++++++--------- xen/arch/arm/include/asm/arm64/hsr.h | 3 ++ 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/xen/arch/arm/arm64/vsysreg.c b/xen/arch/arm/arm64/vsysreg.c index b5d54c569b..813b40425d 100644 --- a/xen/arch/arm/arm64/vsysreg.c +++ b/xen/arch/arm/arm64/vsysreg.c @@ -159,9 +159,6 @@ void do_sysreg(struct cpu_user_regs *regs, * * Unhandled: * MDCCINT_EL1 - * DBGDTR_EL0 - * DBGDTRRX_EL0 - * DBGDTRTX_EL0 * OSDTRRX_EL1 * OSDTRTX_EL1 * OSECCR_EL1 @@ -173,10 +170,40 @@ void do_sysreg(struct cpu_user_regs *regs, return handle_raz_wi(regs, regidx, hsr.sysreg.read, hsr, 1); case HSR_SYSREG_MDCCSR_EL0: /* + * 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. + * + * 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 - * register as RAZ/WI above. So RO at both EL0 and EL1. + * register as RO at EL0 and above. */ - 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: + /* + * As stated before, Xen does not support full emulation of Debug + * Communications Channel (DCC). Thus, if Xen does not support partial + * emulation and access to DBGDTRTX is trapped, it will inject an undef + * exception. Otherwise, Xen emulates this as RAZ/WI as it emulates the + * TX status as full. Thus, guests are not expected to use DBGDTRTX for + * reading/writing. + */ + 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(DBGBCR): HSR_SYSREG_DBG_CASES(DBGWVR): @@ -394,23 +421,21 @@ void do_sysreg(struct cpu_user_regs *regs, * And all other unknown registers. */ default: - { - 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; - } + goto fail; + 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; diff --git a/xen/arch/arm/include/asm/arm64/hsr.h b/xen/arch/arm/include/asm/arm64/hsr.h index e691d41c17..1495ccddea 100644 --- a/xen/arch/arm/include/asm/arm64/hsr.h +++ b/xen/arch/arm/include/asm/arm64/hsr.h @@ -47,6 +47,9 @@ #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_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_DBGBCRn_EL1(n) HSR_SYSREG(2,0,c0,c##n,5) -- 2.25.1