[PATCH 14/15] KVM: ARM: Handle I/O aborts

Will Deacon will.deacon at arm.com
Thu Sep 27 11:11:08 EDT 2012


On Sat, Sep 15, 2012 at 04:35:59PM +0100, Christoffer Dall wrote:
> When the guest accesses I/O memory this will create data abort
> exceptions and they are handled by decoding the HSR information
> (physical address, read/write, length, register) and forwarding reads
> and writes to QEMU which performs the device emulation.
> 
> Certain classes of load/store operations do not support the syndrome
> information provided in the HSR and we therefore must be able to fetch
> the offending instruction from guest memory and decode it manually.
> 
> We only support instruction decoding for valid reasonable MMIO operations
> where trapping them do not provide sufficient information in the HSR (no
> 16-bit Thumb instructions provide register writeback that we care about).
> 
> The following instruciton types are NOT supported for MMIO operations
> despite the HSR not containing decode info:
>  - any Load/Store multiple
>  - any load/store exclusive
>  - any load/store dual
>  - anything with the PC as the dest register

[...]

> +
> +/******************************************************************************
> + * Load-Store instruction emulation
> + *****************************************************************************/
> +
> +/*
> + * Must be ordered with LOADS first and WRITES afterwards
> + * for easy distinction when doing MMIO.
> + */
> +#define NUM_LD_INSTR  9
> +enum INSTR_LS_INDEXES {
> +       INSTR_LS_LDRBT, INSTR_LS_LDRT, INSTR_LS_LDR, INSTR_LS_LDRB,
> +       INSTR_LS_LDRD, INSTR_LS_LDREX, INSTR_LS_LDRH, INSTR_LS_LDRSB,
> +       INSTR_LS_LDRSH,
> +       INSTR_LS_STRBT, INSTR_LS_STRT, INSTR_LS_STR, INSTR_LS_STRB,
> +       INSTR_LS_STRD, INSTR_LS_STREX, INSTR_LS_STRH,
> +       NUM_LS_INSTR
> +};
> +
> +static u32 ls_instr[NUM_LS_INSTR][2] = {
> +       {0x04700000, 0x0d700000}, /* LDRBT */
> +       {0x04300000, 0x0d700000}, /* LDRT  */
> +       {0x04100000, 0x0c500000}, /* LDR   */
> +       {0x04500000, 0x0c500000}, /* LDRB  */
> +       {0x000000d0, 0x0e1000f0}, /* LDRD  */
> +       {0x01900090, 0x0ff000f0}, /* LDREX */
> +       {0x001000b0, 0x0e1000f0}, /* LDRH  */
> +       {0x001000d0, 0x0e1000f0}, /* LDRSB */
> +       {0x001000f0, 0x0e1000f0}, /* LDRSH */
> +       {0x04600000, 0x0d700000}, /* STRBT */
> +       {0x04200000, 0x0d700000}, /* STRT  */
> +       {0x04000000, 0x0c500000}, /* STR   */
> +       {0x04400000, 0x0c500000}, /* STRB  */
> +       {0x000000f0, 0x0e1000f0}, /* STRD  */
> +       {0x01800090, 0x0ff000f0}, /* STREX */
> +       {0x000000b0, 0x0e1000f0}  /* STRH  */
> +};
> +
> +static inline int get_arm_ls_instr_index(u32 instr)
> +{
> +       return kvm_instr_index(instr, ls_instr, NUM_LS_INSTR);
> +}
> +
> +/*
> + * Load-Store instruction decoding
> + */
> +#define INSTR_LS_TYPE_BIT              26
> +#define INSTR_LS_RD_MASK               0x0000f000
> +#define INSTR_LS_RD_SHIFT              12
> +#define INSTR_LS_RN_MASK               0x000f0000
> +#define INSTR_LS_RN_SHIFT              16
> +#define INSTR_LS_RM_MASK               0x0000000f
> +#define INSTR_LS_OFFSET12_MASK         0x00000fff

I'm afraid you're not going to thank me much for this, but it's high time we
unified the various instruction decoding functions we have under arch/arm/
and this seems like a good opportunity for that. For example, look at the
following snippets (there is much more in the files I list) in addition to
what you have:


asm/ptrace.h
-------------
#define PSR_T_BIT	0x00000020
#define PSR_F_BIT	0x00000040
#define PSR_I_BIT	0x00000080
#define PSR_A_BIT	0x00000100
#define PSR_E_BIT	0x00000200
#define PSR_J_BIT	0x01000000
#define PSR_Q_BIT	0x08000000
#define PSR_V_BIT	0x10000000
#define PSR_C_BIT	0x20000000
#define PSR_Z_BIT	0x40000000
#define PSR_N_BIT	0x80000000

mm/alignment.c
--------------
#define LDST_I_BIT(i)	(i & (1 << 26))		/* Immediate constant	*/
#define LDST_P_BIT(i)	(i & (1 << 24))		/* Preindex		*/
#define LDST_U_BIT(i)	(i & (1 << 23))		/* Add offset		*/
#define LDST_W_BIT(i)	(i & (1 << 21))		/* Writeback		*/
#define LDST_L_BIT(i)	(i & (1 << 20))		/* Load			*/

kernel/kprobes*.c
-----------------
static void __kprobes
emulate_ldr(struct kprobe *p, struct pt_regs *regs)
{
	kprobe_opcode_t insn = p->opcode;
	unsigned long pc = (unsigned long)p->addr + 8;
	int rt = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

kernel/opcodes.c
----------------
static const unsigned short cc_map[16] = {
	0xF0F0,			/* EQ == Z set            */
	0x0F0F,			/* NE                     */
	0xCCCC,			/* CS == C set            */
	0x3333,			/* CC                     */
	0xFF00,			/* MI == N set            */
	0x00FF,			/* PL                     */
	0xAAAA,			/* VS == V set            */
	0x5555,			/* VC                     */
	0x0C0C,			/* HI == C set && Z clear */
	0xF3F3,			/* LS == C clear || Z set */
	0xAA55,			/* GE == (N==V)           */
	0x55AA,			/* LT == (N!=V)           */
	0x0A05,			/* GT == (!Z && (N==V))   */
	0xF5FA,			/* LE == (Z || (N!=V))    */
	0xFFFF,			/* AL always              */
	0			/* NV                     */
};

kernel/swp_emulate.c
--------------------
#define EXTRACT_REG_NUM(instruction, offset) \
	(((instruction) & (0xf << (offset))) >> (offset))
#define RN_OFFSET  16
#define RT_OFFSET  12
#define RT2_OFFSET  0


There are also bits and pieces with the patching frameworks and module
relocations that could benefit from some code sharing. Now, I think Dave had
some ideas about moving a load of this code into a common disassembler under
arch/arm/ so it would be great to tie that in here and implement that for
load/store instructions. Then other users can augment the number of
supported instruction classes as and when it is required.

Will



More information about the linux-arm-kernel mailing list