]>
Commit | Line | Data |
---|---|---|
3ebbf861 JY |
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 | /* Base types */ | |
58 | static void bl_gen_nop(uint32_t **p) | |
59 | { | |
60 | stl_p(*p, 0); | |
61 | *p = *p + 1; | |
62 | } | |
63 | ||
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) | |
67 | { | |
68 | uint32_t insn = 0; | |
69 | ||
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); | |
76 | ||
77 | stl_p(*p, insn); | |
78 | *p = *p + 1; | |
79 | } | |
80 | ||
81 | static void bl_gen_i_type(uint32_t **p, uint8_t opcode, | |
82 | bl_reg rs, bl_reg rt, uint16_t imm) | |
83 | { | |
84 | uint32_t insn = 0; | |
85 | ||
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); | |
90 | ||
91 | stl_p(*p, insn); | |
92 | *p = *p + 1; | |
93 | } | |
94 | ||
95 | /* Single instructions */ | |
96 | static void bl_gen_dsll(uint32_t **p, bl_reg rd, bl_reg rt, uint8_t sa) | |
97 | { | |
98 | if (bootcpu_supports_isa(ISA_MIPS3)) { | |
99 | bl_gen_r_type(p, 0, 0, rt, rd, sa, 0x38); | |
100 | } else { | |
101 | g_assert_not_reached(); /* unsupported */ | |
102 | } | |
103 | } | |
104 | ||
105 | static void bl_gen_jalr(uint32_t **p, bl_reg rs) | |
106 | { | |
107 | bl_gen_r_type(p, 0, rs, 0, BL_REG_RA, 0, 0x09); | |
108 | } | |
109 | ||
110 | static void bl_gen_lui(uint32_t **p, bl_reg rt, uint16_t imm) | |
111 | { | |
112 | /* R6: It's a alias of AUI with RS = 0 */ | |
113 | bl_gen_i_type(p, 0x0f, 0, rt, imm); | |
114 | } | |
115 | ||
116 | static void bl_gen_ori(uint32_t **p, bl_reg rt, bl_reg rs, uint16_t imm) | |
117 | { | |
118 | bl_gen_i_type(p, 0x0d, rs, rt, imm); | |
119 | } | |
120 | ||
121 | static void bl_gen_sw(uint32_t **p, bl_reg rt, uint8_t base, uint16_t offset) | |
122 | { | |
123 | bl_gen_i_type(p, 0x2b, base, rt, offset); | |
124 | } | |
125 | ||
126 | static void bl_gen_sd(uint32_t **p, bl_reg rt, uint8_t base, uint16_t offset) | |
127 | { | |
128 | if (bootcpu_supports_isa(ISA_MIPS3)) { | |
129 | bl_gen_i_type(p, 0x3f, base, rt, offset); | |
130 | } else { | |
131 | g_assert_not_reached(); /* unsupported */ | |
132 | } | |
133 | } | |
134 | ||
135 | /* Pseudo instructions */ | |
136 | static void bl_gen_li(uint32_t **p, bl_reg rt, uint32_t imm) | |
137 | { | |
138 | bl_gen_lui(p, rt, extract32(imm, 16, 16)); | |
139 | bl_gen_ori(p, rt, rt, extract32(imm, 0, 16)); | |
140 | } | |
141 | ||
142 | static void bl_gen_dli(uint32_t **p, bl_reg rt, uint64_t imm) | |
143 | { | |
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)); | |
149 | } | |
150 | ||
151 | static void bl_gen_load_ulong(uint32_t **p, bl_reg rt, target_ulong imm) | |
152 | { | |
153 | if (bootcpu_supports_isa(ISA_MIPS3)) { | |
154 | bl_gen_dli(p, rt, imm); /* 64bit */ | |
155 | } else { | |
156 | bl_gen_li(p, rt, imm); /* 32bit */ | |
157 | } | |
158 | } | |
159 | ||
160 | /* Helpers */ | |
161 | void bl_gen_jump_to(uint32_t **p, target_ulong jump_addr) | |
162 | { | |
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 */ | |
166 | } | |
167 | ||
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) | |
171 | { | |
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); | |
177 | ||
178 | bl_gen_jump_to(p, kernel_addr); | |
179 | } | |
180 | ||
181 | void bl_gen_write_ulong(uint32_t **p, target_ulong addr, target_ulong val) | |
182 | { | |
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); | |
186 | } | |
187 | ||
188 | void bl_gen_write_u32(uint32_t **p, target_ulong addr, uint32_t val) | |
189 | { | |
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); | |
193 | } | |
194 | ||
195 | void bl_gen_write_u64(uint32_t **p, target_ulong addr, uint64_t val) | |
196 | { | |
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); | |
200 | } |