root / hw / mips_malta.c @ 087f4ae0
History | View | Annotate | Download (15.9 kB)
1 | 5856de80 | ths | /*
|
---|---|---|---|
2 | 5856de80 | ths | * QEMU Malta board support
|
3 | 5856de80 | ths | *
|
4 | 5856de80 | ths | * Copyright (c) 2006 Aurelien Jarno
|
5 | 5856de80 | ths | *
|
6 | 5856de80 | ths | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 5856de80 | ths | * of this software and associated documentation files (the "Software"), to deal
|
8 | 5856de80 | ths | * in the Software without restriction, including without limitation the rights
|
9 | 5856de80 | ths | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 5856de80 | ths | * copies of the Software, and to permit persons to whom the Software is
|
11 | 5856de80 | ths | * furnished to do so, subject to the following conditions:
|
12 | 5856de80 | ths | *
|
13 | 5856de80 | ths | * The above copyright notice and this permission notice shall be included in
|
14 | 5856de80 | ths | * all copies or substantial portions of the Software.
|
15 | 5856de80 | ths | *
|
16 | 5856de80 | ths | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 5856de80 | ths | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 5856de80 | ths | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 5856de80 | ths | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 5856de80 | ths | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 5856de80 | ths | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 5856de80 | ths | * THE SOFTWARE.
|
23 | 5856de80 | ths | */
|
24 | 5856de80 | ths | |
25 | 5856de80 | ths | #include "vl.h" |
26 | 5856de80 | ths | |
27 | 44cbbf18 | ths | #ifdef TARGET_WORDS_BIGENDIAN
|
28 | 44cbbf18 | ths | #define BIOS_FILENAME "mips_bios.bin" |
29 | 44cbbf18 | ths | #else
|
30 | 44cbbf18 | ths | #define BIOS_FILENAME "mipsel_bios.bin" |
31 | 44cbbf18 | ths | #endif
|
32 | 44cbbf18 | ths | |
33 | 5856de80 | ths | #ifdef MIPS_HAS_MIPS64
|
34 | 44cbbf18 | ths | #define INITRD_LOAD_ADDR (int64_t)0x80800000 |
35 | 5856de80 | ths | #else
|
36 | 44cbbf18 | ths | #define INITRD_LOAD_ADDR (int32_t)0x80800000 |
37 | 5856de80 | ths | #endif
|
38 | 5856de80 | ths | |
39 | 3ddd0065 | ths | #define ENVP_ADDR (int32_t)0x80002000 |
40 | 44cbbf18 | ths | #define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) |
41 | 5856de80 | ths | |
42 | 5856de80 | ths | #define ENVP_NB_ENTRIES 16 |
43 | 5856de80 | ths | #define ENVP_ENTRY_SIZE 256 |
44 | 5856de80 | ths | |
45 | 5856de80 | ths | |
46 | 5856de80 | ths | extern FILE *logfile;
|
47 | 5856de80 | ths | |
48 | 5856de80 | ths | typedef struct { |
49 | 5856de80 | ths | uint32_t leds; |
50 | 5856de80 | ths | uint32_t brk; |
51 | 5856de80 | ths | uint32_t gpout; |
52 | 5856de80 | ths | uint32_t i2coe; |
53 | 5856de80 | ths | uint32_t i2cout; |
54 | 5856de80 | ths | uint32_t i2csel; |
55 | 5856de80 | ths | CharDriverState *display; |
56 | 5856de80 | ths | char display_text[9]; |
57 | 5856de80 | ths | } MaltaFPGAState; |
58 | 5856de80 | ths | |
59 | 5856de80 | ths | static PITState *pit;
|
60 | 5856de80 | ths | |
61 | 4de9b249 | ths | /* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
|
62 | 5856de80 | ths | static void pic_irq_request(void *opaque, int level) |
63 | 5856de80 | ths | { |
64 | 4de9b249 | ths | cpu_mips_irq_request(opaque, 2, level);
|
65 | 5856de80 | ths | } |
66 | 5856de80 | ths | |
67 | 5856de80 | ths | /* Malta FPGA */
|
68 | 5856de80 | ths | static void malta_fpga_update_display(void *opaque) |
69 | 5856de80 | ths | { |
70 | 5856de80 | ths | char leds_text[9]; |
71 | 5856de80 | ths | int i;
|
72 | 5856de80 | ths | MaltaFPGAState *s = opaque; |
73 | 5856de80 | ths | |
74 | 5856de80 | ths | for (i = 7 ; i >= 0 ; i--) { |
75 | 5856de80 | ths | if (s->leds & (1 << i)) |
76 | 5856de80 | ths | leds_text[i] = '#';
|
77 | 5856de80 | ths | else
|
78 | 5856de80 | ths | leds_text[i] = ' ';
|
79 | 5856de80 | ths | } |
80 | 5856de80 | ths | leds_text[8] = '\0'; |
81 | 5856de80 | ths | |
82 | 472c5273 | ths | qemu_chr_printf(s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", leds_text); |
83 | 472c5273 | ths | qemu_chr_printf(s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text); |
84 | 5856de80 | ths | } |
85 | 5856de80 | ths | |
86 | 5856de80 | ths | static uint32_t malta_fpga_readl(void *opaque, target_phys_addr_t addr) |
87 | 5856de80 | ths | { |
88 | 5856de80 | ths | MaltaFPGAState *s = opaque; |
89 | 5856de80 | ths | uint32_t val = 0;
|
90 | 5856de80 | ths | uint32_t saddr; |
91 | 5856de80 | ths | |
92 | 5856de80 | ths | saddr = (addr & 0xfffff);
|
93 | 5856de80 | ths | |
94 | 5856de80 | ths | switch (saddr) {
|
95 | 5856de80 | ths | |
96 | 5856de80 | ths | /* SWITCH Register */
|
97 | 5856de80 | ths | case 0x00200: |
98 | 5856de80 | ths | val = 0x00000000; /* All switches closed */ |
99 | 5856de80 | ths | break;
|
100 | 5856de80 | ths | |
101 | 5856de80 | ths | /* STATUS Register */
|
102 | 5856de80 | ths | case 0x00208: |
103 | 5856de80 | ths | #ifdef TARGET_WORDS_BIGENDIAN
|
104 | 5856de80 | ths | val = 0x00000012;
|
105 | 5856de80 | ths | #else
|
106 | 5856de80 | ths | val = 0x00000010;
|
107 | 5856de80 | ths | #endif
|
108 | 5856de80 | ths | break;
|
109 | 5856de80 | ths | |
110 | 5856de80 | ths | /* JMPRS Register */
|
111 | 5856de80 | ths | case 0x00210: |
112 | 5856de80 | ths | val = 0x00;
|
113 | 5856de80 | ths | break;
|
114 | 5856de80 | ths | |
115 | 5856de80 | ths | /* LEDBAR Register */
|
116 | 5856de80 | ths | case 0x00408: |
117 | 5856de80 | ths | val = s->leds; |
118 | 5856de80 | ths | break;
|
119 | 5856de80 | ths | |
120 | 5856de80 | ths | /* BRKRES Register */
|
121 | 5856de80 | ths | case 0x00508: |
122 | 5856de80 | ths | val = s->brk; |
123 | 5856de80 | ths | break;
|
124 | 5856de80 | ths | |
125 | 5856de80 | ths | /* GPOUT Register */
|
126 | 5856de80 | ths | case 0x00a00: |
127 | 5856de80 | ths | val = s->gpout; |
128 | 5856de80 | ths | break;
|
129 | 5856de80 | ths | |
130 | 5856de80 | ths | /* XXX: implement a real I2C controller */
|
131 | 5856de80 | ths | |
132 | 5856de80 | ths | /* GPINP Register */
|
133 | 5856de80 | ths | case 0x00a08: |
134 | 5856de80 | ths | /* IN = OUT until a real I2C control is implemented */
|
135 | 5856de80 | ths | if (s->i2csel)
|
136 | 5856de80 | ths | val = s->i2cout; |
137 | 5856de80 | ths | else
|
138 | 5856de80 | ths | val = 0x00;
|
139 | 5856de80 | ths | break;
|
140 | 5856de80 | ths | |
141 | 5856de80 | ths | /* I2CINP Register */
|
142 | 5856de80 | ths | case 0x00b00: |
143 | 5856de80 | ths | val = 0x00000003;
|
144 | 5856de80 | ths | break;
|
145 | 5856de80 | ths | |
146 | 5856de80 | ths | /* I2COE Register */
|
147 | 5856de80 | ths | case 0x00b08: |
148 | 5856de80 | ths | val = s->i2coe; |
149 | 5856de80 | ths | break;
|
150 | 5856de80 | ths | |
151 | 5856de80 | ths | /* I2COUT Register */
|
152 | 5856de80 | ths | case 0x00b10: |
153 | 5856de80 | ths | val = s->i2cout; |
154 | 5856de80 | ths | break;
|
155 | 5856de80 | ths | |
156 | 5856de80 | ths | /* I2CSEL Register */
|
157 | 5856de80 | ths | case 0x00b18: |
158 | 5856de80 | ths | val = s->i2cout; |
159 | 5856de80 | ths | break;
|
160 | 5856de80 | ths | |
161 | 5856de80 | ths | default:
|
162 | 5856de80 | ths | #if 0
|
163 | 44cbbf18 | ths | printf ("malta_fpga_read: Bad register offset 0x" TLSZ "\n",
|
164 | 44cbbf18 | ths | addr);
|
165 | 5856de80 | ths | #endif
|
166 | 5856de80 | ths | break;
|
167 | 5856de80 | ths | } |
168 | 5856de80 | ths | return val;
|
169 | 5856de80 | ths | } |
170 | 5856de80 | ths | |
171 | 5856de80 | ths | static void malta_fpga_writel(void *opaque, target_phys_addr_t addr, |
172 | 5856de80 | ths | uint32_t val) |
173 | 5856de80 | ths | { |
174 | 5856de80 | ths | MaltaFPGAState *s = opaque; |
175 | 5856de80 | ths | uint32_t saddr; |
176 | 5856de80 | ths | |
177 | 5856de80 | ths | saddr = (addr & 0xfffff);
|
178 | 5856de80 | ths | |
179 | 5856de80 | ths | switch (saddr) {
|
180 | 5856de80 | ths | |
181 | 5856de80 | ths | /* SWITCH Register */
|
182 | 5856de80 | ths | case 0x00200: |
183 | 5856de80 | ths | break;
|
184 | 5856de80 | ths | |
185 | 5856de80 | ths | /* JMPRS Register */
|
186 | 5856de80 | ths | case 0x00210: |
187 | 5856de80 | ths | break;
|
188 | 5856de80 | ths | |
189 | 5856de80 | ths | /* LEDBAR Register */
|
190 | 5856de80 | ths | /* XXX: implement a 8-LED array */
|
191 | 5856de80 | ths | case 0x00408: |
192 | 5856de80 | ths | s->leds = val & 0xff;
|
193 | 5856de80 | ths | break;
|
194 | 5856de80 | ths | |
195 | 5856de80 | ths | /* ASCIIWORD Register */
|
196 | 5856de80 | ths | case 0x00410: |
197 | 5856de80 | ths | snprintf(s->display_text, 9, "%08X", val); |
198 | 5856de80 | ths | malta_fpga_update_display(s); |
199 | 5856de80 | ths | break;
|
200 | 5856de80 | ths | |
201 | 5856de80 | ths | /* ASCIIPOS0 to ASCIIPOS7 Registers */
|
202 | 5856de80 | ths | case 0x00418: |
203 | 5856de80 | ths | case 0x00420: |
204 | 5856de80 | ths | case 0x00428: |
205 | 5856de80 | ths | case 0x00430: |
206 | 5856de80 | ths | case 0x00438: |
207 | 5856de80 | ths | case 0x00440: |
208 | 5856de80 | ths | case 0x00448: |
209 | 5856de80 | ths | case 0x00450: |
210 | 5856de80 | ths | s->display_text[(saddr - 0x00418) >> 3] = (char) val; |
211 | 5856de80 | ths | malta_fpga_update_display(s); |
212 | 5856de80 | ths | break;
|
213 | 5856de80 | ths | |
214 | 5856de80 | ths | /* SOFTRES Register */
|
215 | 5856de80 | ths | case 0x00500: |
216 | 5856de80 | ths | if (val == 0x42) |
217 | 5856de80 | ths | qemu_system_reset_request (); |
218 | 5856de80 | ths | break;
|
219 | 5856de80 | ths | |
220 | 5856de80 | ths | /* BRKRES Register */
|
221 | 5856de80 | ths | case 0x00508: |
222 | 5856de80 | ths | s->brk = val & 0xff;
|
223 | 5856de80 | ths | break;
|
224 | 5856de80 | ths | |
225 | 5856de80 | ths | /* GPOUT Register */
|
226 | 5856de80 | ths | case 0x00a00: |
227 | 5856de80 | ths | s->gpout = val & 0xff;
|
228 | 5856de80 | ths | break;
|
229 | 5856de80 | ths | |
230 | 5856de80 | ths | /* I2COE Register */
|
231 | 5856de80 | ths | case 0x00b08: |
232 | 5856de80 | ths | s->i2coe = val & 0x03;
|
233 | 5856de80 | ths | break;
|
234 | 5856de80 | ths | |
235 | 5856de80 | ths | /* I2COUT Register */
|
236 | 5856de80 | ths | case 0x00b10: |
237 | 5856de80 | ths | s->i2cout = val & 0x03;
|
238 | 5856de80 | ths | break;
|
239 | 5856de80 | ths | |
240 | 5856de80 | ths | /* I2CSEL Register */
|
241 | 5856de80 | ths | case 0x00b18: |
242 | 5856de80 | ths | s->i2cout = val & 0x01;
|
243 | 5856de80 | ths | break;
|
244 | 5856de80 | ths | |
245 | 5856de80 | ths | default:
|
246 | 5856de80 | ths | #if 0
|
247 | 44cbbf18 | ths | printf ("malta_fpga_write: Bad register offset 0x" TLSZ "\n",
|
248 | 44cbbf18 | ths | addr);
|
249 | 5856de80 | ths | #endif
|
250 | 5856de80 | ths | break;
|
251 | 5856de80 | ths | } |
252 | 5856de80 | ths | } |
253 | 5856de80 | ths | |
254 | 5856de80 | ths | static CPUReadMemoryFunc *malta_fpga_read[] = {
|
255 | 5856de80 | ths | malta_fpga_readl, |
256 | 5856de80 | ths | malta_fpga_readl, |
257 | 5856de80 | ths | malta_fpga_readl |
258 | 5856de80 | ths | }; |
259 | 5856de80 | ths | |
260 | 5856de80 | ths | static CPUWriteMemoryFunc *malta_fpga_write[] = {
|
261 | 5856de80 | ths | malta_fpga_writel, |
262 | 5856de80 | ths | malta_fpga_writel, |
263 | 5856de80 | ths | malta_fpga_writel |
264 | 5856de80 | ths | }; |
265 | 5856de80 | ths | |
266 | 5856de80 | ths | void malta_fpga_reset(void *opaque) |
267 | 5856de80 | ths | { |
268 | 5856de80 | ths | MaltaFPGAState *s = opaque; |
269 | 5856de80 | ths | |
270 | 5856de80 | ths | s->leds = 0x00;
|
271 | 5856de80 | ths | s->brk = 0x0a;
|
272 | 5856de80 | ths | s->gpout = 0x00;
|
273 | 5856de80 | ths | s->i2coe = 0x0;
|
274 | 5856de80 | ths | s->i2cout = 0x3;
|
275 | 5856de80 | ths | s->i2csel = 0x1;
|
276 | 5856de80 | ths | |
277 | 5856de80 | ths | s->display_text[8] = '\0'; |
278 | 5856de80 | ths | snprintf(s->display_text, 9, " "); |
279 | 5856de80 | ths | malta_fpga_update_display(s); |
280 | 5856de80 | ths | } |
281 | 5856de80 | ths | |
282 | 5856de80 | ths | MaltaFPGAState *malta_fpga_init(target_phys_addr_t base) |
283 | 5856de80 | ths | { |
284 | 5856de80 | ths | MaltaFPGAState *s; |
285 | 5856de80 | ths | int malta;
|
286 | 5856de80 | ths | |
287 | 5856de80 | ths | s = (MaltaFPGAState *)qemu_mallocz(sizeof(MaltaFPGAState));
|
288 | 5856de80 | ths | |
289 | 5856de80 | ths | malta = cpu_register_io_memory(0, malta_fpga_read,
|
290 | 5856de80 | ths | malta_fpga_write, s); |
291 | 5856de80 | ths | cpu_register_physical_memory(base, 0x100000, malta);
|
292 | 5856de80 | ths | |
293 | 5856de80 | ths | s->display = qemu_chr_open("vc");
|
294 | af23902b | ths | qemu_chr_printf(s->display, "\e[HMalta LEDBAR\r\n"); |
295 | 5856de80 | ths | qemu_chr_printf(s->display, "+--------+\r\n");
|
296 | 5856de80 | ths | qemu_chr_printf(s->display, "+ +\r\n");
|
297 | 5856de80 | ths | qemu_chr_printf(s->display, "+--------+\r\n");
|
298 | 5856de80 | ths | qemu_chr_printf(s->display, "\n");
|
299 | 5856de80 | ths | qemu_chr_printf(s->display, "Malta ASCII\r\n");
|
300 | 5856de80 | ths | qemu_chr_printf(s->display, "+--------+\r\n");
|
301 | 5856de80 | ths | qemu_chr_printf(s->display, "+ +\r\n");
|
302 | 5856de80 | ths | qemu_chr_printf(s->display, "+--------+\r\n");
|
303 | 5856de80 | ths | |
304 | 5856de80 | ths | malta_fpga_reset(s); |
305 | 5856de80 | ths | qemu_register_reset(malta_fpga_reset, s); |
306 | 5856de80 | ths | |
307 | 5856de80 | ths | return s;
|
308 | 5856de80 | ths | } |
309 | 5856de80 | ths | |
310 | 5856de80 | ths | /* Audio support */
|
311 | 5856de80 | ths | #ifdef HAS_AUDIO
|
312 | 5856de80 | ths | static void audio_init (PCIBus *pci_bus) |
313 | 5856de80 | ths | { |
314 | 5856de80 | ths | struct soundhw *c;
|
315 | 5856de80 | ths | int audio_enabled = 0; |
316 | 5856de80 | ths | |
317 | 5856de80 | ths | for (c = soundhw; !audio_enabled && c->name; ++c) {
|
318 | 5856de80 | ths | audio_enabled = c->enabled; |
319 | 5856de80 | ths | } |
320 | 5856de80 | ths | |
321 | 5856de80 | ths | if (audio_enabled) {
|
322 | 5856de80 | ths | AudioState *s; |
323 | 5856de80 | ths | |
324 | 5856de80 | ths | s = AUD_init (); |
325 | 5856de80 | ths | if (s) {
|
326 | 5856de80 | ths | for (c = soundhw; c->name; ++c) {
|
327 | 5856de80 | ths | if (c->enabled) {
|
328 | 5856de80 | ths | if (c->isa) {
|
329 | 5856de80 | ths | fprintf(stderr, "qemu: Unsupported Sound Card: %s\n", c->name);
|
330 | 5856de80 | ths | exit(1);
|
331 | 5856de80 | ths | } |
332 | 5856de80 | ths | else {
|
333 | 5856de80 | ths | if (pci_bus) {
|
334 | 5856de80 | ths | c->init.init_pci (pci_bus, s); |
335 | 5856de80 | ths | } |
336 | 5856de80 | ths | } |
337 | 5856de80 | ths | } |
338 | 5856de80 | ths | } |
339 | 5856de80 | ths | } |
340 | 5856de80 | ths | } |
341 | 5856de80 | ths | } |
342 | 5856de80 | ths | #endif
|
343 | 5856de80 | ths | |
344 | 5856de80 | ths | /* Network support */
|
345 | 5856de80 | ths | static void network_init (PCIBus *pci_bus) |
346 | 5856de80 | ths | { |
347 | 5856de80 | ths | int i;
|
348 | 5856de80 | ths | NICInfo *nd; |
349 | 5856de80 | ths | |
350 | 5856de80 | ths | for(i = 0; i < nb_nics; i++) { |
351 | 5856de80 | ths | nd = &nd_table[i]; |
352 | 5856de80 | ths | if (!nd->model) {
|
353 | 5856de80 | ths | nd->model = "pcnet";
|
354 | 5856de80 | ths | } |
355 | 5856de80 | ths | if (i == 0 && strcmp(nd->model, "pcnet") == 0) { |
356 | 5856de80 | ths | /* The malta board has a PCNet card using PCI SLOT 11 */
|
357 | 5856de80 | ths | pci_nic_init(pci_bus, nd, 88);
|
358 | 5856de80 | ths | } else {
|
359 | 5856de80 | ths | pci_nic_init(pci_bus, nd, -1);
|
360 | 5856de80 | ths | } |
361 | 5856de80 | ths | } |
362 | 5856de80 | ths | } |
363 | 5856de80 | ths | |
364 | 5856de80 | ths | /* ROM and pseudo bootloader
|
365 | 5856de80 | ths | |
366 | 5856de80 | ths | The following code implements a very very simple bootloader. It first
|
367 | 5856de80 | ths | loads the registers a0 to a3 to the values expected by the OS, and
|
368 | 5856de80 | ths | then jump at the kernel address.
|
369 | 5856de80 | ths | |
370 | 5856de80 | ths | The bootloader should pass the locations of the kernel arguments and
|
371 | 5856de80 | ths | environment variables tables. Those tables contain the 32-bit address
|
372 | 5856de80 | ths | of NULL terminated strings. The environment variables table should be
|
373 | 5856de80 | ths | terminated by a NULL address.
|
374 | 5856de80 | ths | |
375 | 5856de80 | ths | For a simpler implementation, the number of kernel arguments is fixed
|
376 | 5856de80 | ths | to two (the name of the kernel and the command line), and the two
|
377 | 5856de80 | ths | tables are actually the same one.
|
378 | 5856de80 | ths | |
379 | 5856de80 | ths | The registers a0 to a3 should contain the following values:
|
380 | 5856de80 | ths | a0 - number of kernel arguments
|
381 | 5856de80 | ths | a1 - 32-bit address of the kernel arguments table
|
382 | 5856de80 | ths | a2 - 32-bit address of the environment variables table
|
383 | 5856de80 | ths | a3 - RAM size in bytes
|
384 | 5856de80 | ths | */
|
385 | 5856de80 | ths | |
386 | 5856de80 | ths | static void write_bootloader (CPUState *env, unsigned long bios_offset, int64_t kernel_addr) |
387 | 5856de80 | ths | { |
388 | 5856de80 | ths | uint32_t *p; |
389 | 5856de80 | ths | |
390 | 5856de80 | ths | /* Small bootloader */
|
391 | 5856de80 | ths | p = (uint32_t *) (phys_ram_base + bios_offset); |
392 | 3ddd0065 | ths | stl_raw(p++, 0x0bf00010); /* j 0x1fc00040 */ |
393 | 3ddd0065 | ths | stl_raw(p++, 0x00000000); /* nop */ |
394 | 5856de80 | ths | |
395 | 5856de80 | ths | /* Second part of the bootloader */
|
396 | 5856de80 | ths | p = (uint32_t *) (phys_ram_base + bios_offset + 0x040);
|
397 | 3ddd0065 | ths | stl_raw(p++, 0x3c040000); /* lui a0, 0 */ |
398 | 3ddd0065 | ths | stl_raw(p++, 0x34840002); /* ori a0, a0, 2 */ |
399 | 3ddd0065 | ths | stl_raw(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */ |
400 | 3ddd0065 | ths | stl_raw(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a0, low(ENVP_ADDR) */ |
401 | 3ddd0065 | ths | stl_raw(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */ |
402 | 3ddd0065 | ths | stl_raw(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */ |
403 | 3ddd0065 | ths | stl_raw(p++, 0x3c070000 | (env->ram_size >> 16)); /* lui a3, high(env->ram_size) */ |
404 | 3ddd0065 | ths | stl_raw(p++, 0x34e70000 | (env->ram_size & 0xffff)); /* ori a3, a3, low(env->ram_size) */ |
405 | 3ddd0065 | ths | stl_raw(p++, 0x3c1f0000 | ((kernel_addr >> 16) & 0xffff)); /* lui ra, high(kernel_addr) */; |
406 | 3ddd0065 | ths | stl_raw(p++, 0x37ff0000 | (kernel_addr & 0xffff)); /* ori ra, ra, low(kernel_addr) */ |
407 | 3ddd0065 | ths | stl_raw(p++, 0x03e00008); /* jr ra */ |
408 | 3ddd0065 | ths | stl_raw(p++, 0x00000000); /* nop */ |
409 | 5856de80 | ths | } |
410 | 5856de80 | ths | |
411 | 5856de80 | ths | static void prom_set(int index, const char *string, ...) |
412 | 5856de80 | ths | { |
413 | 5856de80 | ths | va_list ap; |
414 | 3ddd0065 | ths | int32_t *p; |
415 | 3ddd0065 | ths | int32_t table_addr; |
416 | 5856de80 | ths | char *s;
|
417 | 5856de80 | ths | |
418 | 5856de80 | ths | if (index >= ENVP_NB_ENTRIES)
|
419 | 5856de80 | ths | return;
|
420 | 5856de80 | ths | |
421 | 3ddd0065 | ths | p = (int32_t *) (phys_ram_base + ENVP_ADDR + VIRT_TO_PHYS_ADDEND); |
422 | 5856de80 | ths | p += index; |
423 | 5856de80 | ths | |
424 | 5856de80 | ths | if (string == NULL) { |
425 | 5856de80 | ths | stl_raw(p, 0);
|
426 | 5856de80 | ths | return;
|
427 | 5856de80 | ths | } |
428 | 5856de80 | ths | |
429 | 3ddd0065 | ths | table_addr = ENVP_ADDR + sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
|
430 | 5856de80 | ths | s = (char *) (phys_ram_base + VIRT_TO_PHYS_ADDEND + table_addr);
|
431 | 5856de80 | ths | |
432 | 5856de80 | ths | stl_raw(p, table_addr); |
433 | 5856de80 | ths | |
434 | 5856de80 | ths | va_start(ap, string); |
435 | 5856de80 | ths | vsnprintf (s, ENVP_ENTRY_SIZE, string, ap); |
436 | 5856de80 | ths | va_end(ap); |
437 | 5856de80 | ths | } |
438 | 5856de80 | ths | |
439 | 5856de80 | ths | /* Kernel */
|
440 | 5856de80 | ths | static int64_t load_kernel (CPUState *env)
|
441 | 5856de80 | ths | { |
442 | 5856de80 | ths | int64_t kernel_addr = 0;
|
443 | 5856de80 | ths | int index = 0; |
444 | 5856de80 | ths | long initrd_size;
|
445 | 5856de80 | ths | |
446 | 5856de80 | ths | if (load_elf(env->kernel_filename, VIRT_TO_PHYS_ADDEND, &kernel_addr) < 0) { |
447 | 5856de80 | ths | fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
448 | 5856de80 | ths | env->kernel_filename); |
449 | 5856de80 | ths | exit(1);
|
450 | 5856de80 | ths | } |
451 | 5856de80 | ths | |
452 | 5856de80 | ths | /* load initrd */
|
453 | 5856de80 | ths | initrd_size = 0;
|
454 | 5856de80 | ths | if (env->initrd_filename) {
|
455 | 5856de80 | ths | initrd_size = load_image(env->initrd_filename, |
456 | 5856de80 | ths | phys_ram_base + INITRD_LOAD_ADDR + VIRT_TO_PHYS_ADDEND); |
457 | 5856de80 | ths | if (initrd_size == (target_ulong) -1) { |
458 | 5856de80 | ths | fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
|
459 | 5856de80 | ths | env->initrd_filename); |
460 | 5856de80 | ths | exit(1);
|
461 | 5856de80 | ths | } |
462 | 5856de80 | ths | } |
463 | 5856de80 | ths | |
464 | 5856de80 | ths | /* Store command line. */
|
465 | 5856de80 | ths | prom_set(index++, env->kernel_filename); |
466 | 5856de80 | ths | if (initrd_size > 0) |
467 | 44cbbf18 | ths | prom_set(index++, "rd_start=0x" TLSZ " rd_size=%li %s", INITRD_LOAD_ADDR, initrd_size, env->kernel_cmdline); |
468 | 5856de80 | ths | else
|
469 | 5856de80 | ths | prom_set(index++, env->kernel_cmdline); |
470 | 5856de80 | ths | |
471 | 5856de80 | ths | /* Setup minimum environment variables */
|
472 | 5856de80 | ths | prom_set(index++, "memsize");
|
473 | 5856de80 | ths | prom_set(index++, "%i", env->ram_size);
|
474 | 5856de80 | ths | prom_set(index++, "modetty0");
|
475 | 5856de80 | ths | prom_set(index++, "38400n8r");
|
476 | 5856de80 | ths | prom_set(index++, NULL);
|
477 | 5856de80 | ths | |
478 | 5856de80 | ths | return kernel_addr;
|
479 | 5856de80 | ths | } |
480 | 5856de80 | ths | |
481 | 5856de80 | ths | static void main_cpu_reset(void *opaque) |
482 | 5856de80 | ths | { |
483 | 5856de80 | ths | CPUState *env = opaque; |
484 | 5856de80 | ths | cpu_reset(env); |
485 | 5856de80 | ths | |
486 | 5856de80 | ths | /* The bootload does not need to be rewritten as it is located in a
|
487 | 5856de80 | ths | read only location. The kernel location and the arguments table
|
488 | 5856de80 | ths | location does not change. */
|
489 | 5856de80 | ths | if (env->kernel_filename)
|
490 | 5856de80 | ths | load_kernel (env); |
491 | 5856de80 | ths | } |
492 | 5856de80 | ths | |
493 | 5856de80 | ths | void mips_malta_init (int ram_size, int vga_ram_size, int boot_device, |
494 | 5856de80 | ths | DisplayState *ds, const char **fd_filename, int snapshot, |
495 | 5856de80 | ths | const char *kernel_filename, const char *kernel_cmdline, |
496 | 5856de80 | ths | const char *initrd_filename) |
497 | 5856de80 | ths | { |
498 | 5856de80 | ths | char buf[1024]; |
499 | 5856de80 | ths | unsigned long bios_offset; |
500 | 5856de80 | ths | int64_t kernel_addr; |
501 | 5856de80 | ths | PCIBus *pci_bus; |
502 | 5856de80 | ths | CPUState *env; |
503 | 5856de80 | ths | RTCState *rtc_state; |
504 | f1770b3e | ths | /* fdctrl_t *floppy_controller; */
|
505 | 5856de80 | ths | MaltaFPGAState *malta_fpga; |
506 | 5856de80 | ths | int ret;
|
507 | 5856de80 | ths | |
508 | 5856de80 | ths | env = cpu_init(); |
509 | 5856de80 | ths | register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); |
510 | 5856de80 | ths | qemu_register_reset(main_cpu_reset, env); |
511 | 5856de80 | ths | |
512 | 5856de80 | ths | /* allocate RAM */
|
513 | 5856de80 | ths | cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
|
514 | 5856de80 | ths | |
515 | 5856de80 | ths | /* Map the bios at two physical locations, as on the real board */
|
516 | 5856de80 | ths | bios_offset = ram_size + vga_ram_size; |
517 | 5856de80 | ths | cpu_register_physical_memory(0x1e000000LL,
|
518 | 5856de80 | ths | BIOS_SIZE, bios_offset | IO_MEM_ROM); |
519 | 5856de80 | ths | cpu_register_physical_memory(0x1fc00000LL,
|
520 | 5856de80 | ths | BIOS_SIZE, bios_offset | IO_MEM_ROM); |
521 | 5856de80 | ths | |
522 | 5856de80 | ths | /* Load a BIOS image except if a kernel image has been specified. In
|
523 | 5856de80 | ths | the later case, just write a small bootloader to the flash
|
524 | 5856de80 | ths | location. */
|
525 | 5856de80 | ths | if (kernel_filename) {
|
526 | 5856de80 | ths | env->ram_size = ram_size; |
527 | 5856de80 | ths | env->kernel_filename = kernel_filename; |
528 | 5856de80 | ths | env->kernel_cmdline = kernel_cmdline; |
529 | 5856de80 | ths | env->initrd_filename = initrd_filename; |
530 | 5856de80 | ths | kernel_addr = load_kernel(env); |
531 | 5856de80 | ths | write_bootloader(env, bios_offset, kernel_addr); |
532 | 5856de80 | ths | } else {
|
533 | 5856de80 | ths | snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); |
534 | 5856de80 | ths | ret = load_image(buf, phys_ram_base + bios_offset); |
535 | 5856de80 | ths | if (ret != BIOS_SIZE) {
|
536 | 5856de80 | ths | fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
|
537 | 5856de80 | ths | buf); |
538 | 5856de80 | ths | exit(1);
|
539 | 5856de80 | ths | } |
540 | 5856de80 | ths | } |
541 | 5856de80 | ths | |
542 | 5856de80 | ths | /* Board ID = 0x420 (Malta Board with CoreLV)
|
543 | 5856de80 | ths | XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should
|
544 | 5856de80 | ths | map to the board ID. */
|
545 | 5856de80 | ths | stl_raw(phys_ram_base + bios_offset + 0x10, 0x00000420); |
546 | 5856de80 | ths | |
547 | 5856de80 | ths | /* Init internal devices */
|
548 | 5856de80 | ths | cpu_mips_clock_init(env); |
549 | 5856de80 | ths | cpu_mips_irqctrl_init(); |
550 | 5856de80 | ths | |
551 | 5856de80 | ths | /* FPGA */
|
552 | 5856de80 | ths | malta_fpga = malta_fpga_init(0x1f000000LL);
|
553 | 5856de80 | ths | |
554 | 5856de80 | ths | /* Interrupt controller */
|
555 | 5856de80 | ths | isa_pic = pic_init(pic_irq_request, env); |
556 | 5856de80 | ths | |
557 | 5856de80 | ths | /* Northbridge */
|
558 | 5856de80 | ths | pci_bus = pci_gt64120_init(isa_pic); |
559 | 5856de80 | ths | |
560 | 5856de80 | ths | /* Southbridge */
|
561 | 5856de80 | ths | piix4_init(pci_bus, 80);
|
562 | 5856de80 | ths | pci_piix3_ide_init(pci_bus, bs_table, 81);
|
563 | 5856de80 | ths | usb_uhci_init(pci_bus, 82);
|
564 | 5856de80 | ths | piix4_pm_init(pci_bus, 83);
|
565 | 5856de80 | ths | pit = pit_init(0x40, 0); |
566 | 5856de80 | ths | DMA_init(0);
|
567 | 5856de80 | ths | |
568 | 5856de80 | ths | /* Super I/O */
|
569 | 5856de80 | ths | kbd_init(); |
570 | 5856de80 | ths | rtc_state = rtc_init(0x70, 8); |
571 | 5856de80 | ths | serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]); |
572 | 5856de80 | ths | parallel_init(0x378, 7, parallel_hds[0]); |
573 | 5856de80 | ths | /* XXX: The floppy controller does not work correctly, something is
|
574 | f1770b3e | ths | probably wrong.
|
575 | f1770b3e | ths | floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); */
|
576 | 5856de80 | ths | |
577 | 5856de80 | ths | /* Sound card */
|
578 | 5856de80 | ths | #ifdef HAS_AUDIO
|
579 | 5856de80 | ths | audio_init(pci_bus); |
580 | 5856de80 | ths | #endif
|
581 | 5856de80 | ths | |
582 | 5856de80 | ths | /* Network card */
|
583 | 5856de80 | ths | network_init(pci_bus); |
584 | 5856de80 | ths | } |
585 | 5856de80 | ths | |
586 | 5856de80 | ths | QEMUMachine mips_malta_machine = { |
587 | 5856de80 | ths | "malta",
|
588 | 5856de80 | ths | "MIPS Malta Core LV",
|
589 | 5856de80 | ths | mips_malta_init, |
590 | 5856de80 | ths | }; |