1 /*
2 * Helpers to emulate co-processor and system registers
3 */
4 #ifndef __ASM_ARM_VREG__
5 #define __ASM_ARM_VREG__
6
7 typedef bool (*vreg_reg64_fn_t)(struct cpu_user_regs *regs, uint64_t *r,
8 bool read);
9 typedef bool (*vreg_reg_fn_t)(struct cpu_user_regs *regs, register_t *r,
10 bool read);
11
vreg_emulate_cp32(struct cpu_user_regs * regs,union hsr hsr,vreg_reg_fn_t fn)12 static inline bool vreg_emulate_cp32(struct cpu_user_regs *regs, union hsr hsr,
13 vreg_reg_fn_t fn)
14 {
15 struct hsr_cp32 cp32 = hsr.cp32;
16 /*
17 * Initialize to zero to avoid leaking data if there is an
18 * implementation error in the emulation (such as not correctly
19 * setting r).
20 */
21 register_t r = 0;
22 bool ret;
23
24 if ( !cp32.read )
25 r = get_user_reg(regs, cp32.reg);
26
27 ret = fn(regs, &r, cp32.read);
28
29 if ( ret && cp32.read )
30 set_user_reg(regs, cp32.reg, r);
31
32 return ret;
33 }
34
vreg_emulate_cp64(struct cpu_user_regs * regs,union hsr hsr,vreg_reg64_fn_t fn)35 static inline bool vreg_emulate_cp64(struct cpu_user_regs *regs, union hsr hsr,
36 vreg_reg64_fn_t fn)
37 {
38 struct hsr_cp64 cp64 = hsr.cp64;
39 /*
40 * Initialize to zero to avoid leaking data if there is an
41 * implementation error in the emulation (such as not correctly
42 * setting x).
43 */
44 uint64_t x = 0;
45 bool ret;
46
47 if ( !cp64.read )
48 {
49 uint32_t r1 = get_user_reg(regs, cp64.reg1);
50 uint32_t r2 = get_user_reg(regs, cp64.reg2);
51
52 x = (uint64_t)r1 | ((uint64_t)r2 << 32);
53 }
54
55 ret = fn(regs, &x, cp64.read);
56
57 if ( ret && cp64.read )
58 {
59 set_user_reg(regs, cp64.reg1, x & 0xffffffffU);
60 set_user_reg(regs, cp64.reg2, x >> 32);
61 }
62
63 return ret;
64 }
65
66 #ifdef CONFIG_ARM_64
vreg_emulate_sysreg(struct cpu_user_regs * regs,union hsr hsr,vreg_reg_fn_t fn)67 static inline bool vreg_emulate_sysreg(struct cpu_user_regs *regs, union hsr hsr,
68 vreg_reg_fn_t fn)
69 {
70 struct hsr_sysreg sysreg = hsr.sysreg;
71 register_t r = 0;
72 bool ret;
73
74 if ( !sysreg.read )
75 r = get_user_reg(regs, sysreg.reg);
76
77 ret = fn(regs, &r, sysreg.read);
78
79 if ( ret && sysreg.read )
80 set_user_reg(regs, sysreg.reg, r);
81
82 return ret;
83 }
84 #endif
85
86 #define VREG_REG_MASK(size) ((~0UL) >> (BITS_PER_LONG - ((1 << (size)) * 8)))
87
88 /*
89 * The check on the size supported by the register has to be done by
90 * the caller of vreg_regN_*.
91 *
92 * Note that the alignment fault will always be taken in the guest
93 * (see B3.12.7 DDI0406.b).
94 */
95
96 /* N-bit register helpers */
97 #define VREG_REG_HELPERS(sz, offmask) \
98 static inline register_t vreg_reg##sz##_extract(uint##sz##_t reg, \
99 const mmio_info_t *info)\
100 { \
101 unsigned int offset = info->gpa & (offmask); \
102 \
103 reg >>= 8 * offset; \
104 reg &= VREG_REG_MASK(info->dabt.size); \
105 \
106 return reg; \
107 } \
108 \
109 static inline void vreg_reg##sz##_update(uint##sz##_t *reg, \
110 register_t val, \
111 const mmio_info_t *info) \
112 { \
113 unsigned int offset = info->gpa & (offmask); \
114 uint##sz##_t mask = VREG_REG_MASK(info->dabt.size); \
115 unsigned int shift = offset * 8; \
116 \
117 *reg &= ~(mask << shift); \
118 *reg |= ((uint##sz##_t)val & mask) << shift; \
119 } \
120 \
121 static inline void vreg_reg##sz##_setbits(uint##sz##_t *reg, \
122 register_t bits, \
123 const mmio_info_t *info) \
124 { \
125 unsigned int offset = info->gpa & (offmask); \
126 uint##sz##_t mask = VREG_REG_MASK(info->dabt.size); \
127 unsigned int shift = offset * 8; \
128 \
129 *reg |= ((uint##sz##_t)bits & mask) << shift; \
130 } \
131 \
132 static inline void vreg_reg##sz##_clearbits(uint##sz##_t *reg, \
133 register_t bits, \
134 const mmio_info_t *info) \
135 { \
136 unsigned int offset = info->gpa & (offmask); \
137 uint##sz##_t mask = VREG_REG_MASK(info->dabt.size); \
138 unsigned int shift = offset * 8; \
139 \
140 *reg &= ~(((uint##sz##_t)bits & mask) << shift); \
141 }
142
143 VREG_REG_HELPERS(64, 0x7)
144 VREG_REG_HELPERS(32, 0x3)
145
146 #undef VREG_REG_HELPERS
147
148 #endif /* __ASM_ARM_VREG__ */
149