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(void **p, bl_reg rt, uint8_t base, uint16_t offset) 147 { 148 bl_gen_i_type(p, 0x2b, base, rt, offset); 149 } 150 151 static void bl_gen_sd(void **p, bl_reg rt, uint8_t base, uint16_t offset) 152 { 153 if (bootcpu_supports_isa(ISA_MIPS3)) { 154 bl_gen_i_type(p, 0x3f, base, rt, offset); 155 } else { 156 g_assert_not_reached(); /* unsupported */ 157 } 158 } 159 160 /* Pseudo instructions */ 161 static void bl_gen_li(void **p, bl_reg rt, uint32_t imm) 162 { 163 bl_gen_lui(p, rt, extract32(imm, 16, 16)); 164 bl_gen_ori(p, rt, rt, extract32(imm, 0, 16)); 165 } 166 167 static void bl_gen_dli(void **p, bl_reg rt, uint64_t imm) 168 { 169 bl_gen_li(p, rt, extract64(imm, 32, 32)); 170 bl_gen_dsll(p, rt, rt, 16); 171 bl_gen_ori(p, rt, rt, extract64(imm, 16, 16)); 172 bl_gen_dsll(p, rt, rt, 16); 173 bl_gen_ori(p, rt, rt, extract64(imm, 0, 16)); 174 } 175 176 static void bl_gen_load_ulong(void **p, bl_reg rt, target_ulong imm) 177 { 178 if (bootcpu_supports_isa(ISA_MIPS3)) { 179 bl_gen_dli(p, rt, imm); /* 64bit */ 180 } else { 181 bl_gen_li(p, rt, imm); /* 32bit */ 182 } 183 } 184 185 /* Helpers */ 186 void bl_gen_jump_to(void **p, target_ulong jump_addr) 187 { 188 bl_gen_load_ulong(p, BL_REG_T9, jump_addr); 189 bl_gen_jalr(p, BL_REG_T9); 190 bl_gen_nop(p); /* delay slot */ 191 } 192 193 void bl_gen_jump_kernel(void **p, 194 bool set_sp, target_ulong sp, 195 bool set_a0, target_ulong a0, 196 bool set_a1, target_ulong a1, 197 bool set_a2, target_ulong a2, 198 bool set_a3, target_ulong a3, 199 target_ulong kernel_addr) 200 { 201 if (set_sp) { 202 bl_gen_load_ulong(p, BL_REG_SP, sp); 203 } 204 if (set_a0) { 205 bl_gen_load_ulong(p, BL_REG_A0, a0); 206 } 207 if (set_a1) { 208 bl_gen_load_ulong(p, BL_REG_A1, a1); 209 } 210 if (set_a2) { 211 bl_gen_load_ulong(p, BL_REG_A2, a2); 212 } 213 if (set_a3) { 214 bl_gen_load_ulong(p, BL_REG_A3, a3); 215 } 216 217 bl_gen_jump_to(p, kernel_addr); 218 } 219 220 void bl_gen_write_ulong(void **p, target_ulong addr, target_ulong val) 221 { 222 bl_gen_load_ulong(p, BL_REG_K0, val); 223 bl_gen_load_ulong(p, BL_REG_K1, addr); 224 if (bootcpu_supports_isa(ISA_MIPS3)) { 225 bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0); 226 } else { 227 bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0); 228 } 229 } 230 231 void bl_gen_write_u32(void **p, target_ulong addr, uint32_t val) 232 { 233 bl_gen_li(p, BL_REG_K0, val); 234 bl_gen_load_ulong(p, BL_REG_K1, addr); 235 bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0); 236 } 237 238 void bl_gen_write_u64(void **p, target_ulong addr, uint64_t val) 239 { 240 bl_gen_dli(p, BL_REG_K0, val); 241 bl_gen_load_ulong(p, BL_REG_K1, addr); 242 bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0); 243 } 244