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 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 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 */ 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 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 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 */ 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 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 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 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 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 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 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 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 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 */ 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 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 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 */ 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 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 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 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 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