1 /*
2 * Utility for QEMU MIPS to generate it's simple bootloader
3 *
4 * Instructions used here are carefully selected to keep compatibility with
5 * MIPS Release 6.
6 *
7 * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include "qemu/osdep.h"
13 #include "qemu/bitops.h"
14 #include "cpu.h"
15 #include "hw/mips/bootloader.h"
16
17 typedef enum bl_reg {
18 BL_REG_ZERO = 0,
19 BL_REG_AT = 1,
20 BL_REG_V0 = 2,
21 BL_REG_V1 = 3,
22 BL_REG_A0 = 4,
23 BL_REG_A1 = 5,
24 BL_REG_A2 = 6,
25 BL_REG_A3 = 7,
26 BL_REG_T0 = 8,
27 BL_REG_T1 = 9,
28 BL_REG_T2 = 10,
29 BL_REG_T3 = 11,
30 BL_REG_T4 = 12,
31 BL_REG_T5 = 13,
32 BL_REG_T6 = 14,
33 BL_REG_T7 = 15,
34 BL_REG_S0 = 16,
35 BL_REG_S1 = 17,
36 BL_REG_S2 = 18,
37 BL_REG_S3 = 19,
38 BL_REG_S4 = 20,
39 BL_REG_S5 = 21,
40 BL_REG_S6 = 22,
41 BL_REG_S7 = 23,
42 BL_REG_T8 = 24,
43 BL_REG_T9 = 25,
44 BL_REG_K0 = 26,
45 BL_REG_K1 = 27,
46 BL_REG_GP = 28,
47 BL_REG_SP = 29,
48 BL_REG_FP = 30,
49 BL_REG_RA = 31,
50 } bl_reg;
51
bootcpu_supports_isa(uint64_t isa_mask)52 static bool bootcpu_supports_isa(uint64_t isa_mask)
53 {
54 return cpu_supports_isa(&MIPS_CPU(first_cpu)->env, isa_mask);
55 }
56
st_nm32_p(void ** ptr,uint32_t insn)57 static void st_nm32_p(void **ptr, uint32_t insn)
58 {
59 uint16_t *p = *ptr;
60
61 stw_p(p, insn >> 16);
62 p++;
63 stw_p(p, insn >> 0);
64 p++;
65
66 *ptr = p;
67 }
68
69 /* Base types */
bl_gen_nop(void ** ptr)70 static void bl_gen_nop(void **ptr)
71 {
72 if (bootcpu_supports_isa(ISA_NANOMIPS32)) {
73 st_nm32_p(ptr, 0x8000c000);
74 } else {
75 uint32_t *p = *ptr;
76
77 stl_p(p, 0);
78 p++;
79 *ptr = p;
80 }
81 }
82
bl_gen_r_type(void ** ptr,uint8_t opcode,bl_reg rs,bl_reg rt,bl_reg rd,uint8_t shift,uint8_t funct)83 static void bl_gen_r_type(void **ptr, uint8_t opcode,
84 bl_reg rs, bl_reg rt, bl_reg rd,
85 uint8_t shift, uint8_t funct)
86 {
87 uint32_t *p = *ptr;
88 uint32_t insn = 0;
89
90 insn = deposit32(insn, 26, 6, opcode);
91 insn = deposit32(insn, 21, 5, rs);
92 insn = deposit32(insn, 16, 5, rt);
93 insn = deposit32(insn, 11, 5, rd);
94 insn = deposit32(insn, 6, 5, shift);
95 insn = deposit32(insn, 0, 6, funct);
96
97 stl_p(p, insn);
98 p++;
99
100 *ptr = p;
101 }
102
bl_gen_i_type(void ** ptr,uint8_t opcode,bl_reg rs,bl_reg rt,uint16_t imm)103 static void bl_gen_i_type(void **ptr, uint8_t opcode,
104 bl_reg rs, bl_reg rt, uint16_t imm)
105 {
106 uint32_t *p = *ptr;
107 uint32_t insn = 0;
108
109 insn = deposit32(insn, 26, 6, opcode);
110 insn = deposit32(insn, 21, 5, rs);
111 insn = deposit32(insn, 16, 5, rt);
112 insn = deposit32(insn, 0, 16, imm);
113
114 stl_p(p, insn);
115 p++;
116
117 *ptr = p;
118 }
119
120 /* Single instructions */
bl_gen_dsll(void ** p,bl_reg rd,bl_reg rt,uint8_t sa)121 static void bl_gen_dsll(void **p, bl_reg rd, bl_reg rt, uint8_t sa)
122 {
123 if (bootcpu_supports_isa(ISA_MIPS3)) {
124 bl_gen_r_type(p, 0, 0, rt, rd, sa, 0x38);
125 } else {
126 g_assert_not_reached(); /* unsupported */
127 }
128 }
129
bl_gen_jalr(void ** p,bl_reg rs)130 static void bl_gen_jalr(void **p, bl_reg rs)
131 {
132 if (bootcpu_supports_isa(ISA_NANOMIPS32)) {
133 uint32_t insn = 0;
134
135 insn = deposit32(insn, 26, 6, 0b010010); /* JALRC */
136 insn = deposit32(insn, 21, 5, BL_REG_RA);
137 insn = deposit32(insn, 16, 5, rs);
138
139 st_nm32_p(p, insn);
140 } else {
141 bl_gen_r_type(p, 0, rs, 0, BL_REG_RA, 0, 0x09);
142 }
143 }
144
bl_gen_lui_nm(void ** ptr,bl_reg rt,uint32_t imm20)145 static void bl_gen_lui_nm(void **ptr, bl_reg rt, uint32_t imm20)
146 {
147 uint32_t insn = 0;
148
149 assert(extract32(imm20, 0, 20) == imm20);
150 insn = deposit32(insn, 26, 6, 0b111000);
151 insn = deposit32(insn, 21, 5, rt);
152 insn = deposit32(insn, 12, 9, extract32(imm20, 0, 9));
153 insn = deposit32(insn, 2, 10, extract32(imm20, 9, 10));
154 insn = deposit32(insn, 0, 1, sextract32(imm20, 19, 1));
155
156 st_nm32_p(ptr, insn);
157 }
158
bl_gen_lui(void ** p,bl_reg rt,uint16_t imm)159 static void bl_gen_lui(void **p, bl_reg rt, uint16_t imm)
160 {
161 /* R6: It's a alias of AUI with RS = 0 */
162 bl_gen_i_type(p, 0x0f, 0, rt, imm);
163 }
164
bl_gen_ori_nm(void ** ptr,bl_reg rt,bl_reg rs,uint16_t imm12)165 static void bl_gen_ori_nm(void **ptr, bl_reg rt, bl_reg rs, uint16_t imm12)
166 {
167 uint32_t insn = 0;
168
169 assert(extract32(imm12, 0, 12) == imm12);
170 insn = deposit32(insn, 26, 6, 0b100000);
171 insn = deposit32(insn, 21, 5, rt);
172 insn = deposit32(insn, 16, 5, rs);
173 insn = deposit32(insn, 0, 12, imm12);
174
175 st_nm32_p(ptr, insn);
176 }
177
bl_gen_ori(void ** p,bl_reg rt,bl_reg rs,uint16_t imm)178 static void bl_gen_ori(void **p, bl_reg rt, bl_reg rs, uint16_t imm)
179 {
180 bl_gen_i_type(p, 0x0d, rs, rt, imm);
181 }
182
bl_gen_sw_nm(void ** ptr,bl_reg rt,uint8_t rs,uint16_t ofs12)183 static void bl_gen_sw_nm(void **ptr, bl_reg rt, uint8_t rs, uint16_t ofs12)
184 {
185 uint32_t insn = 0;
186
187 assert(extract32(ofs12, 0, 12) == ofs12);
188 insn = deposit32(insn, 26, 6, 0b100001);
189 insn = deposit32(insn, 21, 5, rt);
190 insn = deposit32(insn, 16, 5, rs);
191 insn = deposit32(insn, 12, 4, 0b1001);
192 insn = deposit32(insn, 0, 12, ofs12);
193
194 st_nm32_p(ptr, insn);
195 }
196
bl_gen_sw(void ** p,bl_reg rt,uint8_t base,uint16_t offset)197 static void bl_gen_sw(void **p, bl_reg rt, uint8_t base, uint16_t offset)
198 {
199 if (bootcpu_supports_isa(ISA_NANOMIPS32)) {
200 bl_gen_sw_nm(p, rt, base, offset);
201 } else {
202 bl_gen_i_type(p, 0x2b, base, rt, offset);
203 }
204 }
205
bl_gen_sd(void ** p,bl_reg rt,uint8_t base,uint16_t offset)206 static void bl_gen_sd(void **p, bl_reg rt, uint8_t base, uint16_t offset)
207 {
208 if (bootcpu_supports_isa(ISA_MIPS3)) {
209 bl_gen_i_type(p, 0x3f, base, rt, offset);
210 } else {
211 g_assert_not_reached(); /* unsupported */
212 }
213 }
214
215 /* Pseudo instructions */
bl_gen_li(void ** p,bl_reg rt,uint32_t imm)216 static void bl_gen_li(void **p, bl_reg rt, uint32_t imm)
217 {
218 if (bootcpu_supports_isa(ISA_NANOMIPS32)) {
219 bl_gen_lui_nm(p, rt, extract32(imm, 12, 20));
220 bl_gen_ori_nm(p, rt, rt, extract32(imm, 0, 12));
221 } else {
222 bl_gen_lui(p, rt, extract32(imm, 16, 16));
223 bl_gen_ori(p, rt, rt, extract32(imm, 0, 16));
224 }
225 }
226
bl_gen_dli(void ** p,bl_reg rt,uint64_t imm)227 static void bl_gen_dli(void **p, bl_reg rt, uint64_t imm)
228 {
229 bl_gen_li(p, rt, extract64(imm, 32, 32));
230 bl_gen_dsll(p, rt, rt, 16);
231 bl_gen_ori(p, rt, rt, extract64(imm, 16, 16));
232 bl_gen_dsll(p, rt, rt, 16);
233 bl_gen_ori(p, rt, rt, extract64(imm, 0, 16));
234 }
235
bl_gen_load_ulong(void ** p,bl_reg rt,target_ulong imm)236 static void bl_gen_load_ulong(void **p, bl_reg rt, target_ulong imm)
237 {
238 if (bootcpu_supports_isa(ISA_MIPS3)) {
239 bl_gen_dli(p, rt, imm); /* 64bit */
240 } else {
241 bl_gen_li(p, rt, imm); /* 32bit */
242 }
243 }
244
245 /* Helpers */
bl_gen_jump_to(void ** p,target_ulong jump_addr)246 void bl_gen_jump_to(void **p, target_ulong jump_addr)
247 {
248 bl_gen_load_ulong(p, BL_REG_T9, jump_addr);
249 bl_gen_jalr(p, BL_REG_T9);
250 bl_gen_nop(p); /* delay slot */
251 }
252
bl_gen_jump_kernel(void ** p,bool set_sp,target_ulong sp,bool set_a0,target_ulong a0,bool set_a1,target_ulong a1,bool set_a2,target_ulong a2,bool set_a3,target_ulong a3,target_ulong kernel_addr)253 void bl_gen_jump_kernel(void **p,
254 bool set_sp, target_ulong sp,
255 bool set_a0, target_ulong a0,
256 bool set_a1, target_ulong a1,
257 bool set_a2, target_ulong a2,
258 bool set_a3, target_ulong a3,
259 target_ulong kernel_addr)
260 {
261 if (set_sp) {
262 bl_gen_load_ulong(p, BL_REG_SP, sp);
263 }
264 if (set_a0) {
265 bl_gen_load_ulong(p, BL_REG_A0, a0);
266 }
267 if (set_a1) {
268 bl_gen_load_ulong(p, BL_REG_A1, a1);
269 }
270 if (set_a2) {
271 bl_gen_load_ulong(p, BL_REG_A2, a2);
272 }
273 if (set_a3) {
274 bl_gen_load_ulong(p, BL_REG_A3, a3);
275 }
276
277 bl_gen_jump_to(p, kernel_addr);
278 }
279
bl_gen_write_ulong(void ** p,target_ulong addr,target_ulong val)280 void bl_gen_write_ulong(void **p, target_ulong addr, target_ulong val)
281 {
282 bl_gen_load_ulong(p, BL_REG_K0, val);
283 bl_gen_load_ulong(p, BL_REG_K1, addr);
284 if (bootcpu_supports_isa(ISA_MIPS3)) {
285 bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0);
286 } else {
287 bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0);
288 }
289 }
290
bl_gen_write_u32(void ** p,target_ulong addr,uint32_t val)291 void bl_gen_write_u32(void **p, target_ulong addr, uint32_t val)
292 {
293 bl_gen_li(p, BL_REG_K0, val);
294 bl_gen_load_ulong(p, BL_REG_K1, addr);
295 bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0);
296 }
297
bl_gen_write_u64(void ** p,target_ulong addr,uint64_t val)298 void bl_gen_write_u64(void **p, target_ulong addr, uint64_t val)
299 {
300 bl_gen_dli(p, BL_REG_K0, val);
301 bl_gen_load_ulong(p, BL_REG_K1, addr);
302 bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0);
303 }
304