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