2 * Utility for QEMU MIPS to generate it's simple bootloader
4 * Instructions used here are carefully selected to keep compatibility with
7 * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "qemu/osdep.h"
13 #include "qemu/bitops.h"
15 #include "hw/mips/bootloader.h"
52 static bool bootcpu_supports_isa(uint64_t isa_mask
)
54 return cpu_supports_isa(&MIPS_CPU(first_cpu
)->env
, isa_mask
);
58 static void bl_gen_nop(uint32_t **p
)
64 static void bl_gen_r_type(uint32_t **p
, uint8_t opcode
,
65 bl_reg rs
, bl_reg rt
, bl_reg rd
,
66 uint8_t shift
, uint8_t funct
)
70 insn
= deposit32(insn
, 26, 6, opcode
);
71 insn
= deposit32(insn
, 21, 5, rs
);
72 insn
= deposit32(insn
, 16, 5, rt
);
73 insn
= deposit32(insn
, 11, 5, rd
);
74 insn
= deposit32(insn
, 6, 5, shift
);
75 insn
= deposit32(insn
, 0, 6, funct
);
81 static void bl_gen_i_type(uint32_t **p
, uint8_t opcode
,
82 bl_reg rs
, bl_reg rt
, uint16_t imm
)
86 insn
= deposit32(insn
, 26, 6, opcode
);
87 insn
= deposit32(insn
, 21, 5, rs
);
88 insn
= deposit32(insn
, 16, 5, rt
);
89 insn
= deposit32(insn
, 0, 16, imm
);
95 /* Single instructions */
96 static void bl_gen_dsll(uint32_t **p
, bl_reg rd
, bl_reg rt
, uint8_t sa
)
98 if (bootcpu_supports_isa(ISA_MIPS3
)) {
99 bl_gen_r_type(p
, 0, 0, rt
, rd
, sa
, 0x38);
101 g_assert_not_reached(); /* unsupported */
105 static void bl_gen_jalr(uint32_t **p
, bl_reg rs
)
107 bl_gen_r_type(p
, 0, rs
, 0, BL_REG_RA
, 0, 0x09);
110 static void bl_gen_lui(uint32_t **p
, bl_reg rt
, uint16_t imm
)
112 /* R6: It's a alias of AUI with RS = 0 */
113 bl_gen_i_type(p
, 0x0f, 0, rt
, imm
);
116 static void bl_gen_ori(uint32_t **p
, bl_reg rt
, bl_reg rs
, uint16_t imm
)
118 bl_gen_i_type(p
, 0x0d, rs
, rt
, imm
);
121 static void bl_gen_sw(uint32_t **p
, bl_reg rt
, uint8_t base
, uint16_t offset
)
123 bl_gen_i_type(p
, 0x2b, base
, rt
, offset
);
126 static void bl_gen_sd(uint32_t **p
, bl_reg rt
, uint8_t base
, uint16_t offset
)
128 if (bootcpu_supports_isa(ISA_MIPS3
)) {
129 bl_gen_i_type(p
, 0x3f, base
, rt
, offset
);
131 g_assert_not_reached(); /* unsupported */
135 /* Pseudo instructions */
136 static void bl_gen_li(uint32_t **p
, bl_reg rt
, uint32_t imm
)
138 bl_gen_lui(p
, rt
, extract32(imm
, 16, 16));
139 bl_gen_ori(p
, rt
, rt
, extract32(imm
, 0, 16));
142 static void bl_gen_dli(uint32_t **p
, bl_reg rt
, uint64_t imm
)
144 bl_gen_li(p
, rt
, extract64(imm
, 32, 32));
145 bl_gen_dsll(p
, rt
, rt
, 16);
146 bl_gen_ori(p
, rt
, rt
, extract64(imm
, 16, 16));
147 bl_gen_dsll(p
, rt
, rt
, 16);
148 bl_gen_ori(p
, rt
, rt
, extract64(imm
, 0, 16));
151 static void bl_gen_load_ulong(uint32_t **p
, bl_reg rt
, target_ulong imm
)
153 if (bootcpu_supports_isa(ISA_MIPS3
)) {
154 bl_gen_dli(p
, rt
, imm
); /* 64bit */
156 bl_gen_li(p
, rt
, imm
); /* 32bit */
161 void bl_gen_jump_to(uint32_t **p
, target_ulong jump_addr
)
163 bl_gen_load_ulong(p
, BL_REG_T9
, jump_addr
);
164 bl_gen_jalr(p
, BL_REG_T9
);
165 bl_gen_nop(p
); /* delay slot */
168 void bl_gen_jump_kernel(uint32_t **p
, target_ulong sp
, target_ulong a0
,
169 target_ulong a1
, target_ulong a2
, target_ulong a3
,
170 target_ulong kernel_addr
)
172 bl_gen_load_ulong(p
, BL_REG_SP
, sp
);
173 bl_gen_load_ulong(p
, BL_REG_A0
, a0
);
174 bl_gen_load_ulong(p
, BL_REG_A1
, a1
);
175 bl_gen_load_ulong(p
, BL_REG_A2
, a2
);
176 bl_gen_load_ulong(p
, BL_REG_A3
, a3
);
178 bl_gen_jump_to(p
, kernel_addr
);
181 void bl_gen_write_ulong(uint32_t **p
, target_ulong addr
, target_ulong val
)
183 bl_gen_load_ulong(p
, BL_REG_K0
, val
);
184 bl_gen_load_ulong(p
, BL_REG_K1
, addr
);
185 bl_gen_sd(p
, BL_REG_K0
, BL_REG_K1
, 0x0);
188 void bl_gen_write_u32(uint32_t **p
, target_ulong addr
, uint32_t val
)
190 bl_gen_li(p
, BL_REG_K0
, val
);
191 bl_gen_load_ulong(p
, BL_REG_K1
, addr
);
192 bl_gen_sw(p
, BL_REG_K0
, BL_REG_K1
, 0x0);
195 void bl_gen_write_u64(uint32_t **p
, target_ulong addr
, uint64_t val
)
197 bl_gen_dli(p
, BL_REG_K0
, val
);
198 bl_gen_load_ulong(p
, BL_REG_K1
, addr
);
199 bl_gen_sd(p
, BL_REG_K0
, BL_REG_K1
, 0x0);