1 /* SPDX-License-Identifier: GPL-2.0-only */
5 #include <arch/exception.h>
6 #include <commonlib/helpers.h>
8 /* these functions are defined in src/arch/riscv/fp_asm.S */
9 #if defined(__riscv_flen)
10 #if __riscv_flen >= 32
11 extern void read_f32(int regnum
, uint32_t *v
);
12 extern void write_f32(int regnum
, uint32_t *v
);
13 #endif // __riscv_flen >= 32
14 #if __riscv_flen >= 64
15 extern void read_f64(int regnum
, uint64_t *v
);
16 extern void write_f64(int regnum
, uint64_t *v
);
17 #endif // __riscv_flen >= 64
18 #endif // defined(__riscv_flen)
20 /* This union makes it easy to read multibyte types by byte operations. */
29 /* This struct hold info of load/store instruction */
30 struct memory_instruction_info
{
31 /* opcode/mask used to identify instruction,
32 * (instruction_val) & mask == opcode */
35 /* reg_shift/reg_mask/reg_addition used to get register number
36 * ((instruction_val >> reg_shift) & reg_mask) + reg_addition */
37 unsigned int reg_shift
;
38 unsigned int reg_mask
;
39 unsigned int reg_addition
;
40 unsigned int is_fp
: 1; /* mark as a float operation */
41 unsigned int is_load
: 1; /* mark as a load operation */
42 unsigned int width
: 8; /* Record the memory width of the operation */
43 unsigned int sign_extend
: 1; /* mark need to be sign extended */
46 static struct memory_instruction_info insn_info
[] = {
47 #if __riscv_xlen == 128
48 { 0x00002000, 0x0000e003, 2, 7, 8, 0, 1, 16, 1}, // C.LQ
50 { 0x00002000, 0x0000e003, 2, 7, 8, 1, 1, 8, 0}, // C.FLD
52 { 0x00004000, 0x0000e003, 2, 7, 8, 0, 1, 4, 1}, // C.LW
53 #if __riscv_xlen == 32
54 { 0x00006000, 0x0000e003, 2, 7, 8, 1, 1, 4, 0}, // C.FLW
56 { 0x00006000, 0x0000e003, 2, 7, 8, 0, 1, 8, 1}, // C.LD
59 #if __riscv_xlen == 128
60 { 0x0000a000, 0x0000e003, 2, 7, 8, 0, 0, 16, 0}, // C.SQ
62 { 0x0000a000, 0x0000e003, 2, 7, 8, 1, 0, 8, 0}, // C.FSD
64 { 0x0000c000, 0x0000e003, 2, 7, 8, 0, 0, 4, 0}, // C.SW
65 #if __riscv_xlen == 32
66 { 0x0000e000, 0x0000e003, 2, 7, 8, 1, 0, 4, 0}, // C.FSW
68 { 0x0000e000, 0x0000e003, 2, 7, 8, 0, 0, 8, 0}, // C.SD
71 #if __riscv_xlen == 128
72 { 0x00002002, 0x0000e003, 7, 15, 0, 0, 1, 16, 1}, // C.LQSP
74 { 0x00002002, 0x0000e003, 7, 15, 0, 1, 1, 8, 0}, // C.FLDSP
76 { 0x00004002, 0x0000e003, 7, 15, 0, 0, 1, 4, 1}, // C.LWSP
77 #if __riscv_xlen == 32
78 { 0x00006002, 0x0000e003, 7, 15, 0, 1, 1, 4, 0}, // C.FLWSP
80 { 0x00006002, 0x0000e003, 7, 15, 0, 0, 1, 8, 1}, // C.LDSP
83 #if __riscv_xlen == 128
84 { 0x0000a002, 0x0000e003, 2, 15, 0, 0, 0, 16, 0}, // C.SQSP
86 { 0x0000a002, 0x0000e003, 2, 15, 0, 1, 0, 8, 0}, // C.FSDSP
88 { 0x0000c002, 0x0000e003, 2, 15, 0, 0, 0, 4, 0}, // C.SWSP
89 #if __riscv_xlen == 32
90 { 0x0000e002, 0x0000e003, 2, 15, 0, 1, 0, 4, 0}, // C.FSWSP
92 { 0x0000e002, 0x0000e003, 2, 15, 0, 0, 0, 8, 0}, // C.SDSP
95 { 0x00000003, 0x0000707f, 7, 15, 0, 0, 1, 1, 1}, // LB
96 { 0x00001003, 0x0000707f, 7, 15, 0, 0, 1, 2, 1}, // LH
97 { 0x00002003, 0x0000707f, 7, 15, 0, 0, 1, 4, 1}, // LW
99 { 0x00003003, 0x0000707f, 7, 15, 0, 0, 1, 8, 1}, // LD
101 { 0x00004003, 0x0000707f, 7, 15, 0, 0, 1, 1, 0}, // LBU
102 { 0x00005003, 0x0000707f, 7, 15, 0, 0, 1, 2, 0}, // LHU
103 { 0x00006003, 0x0000707f, 7, 15, 0, 0, 1, 4, 0}, // LWU
105 { 0x00000023, 0x0000707f, 20, 15, 0, 0, 0, 1, 0}, // SB
106 { 0x00001023, 0x0000707f, 20, 15, 0, 0, 0, 2, 0}, // SH
107 { 0x00002023, 0x0000707f, 20, 15, 0, 0, 0, 4, 0}, // SW
108 #if __riscv_xlen > 32
109 { 0x00003023, 0x0000707f, 20, 15, 0, 0, 0, 8, 0}, // SD
112 #if defined(__riscv_flen)
113 #if __riscv_flen >= 32
114 { 0x00002007, 0x0000707f, 7, 15, 0, 1, 1, 4, 0}, // FLW
115 { 0x00003007, 0x0000707f, 7, 15, 0, 1, 1, 8, 0}, // FLD
116 #endif // __riscv_flen >= 32
118 #if __riscv_flen >= 64
119 { 0x00002027, 0x0000707f, 20, 15, 0, 1, 0, 4, 0}, // FSW
120 { 0x00003027, 0x0000707f, 20, 15, 0, 1, 0, 8, 0}, // FSD
121 #endif // __riscv_flen >= 64
122 #endif // defined(__riscv_flen)
125 static struct memory_instruction_info
*match_instruction(uintptr_t insn
)
128 for (i
= 0; i
< ARRAY_SIZE(insn_info
); i
++)
129 if ((insn_info
[i
].mask
& insn
) == insn_info
[i
].opcode
)
130 return &(insn_info
[i
]);
134 static int fetch_16bit_instruction(uintptr_t vaddr
, uintptr_t *insn
, int *size
)
136 uint16_t ins
= mprv_read_mxr_u16((uint16_t *)vaddr
);
137 if (EXTRACT_FIELD(ins
, 0x3) != 3) {
145 static int fetch_32bit_instruction(uintptr_t vaddr
, uintptr_t *insn
, int *size
)
147 uint32_t l
= (uint32_t)mprv_read_mxr_u16((uint16_t *)vaddr
+ 0);
148 uint32_t h
= (uint32_t)mprv_read_mxr_u16((uint16_t *)vaddr
+ 1);
149 uint32_t ins
= (h
<< 16) | l
;
150 if ((EXTRACT_FIELD(ins
, 0x3) == 3) &&
151 (EXTRACT_FIELD(ins
, 0x1c) != 0x7)) {
159 void handle_misaligned(trapframe
*tf
)
162 union endian_buf buff
;
165 /* try to fetch 16/32 bits instruction */
166 if (fetch_16bit_instruction(tf
->epc
, &insn
, &insn_size
) < 0) {
167 if (fetch_32bit_instruction(tf
->epc
, &insn
, &insn_size
) < 0) {
173 /* matching instruction */
174 struct memory_instruction_info
*match
= match_instruction(insn
);
182 regnum
= ((insn
>> match
->reg_shift
) & match
->reg_mask
);
183 regnum
= regnum
+ match
->reg_addition
;
185 if (match
->is_load
) {
188 /* reading from memory by bytes prevents misaligned
190 for (int i
= 0; i
< match
->width
; i
++) {
191 uint8_t *addr
= (uint8_t *)(tf
->badvaddr
+ i
);
192 buff
.b
[i
] = mprv_read_u8(addr
);
195 /* sign extend for signed integer loading */
196 if (match
->sign_extend
)
197 if (buff
.v
>> (8 * match
->width
- 1))
198 buff
.v
|= -1 << (8 * match
->width
);
200 /* write to register */
203 #if defined(__riscv_flen)
204 #if __riscv_flen >= 32
205 /* single-precision floating-point */
206 if (match
->width
== 4) {
207 write_f32(regnum
, buff
.w
);
210 #endif // __riscv_flen >= 32
211 #if __riscv_flen >= 64
212 /* double-precision floating-point */
213 if (match
->width
== 8) {
214 write_f64(regnum
, buff
.d
);
217 #endif // __riscv_flen >= 64
218 #endif // defined(__riscv_flen)
222 tf
->gpr
[regnum
] = buff
.v
;
225 /* store operation */
227 /* reading from register */
230 #if defined(__riscv_flen)
231 #if __riscv_flen >= 32
232 if (match
->width
== 4) {
233 read_f32(regnum
, buff
.w
);
236 #endif // __riscv_flen >= 32
237 #if __riscv_flen >= 64
238 if (match
->width
== 8) {
239 read_f64(regnum
, buff
.d
);
242 #endif // __riscv_flen >= 64
243 #endif // defined(__riscv_flen)
247 buff
.v
= tf
->gpr
[regnum
];
250 /* writing to memory by bytes prevents misaligned
252 for (int i
= 0; i
< match
->width
; i
++) {
253 uint8_t *addr
= (uint8_t *)(tf
->badvaddr
+ i
);
254 mprv_write_u8(addr
, buff
.b
[i
]);
258 /* return to where we came from */
259 write_csr(mepc
, read_csr(mepc
) + insn_size
);