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 bl_gen_r_type(p, 0, rs, 0, BL_REG_RA, 0, 0x09); 133 } 134 135 static void bl_gen_lui(void **p, bl_reg rt, uint16_t imm) 136 { 137 /* R6: It's a alias of AUI with RS = 0 */ 138 bl_gen_i_type(p, 0x0f, 0, rt, imm); 139 } 140 141 static void bl_gen_ori(void **p, bl_reg rt, bl_reg rs, uint16_t imm) 142 { 143 bl_gen_i_type(p, 0x0d, rs, rt, imm); 144 } 145 146 static void bl_gen_sw_nm(void **ptr, bl_reg rt, uint8_t rs, uint16_t ofs12) 147 { 148 uint32_t insn = 0; 149 150 assert(extract32(ofs12, 0, 12) == ofs12); 151 insn = deposit32(insn, 26, 6, 0b100001); 152 insn = deposit32(insn, 21, 5, rt); 153 insn = deposit32(insn, 16, 5, rs); 154 insn = deposit32(insn, 12, 4, 0b1001); 155 insn = deposit32(insn, 0, 12, ofs12); 156 157 st_nm32_p(ptr, insn); 158 } 159 160 static void bl_gen_sw(void **p, bl_reg rt, uint8_t base, uint16_t offset) 161 { 162 if (bootcpu_supports_isa(ISA_NANOMIPS32)) { 163 bl_gen_sw_nm(p, rt, base, offset); 164 } else { 165 bl_gen_i_type(p, 0x2b, base, rt, offset); 166 } 167 } 168 169 static void bl_gen_sd(void **p, bl_reg rt, uint8_t base, uint16_t offset) 170 { 171 if (bootcpu_supports_isa(ISA_MIPS3)) { 172 bl_gen_i_type(p, 0x3f, base, rt, offset); 173 } else { 174 g_assert_not_reached(); /* unsupported */ 175 } 176 } 177 178 /* Pseudo instructions */ 179 static void bl_gen_li(void **p, bl_reg rt, uint32_t imm) 180 { 181 bl_gen_lui(p, rt, extract32(imm, 16, 16)); 182 bl_gen_ori(p, rt, rt, extract32(imm, 0, 16)); 183 } 184 185 static void bl_gen_dli(void **p, bl_reg rt, uint64_t imm) 186 { 187 bl_gen_li(p, rt, extract64(imm, 32, 32)); 188 bl_gen_dsll(p, rt, rt, 16); 189 bl_gen_ori(p, rt, rt, extract64(imm, 16, 16)); 190 bl_gen_dsll(p, rt, rt, 16); 191 bl_gen_ori(p, rt, rt, extract64(imm, 0, 16)); 192 } 193 194 static void bl_gen_load_ulong(void **p, bl_reg rt, target_ulong imm) 195 { 196 if (bootcpu_supports_isa(ISA_MIPS3)) { 197 bl_gen_dli(p, rt, imm); /* 64bit */ 198 } else { 199 bl_gen_li(p, rt, imm); /* 32bit */ 200 } 201 } 202 203 /* Helpers */ 204 void bl_gen_jump_to(void **p, target_ulong jump_addr) 205 { 206 bl_gen_load_ulong(p, BL_REG_T9, jump_addr); 207 bl_gen_jalr(p, BL_REG_T9); 208 bl_gen_nop(p); /* delay slot */ 209 } 210 211 void bl_gen_jump_kernel(void **p, 212 bool set_sp, target_ulong sp, 213 bool set_a0, target_ulong a0, 214 bool set_a1, target_ulong a1, 215 bool set_a2, target_ulong a2, 216 bool set_a3, target_ulong a3, 217 target_ulong kernel_addr) 218 { 219 if (set_sp) { 220 bl_gen_load_ulong(p, BL_REG_SP, sp); 221 } 222 if (set_a0) { 223 bl_gen_load_ulong(p, BL_REG_A0, a0); 224 } 225 if (set_a1) { 226 bl_gen_load_ulong(p, BL_REG_A1, a1); 227 } 228 if (set_a2) { 229 bl_gen_load_ulong(p, BL_REG_A2, a2); 230 } 231 if (set_a3) { 232 bl_gen_load_ulong(p, BL_REG_A3, a3); 233 } 234 235 bl_gen_jump_to(p, kernel_addr); 236 } 237 238 void bl_gen_write_ulong(void **p, target_ulong addr, target_ulong val) 239 { 240 bl_gen_load_ulong(p, BL_REG_K0, val); 241 bl_gen_load_ulong(p, BL_REG_K1, addr); 242 if (bootcpu_supports_isa(ISA_MIPS3)) { 243 bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0); 244 } else { 245 bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0); 246 } 247 } 248 249 void bl_gen_write_u32(void **p, target_ulong addr, uint32_t val) 250 { 251 bl_gen_li(p, BL_REG_K0, val); 252 bl_gen_load_ulong(p, BL_REG_K1, addr); 253 bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0); 254 } 255 256 void bl_gen_write_u64(void **p, target_ulong addr, uint64_t val) 257 { 258 bl_gen_dli(p, BL_REG_K0, val); 259 bl_gen_load_ulong(p, BL_REG_K1, addr); 260 bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0); 261 } 262