xref: /qemu/hw/mips/mipssim.c (revision e16ad5b0442931e6b76be246cf9dd2866ce352c8)
1 /*
2  * QEMU/mipssim emulation
3  *
4  * Emulates a very simple machine model similiar to the one use by the
5  * proprietary MIPS emulator.
6  *
7  * Copyright (c) 2007 Thiemo Seufer
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27 #include "hw.h"
28 #include "mips.h"
29 #include "pc.h"
30 #include "isa.h"
31 #include "net.h"
32 #include "sysemu.h"
33 #include "boards.h"
34 #include "mips-bios.h"
35 #include "loader.h"
36 #include "elf.h"
37 
38 #ifdef TARGET_MIPS64
39 #define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL)
40 #else
41 #define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU)
42 #endif
43 
44 #define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000))
45 
46 static struct _loaderparams {
47     int ram_size;
48     const char *kernel_filename;
49     const char *kernel_cmdline;
50     const char *initrd_filename;
51 } loaderparams;
52 
53 typedef struct ResetData {
54     CPUState *env;
55     uint64_t vector;
56 } ResetData;
57 
58 static int64_t load_kernel(void)
59 {
60     int64_t entry, kernel_low, kernel_high;
61     long kernel_size;
62     long initrd_size;
63     ram_addr_t initrd_offset;
64     int big_endian;
65 
66 #ifdef TARGET_WORDS_BIGENDIAN
67     big_endian = 1;
68 #else
69     big_endian = 0;
70 #endif
71 
72     kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND,
73                            (uint64_t *)&entry, (uint64_t *)&kernel_low,
74                            (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1);
75     if (kernel_size >= 0) {
76         if ((entry & ~0x7fffffffULL) == 0x80000000)
77             entry = (int32_t)entry;
78     } else {
79         fprintf(stderr, "qemu: could not load kernel '%s'\n",
80                 loaderparams.kernel_filename);
81         exit(1);
82     }
83 
84     /* load initrd */
85     initrd_size = 0;
86     initrd_offset = 0;
87     if (loaderparams.initrd_filename) {
88         initrd_size = get_image_size (loaderparams.initrd_filename);
89         if (initrd_size > 0) {
90             initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK;
91             if (initrd_offset + initrd_size > loaderparams.ram_size) {
92                 fprintf(stderr,
93                         "qemu: memory too small for initial ram disk '%s'\n",
94                         loaderparams.initrd_filename);
95                 exit(1);
96             }
97             initrd_size = load_image_targphys(loaderparams.initrd_filename,
98                 initrd_offset, loaderparams.ram_size - initrd_offset);
99         }
100         if (initrd_size == (target_ulong) -1) {
101             fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
102                     loaderparams.initrd_filename);
103             exit(1);
104         }
105     }
106     return entry;
107 }
108 
109 static void main_cpu_reset(void *opaque)
110 {
111     ResetData *s = (ResetData *)opaque;
112     CPUState *env = s->env;
113 
114     cpu_reset(env);
115     env->active_tc.PC = s->vector;
116 }
117 
118 static void
119 mips_mipssim_init (ram_addr_t ram_size,
120                    const char *boot_device,
121                    const char *kernel_filename, const char *kernel_cmdline,
122                    const char *initrd_filename, const char *cpu_model)
123 {
124     char *filename;
125     ram_addr_t ram_offset;
126     ram_addr_t bios_offset;
127     CPUState *env;
128     ResetData *reset_info;
129     int bios_size;
130 
131     /* Init CPUs. */
132     if (cpu_model == NULL) {
133 #ifdef TARGET_MIPS64
134         cpu_model = "5Kf";
135 #else
136         cpu_model = "24Kf";
137 #endif
138     }
139     env = cpu_init(cpu_model);
140     if (!env) {
141         fprintf(stderr, "Unable to find CPU definition\n");
142         exit(1);
143     }
144     reset_info = qemu_mallocz(sizeof(ResetData));
145     reset_info->env = env;
146     reset_info->vector = env->active_tc.PC;
147     qemu_register_reset(main_cpu_reset, reset_info);
148 
149     /* Allocate RAM. */
150     ram_offset = qemu_ram_alloc(ram_size);
151     bios_offset = qemu_ram_alloc(BIOS_SIZE);
152 
153     cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM);
154 
155     /* Map the BIOS / boot exception handler. */
156     cpu_register_physical_memory(0x1fc00000LL,
157                                  BIOS_SIZE, bios_offset | IO_MEM_ROM);
158     /* Load a BIOS / boot exception handler image. */
159     if (bios_name == NULL)
160         bios_name = BIOS_FILENAME;
161     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
162     if (filename) {
163         bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE);
164         qemu_free(filename);
165     } else {
166         bios_size = -1;
167     }
168     if ((bios_size < 0 || bios_size > BIOS_SIZE) && !kernel_filename) {
169         /* Bail out if we have neither a kernel image nor boot vector code. */
170         fprintf(stderr,
171                 "qemu: Could not load MIPS bios '%s', and no -kernel argument was specified\n",
172                 filename);
173         exit(1);
174     } else {
175         /* We have a boot vector start address. */
176         env->active_tc.PC = (target_long)(int32_t)0xbfc00000;
177     }
178 
179     if (kernel_filename) {
180         loaderparams.ram_size = ram_size;
181         loaderparams.kernel_filename = kernel_filename;
182         loaderparams.kernel_cmdline = kernel_cmdline;
183         loaderparams.initrd_filename = initrd_filename;
184         reset_info->vector = load_kernel();
185     }
186 
187     /* Init CPU internal devices. */
188     cpu_mips_irq_init_cpu(env);
189     cpu_mips_clock_init(env);
190 
191     /* Register 64 KB of ISA IO space at 0x1fd00000. */
192     isa_mmio_init(0x1fd00000, 0x00010000);
193 
194     /* A single 16450 sits at offset 0x3f8. It is attached to
195        MIPS CPU INT2, which is interrupt 4. */
196     if (serial_hds[0])
197         serial_init(0x3f8, env->irq[4], 115200, serial_hds[0]);
198 
199     if (nd_table[0].vlan)
200         /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */
201         mipsnet_init(0x4200, env->irq[2], &nd_table[0]);
202 }
203 
204 static QEMUMachine mips_mipssim_machine = {
205     .name = "mipssim",
206     .desc = "MIPS MIPSsim platform",
207     .init = mips_mipssim_init,
208 };
209 
210 static void mips_mipssim_machine_init(void)
211 {
212     qemu_register_machine(&mips_mipssim_machine);
213 }
214 
215 machine_init(mips_mipssim_machine_init);
216