]>
Commit | Line | Data |
---|---|---|
c76b409f HC |
1 | /* |
2 | * Generic Loongson-3 Platform support | |
3 | * | |
4 | * Copyright (c) 2018-2020 Huacai Chen (chenhc@lemote.com) | |
5 | * Copyright (c) 2018-2020 Jiaxun Yang <jiaxun.yang@flygoat.com> | |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | /* | |
22 | * Generic virtualized PC Platform based on Loongson-3 CPU (MIPS64R2 with | |
23 | * extensions, 800~2000MHz) | |
24 | */ | |
25 | ||
26 | #include "qemu/osdep.h" | |
27 | #include "qemu-common.h" | |
28 | #include "qemu/units.h" | |
29 | #include "qemu/cutils.h" | |
30 | #include "qemu/datadir.h" | |
31 | #include "qapi/error.h" | |
32 | #include "cpu.h" | |
33 | #include "elf.h" | |
34 | #include "kvm_mips.h" | |
35 | #include "hw/boards.h" | |
36 | #include "hw/char/serial.h" | |
37 | #include "hw/intc/loongson_liointc.h" | |
38 | #include "hw/mips/mips.h" | |
39 | #include "hw/mips/cpudevs.h" | |
40 | #include "hw/mips/fw_cfg.h" | |
41 | #include "hw/mips/loongson3_bootp.h" | |
42 | #include "hw/misc/unimp.h" | |
43 | #include "hw/intc/i8259.h" | |
44 | #include "hw/loader.h" | |
45 | #include "hw/isa/superio.h" | |
46 | #include "hw/pci/msi.h" | |
47 | #include "hw/pci/pci.h" | |
48 | #include "hw/pci/pci_host.h" | |
49 | #include "hw/pci-host/gpex.h" | |
50 | #include "hw/usb.h" | |
51 | #include "net/net.h" | |
52 | #include "exec/address-spaces.h" | |
53 | #include "sysemu/kvm.h" | |
54 | #include "sysemu/qtest.h" | |
55 | #include "sysemu/reset.h" | |
56 | #include "sysemu/runstate.h" | |
57 | #include "qemu/log.h" | |
58 | #include "qemu/error-report.h" | |
59 | ||
60 | #define PM_CNTL_MODE 0x10 | |
61 | ||
62 | #define LOONGSON_MAX_VCPUS 16 | |
63 | ||
64 | /* | |
65 | * Loongson-3's virtual machine BIOS can be obtained here: | |
66 | * 1, https://github.com/loongson-community/firmware-nonfree | |
67 | * 2, http://dev.lemote.com:8000/files/firmware/UEFI/KVM/bios_loongson3.bin | |
68 | */ | |
69 | #define LOONGSON3_BIOSNAME "bios_loongson3.bin" | |
70 | ||
71 | #define UART_IRQ 0 | |
72 | #define RTC_IRQ 1 | |
73 | #define PCIE_IRQ_BASE 2 | |
74 | ||
75 | const struct MemmapEntry virt_memmap[] = { | |
76 | [VIRT_LOWMEM] = { 0x00000000, 0x10000000 }, | |
77 | [VIRT_PM] = { 0x10080000, 0x100 }, | |
78 | [VIRT_FW_CFG] = { 0x10080100, 0x100 }, | |
79 | [VIRT_RTC] = { 0x10081000, 0x1000 }, | |
80 | [VIRT_PCIE_PIO] = { 0x18000000, 0x80000 }, | |
81 | [VIRT_PCIE_ECAM] = { 0x1a000000, 0x2000000 }, | |
82 | [VIRT_BIOS_ROM] = { 0x1fc00000, 0x200000 }, | |
83 | [VIRT_UART] = { 0x1fe001e0, 0x8 }, | |
84 | [VIRT_LIOINTC] = { 0x3ff01400, 0x64 }, | |
85 | [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 }, | |
86 | [VIRT_HIGHMEM] = { 0x80000000, 0x0 }, /* Variable */ | |
87 | }; | |
88 | ||
89 | static const struct MemmapEntry loader_memmap[] = { | |
90 | [LOADER_KERNEL] = { 0x00000000, 0x4000000 }, | |
91 | [LOADER_INITRD] = { 0x04000000, 0x0 }, /* Variable */ | |
92 | [LOADER_CMDLINE] = { 0x0ff00000, 0x100000 }, | |
93 | }; | |
94 | ||
95 | static const struct MemmapEntry loader_rommap[] = { | |
96 | [LOADER_BOOTROM] = { 0x1fc00000, 0x1000 }, | |
97 | [LOADER_PARAM] = { 0x1fc01000, 0x10000 }, | |
98 | }; | |
99 | ||
100 | struct LoongsonMachineState { | |
101 | MachineState parent_obj; | |
102 | MemoryRegion *pio_alias; | |
103 | MemoryRegion *mmio_alias; | |
104 | MemoryRegion *ecam_alias; | |
105 | }; | |
106 | typedef struct LoongsonMachineState LoongsonMachineState; | |
107 | ||
108 | #define TYPE_LOONGSON_MACHINE MACHINE_TYPE_NAME("loongson3-virt") | |
109 | DECLARE_INSTANCE_CHECKER(LoongsonMachineState, LOONGSON_MACHINE, TYPE_LOONGSON_MACHINE) | |
110 | ||
111 | static struct _loaderparams { | |
112 | uint64_t cpu_freq; | |
113 | uint64_t ram_size; | |
114 | const char *kernel_cmdline; | |
115 | const char *kernel_filename; | |
116 | const char *initrd_filename; | |
117 | uint64_t kernel_entry; | |
118 | uint64_t a0, a1, a2; | |
119 | } loaderparams; | |
120 | ||
121 | static uint64_t loongson3_pm_read(void *opaque, hwaddr addr, unsigned size) | |
122 | { | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static void loongson3_pm_write(void *opaque, hwaddr addr, | |
127 | uint64_t val, unsigned size) | |
128 | { | |
129 | if (addr != PM_CNTL_MODE) { | |
130 | return; | |
131 | } | |
132 | ||
133 | switch (val) { | |
134 | case 0x00: | |
135 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); | |
136 | return; | |
137 | case 0xff: | |
138 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); | |
139 | return; | |
140 | default: | |
141 | return; | |
142 | } | |
143 | } | |
144 | ||
145 | static const MemoryRegionOps loongson3_pm_ops = { | |
146 | .read = loongson3_pm_read, | |
147 | .write = loongson3_pm_write, | |
148 | .endianness = DEVICE_NATIVE_ENDIAN, | |
149 | .valid = { | |
150 | .min_access_size = 1, | |
151 | .max_access_size = 1 | |
152 | } | |
153 | }; | |
154 | ||
155 | #define DEF_LOONGSON3_FREQ (800 * 1000 * 1000) | |
156 | ||
157 | static uint64_t get_cpu_freq_hz(void) | |
158 | { | |
159 | #ifdef CONFIG_KVM | |
160 | int ret; | |
161 | uint64_t freq; | |
162 | struct kvm_one_reg freq_reg = { | |
163 | .id = KVM_REG_MIPS_COUNT_HZ, | |
164 | .addr = (uintptr_t)(&freq) | |
165 | }; | |
166 | ||
167 | if (kvm_enabled()) { | |
168 | ret = kvm_vcpu_ioctl(first_cpu, KVM_GET_ONE_REG, &freq_reg); | |
169 | if (ret >= 0) { | |
170 | return freq * 2; | |
171 | } | |
172 | } | |
173 | #endif | |
174 | return DEF_LOONGSON3_FREQ; | |
175 | } | |
176 | ||
177 | static void init_boot_param(void) | |
178 | { | |
179 | static void *p; | |
180 | struct boot_params *bp; | |
181 | ||
182 | p = g_malloc0(loader_rommap[LOADER_PARAM].size); | |
183 | bp = p; | |
184 | ||
185 | bp->efi.smbios.vers = cpu_to_le16(1); | |
186 | init_reset_system(&(bp->reset_system)); | |
187 | p += ROUND_UP(sizeof(struct boot_params), 64); | |
188 | init_loongson_params(&(bp->efi.smbios.lp), p, | |
189 | loaderparams.cpu_freq, loaderparams.ram_size); | |
190 | ||
191 | rom_add_blob_fixed("params_rom", bp, | |
192 | loader_rommap[LOADER_PARAM].size, | |
193 | loader_rommap[LOADER_PARAM].base); | |
194 | ||
195 | g_free(bp); | |
196 | ||
197 | loaderparams.a2 = cpu_mips_phys_to_kseg0(NULL, | |
198 | loader_rommap[LOADER_PARAM].base); | |
199 | } | |
200 | ||
201 | static void init_boot_rom(void) | |
202 | { | |
203 | const unsigned int boot_code[] = { | |
204 | 0x40086000, /* mfc0 t0, CP0_STATUS */ | |
205 | 0x240900E4, /* li t1, 0xe4 #set kx, sx, ux, erl */ | |
206 | 0x01094025, /* or t0, t0, t1 */ | |
207 | 0x3C090040, /* lui t1, 0x40 #set bev */ | |
208 | 0x01094025, /* or t0, t0, t1 */ | |
209 | 0x40886000, /* mtc0 t0, CP0_STATUS */ | |
210 | 0x00000000, | |
211 | 0x40806800, /* mtc0 zero, CP0_CAUSE */ | |
212 | 0x00000000, | |
213 | 0x400A7801, /* mfc0 t2, $15, 1 */ | |
214 | 0x314A00FF, /* andi t2, 0x0ff */ | |
215 | 0x3C089000, /* dli t0, 0x900000003ff01000 */ | |
216 | 0x00084438, | |
217 | 0x35083FF0, | |
218 | 0x00084438, | |
219 | 0x35081000, | |
220 | 0x314B0003, /* andi t3, t2, 0x3 #local cpuid */ | |
221 | 0x000B5A00, /* sll t3, 8 */ | |
222 | 0x010B4025, /* or t0, t0, t3 */ | |
223 | 0x314C000C, /* andi t4, t2, 0xc #node id */ | |
224 | 0x000C62BC, /* dsll t4, 42 */ | |
225 | 0x010C4025, /* or t0, t0, t4 */ | |
226 | /* WaitForInit: */ | |
227 | 0xDD020020, /* ld v0, FN_OFF(t0) #FN_OFF 0x020 */ | |
228 | 0x1040FFFE, /* beqz v0, WaitForInit */ | |
229 | 0x00000000, /* nop */ | |
230 | 0xDD1D0028, /* ld sp, SP_OFF(t0) #FN_OFF 0x028 */ | |
231 | 0xDD1C0030, /* ld gp, GP_OFF(t0) #FN_OFF 0x030 */ | |
232 | 0xDD050038, /* ld a1, A1_OFF(t0) #FN_OFF 0x038 */ | |
233 | 0x00400008, /* jr v0 #byebye */ | |
234 | 0x00000000, /* nop */ | |
235 | 0x1000FFFF, /* 1: b 1b */ | |
236 | 0x00000000, /* nop */ | |
237 | ||
238 | /* Reset */ | |
239 | 0x3C0C9000, /* dli t0, 0x9000000010080010 */ | |
240 | 0x358C0000, | |
241 | 0x000C6438, | |
242 | 0x358C1008, | |
243 | 0x000C6438, | |
244 | 0x358C0010, | |
245 | 0x240D0000, /* li t1, 0x00 */ | |
246 | 0xA18D0000, /* sb t1, (t0) */ | |
247 | 0x1000FFFF, /* 1: b 1b */ | |
248 | 0x00000000, /* nop */ | |
249 | ||
250 | /* Shutdown */ | |
251 | 0x3C0C9000, /* dli t0, 0x9000000010080010 */ | |
252 | 0x358C0000, | |
253 | 0x000C6438, | |
254 | 0x358C1008, | |
255 | 0x000C6438, | |
256 | 0x358C0010, | |
257 | 0x240D00FF, /* li t1, 0xff */ | |
258 | 0xA18D0000, /* sb t1, (t0) */ | |
259 | 0x1000FFFF, /* 1: b 1b */ | |
260 | 0x00000000 /* nop */ | |
261 | }; | |
262 | ||
263 | rom_add_blob_fixed("boot_rom", boot_code, sizeof(boot_code), | |
264 | loader_rommap[LOADER_BOOTROM].base); | |
265 | } | |
266 | ||
267 | static void fw_cfg_boot_set(void *opaque, const char *boot_device, | |
268 | Error **errp) | |
269 | { | |
270 | fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); | |
271 | } | |
272 | ||
273 | static void fw_conf_init(unsigned long ram_size) | |
274 | { | |
275 | FWCfgState *fw_cfg; | |
276 | hwaddr cfg_addr = virt_memmap[VIRT_FW_CFG].base; | |
277 | ||
278 | fw_cfg = fw_cfg_init_mem_wide(cfg_addr, cfg_addr + 8, 8, 0, NULL); | |
279 | fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)current_machine->smp.cpus); | |
280 | fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)current_machine->smp.max_cpus); | |
281 | fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); | |
282 | fw_cfg_add_i32(fw_cfg, FW_CFG_MACHINE_VERSION, 1); | |
283 | fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, get_cpu_freq_hz()); | |
284 | qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); | |
285 | } | |
286 | ||
287 | static int set_prom_cmdline(ram_addr_t initrd_offset, long initrd_size) | |
288 | { | |
289 | int ret = 0; | |
290 | void *cmdline_buf; | |
291 | hwaddr cmdline_vaddr; | |
292 | unsigned int *parg_env; | |
293 | ||
294 | /* Allocate cmdline_buf for command line. */ | |
295 | cmdline_buf = g_malloc0(loader_memmap[LOADER_CMDLINE].size); | |
296 | cmdline_vaddr = cpu_mips_phys_to_kseg0(NULL, | |
297 | loader_memmap[LOADER_CMDLINE].base); | |
298 | ||
299 | /* | |
300 | * Layout of cmdline_buf looks like this: | |
301 | * argv[0], argv[1], 0, env[0], env[1], ... env[i], 0, | |
302 | * argv[0]'s data, argv[1]'s data, env[0]'data, ..., env[i]'s data, 0 | |
303 | */ | |
304 | parg_env = (void *)cmdline_buf; | |
305 | ||
306 | ret = (3 + 1) * 4; | |
307 | *parg_env++ = cmdline_vaddr + ret; | |
308 | ret += (1 + snprintf(cmdline_buf + ret, 256 - ret, "g")); | |
309 | ||
310 | /* argv1 */ | |
311 | *parg_env++ = cmdline_vaddr + ret; | |
312 | if (initrd_size > 0) | |
313 | ret += (1 + snprintf(cmdline_buf + ret, 256 - ret, | |
314 | "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s", | |
315 | cpu_mips_phys_to_kseg0(NULL, initrd_offset), | |
316 | initrd_size, loaderparams.kernel_cmdline)); | |
317 | else | |
318 | ret += (1 + snprintf(cmdline_buf + ret, 256 - ret, "%s", | |
319 | loaderparams.kernel_cmdline)); | |
320 | ||
321 | /* argv2 */ | |
322 | *parg_env++ = cmdline_vaddr + 4 * ret; | |
323 | ||
324 | rom_add_blob_fixed("cmdline", cmdline_buf, | |
325 | loader_memmap[LOADER_CMDLINE].size, | |
326 | loader_memmap[LOADER_CMDLINE].base); | |
327 | ||
328 | g_free(cmdline_buf); | |
329 | ||
330 | loaderparams.a0 = 2; | |
331 | loaderparams.a1 = cmdline_vaddr; | |
332 | ||
333 | return 0; | |
334 | } | |
335 | ||
336 | static uint64_t load_kernel(CPUMIPSState *env) | |
337 | { | |
338 | long kernel_size; | |
339 | ram_addr_t initrd_offset; | |
340 | uint64_t kernel_entry, kernel_low, kernel_high, initrd_size; | |
341 | ||
342 | kernel_size = load_elf(loaderparams.kernel_filename, NULL, | |
343 | cpu_mips_kseg0_to_phys, NULL, | |
344 | (uint64_t *)&kernel_entry, | |
345 | (uint64_t *)&kernel_low, (uint64_t *)&kernel_high, | |
346 | NULL, 0, EM_MIPS, 1, 0); | |
347 | if (kernel_size < 0) { | |
348 | error_report("could not load kernel '%s': %s", | |
349 | loaderparams.kernel_filename, | |
350 | load_elf_strerror(kernel_size)); | |
351 | exit(1); | |
352 | } | |
353 | ||
354 | /* load initrd */ | |
355 | initrd_size = 0; | |
356 | initrd_offset = 0; | |
357 | if (loaderparams.initrd_filename) { | |
358 | initrd_size = get_image_size(loaderparams.initrd_filename); | |
359 | if (initrd_size > 0) { | |
360 | initrd_offset = MAX(loader_memmap[LOADER_INITRD].base, | |
361 | ROUND_UP(kernel_high, INITRD_PAGE_SIZE)); | |
362 | ||
363 | if (initrd_offset + initrd_size > loaderparams.ram_size) { | |
364 | error_report("memory too small for initial ram disk '%s'", | |
365 | loaderparams.initrd_filename); | |
366 | exit(1); | |
367 | } | |
368 | ||
369 | initrd_size = load_image_targphys(loaderparams.initrd_filename, | |
370 | initrd_offset, | |
371 | loaderparams.ram_size - initrd_offset); | |
372 | } | |
373 | ||
374 | if (initrd_size == (target_ulong) -1) { | |
375 | error_report("could not load initial ram disk '%s'", | |
376 | loaderparams.initrd_filename); | |
377 | exit(1); | |
378 | } | |
379 | } | |
380 | ||
381 | /* Setup prom cmdline. */ | |
382 | set_prom_cmdline(initrd_offset, initrd_size); | |
383 | ||
384 | return kernel_entry; | |
385 | } | |
386 | ||
387 | static void main_cpu_reset(void *opaque) | |
388 | { | |
389 | MIPSCPU *cpu = opaque; | |
390 | CPUMIPSState *env = &cpu->env; | |
391 | ||
392 | cpu_reset(CPU(cpu)); | |
393 | ||
394 | /* Loongson-3 reset stuff */ | |
395 | if (loaderparams.kernel_filename) { | |
396 | if (cpu == MIPS_CPU(first_cpu)) { | |
397 | env->active_tc.gpr[4] = loaderparams.a0; | |
398 | env->active_tc.gpr[5] = loaderparams.a1; | |
399 | env->active_tc.gpr[6] = loaderparams.a2; | |
400 | env->active_tc.PC = loaderparams.kernel_entry; | |
401 | } | |
402 | env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); | |
403 | } | |
404 | } | |
405 | ||
406 | static inline void loongson3_virt_devices_init(MachineState *machine, | |
407 | DeviceState *pic) | |
408 | { | |
409 | int i; | |
410 | qemu_irq irq; | |
411 | PCIBus *pci_bus; | |
412 | DeviceState *dev; | |
413 | MemoryRegion *mmio_reg, *ecam_reg; | |
414 | LoongsonMachineState *s = LOONGSON_MACHINE(machine); | |
415 | ||
416 | dev = qdev_new(TYPE_GPEX_HOST); | |
417 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); | |
418 | pci_bus = PCI_HOST_BRIDGE(dev)->bus; | |
419 | ||
420 | s->ecam_alias = g_new0(MemoryRegion, 1); | |
421 | ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); | |
422 | memory_region_init_alias(s->ecam_alias, OBJECT(dev), "pcie-ecam", | |
423 | ecam_reg, 0, virt_memmap[VIRT_PCIE_ECAM].size); | |
424 | memory_region_add_subregion(get_system_memory(), | |
425 | virt_memmap[VIRT_PCIE_ECAM].base, | |
426 | s->ecam_alias); | |
427 | ||
428 | s->mmio_alias = g_new0(MemoryRegion, 1); | |
429 | mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); | |
430 | memory_region_init_alias(s->mmio_alias, OBJECT(dev), "pcie-mmio", | |
431 | mmio_reg, virt_memmap[VIRT_PCIE_MMIO].base, | |
432 | virt_memmap[VIRT_PCIE_MMIO].size); | |
433 | memory_region_add_subregion(get_system_memory(), | |
434 | virt_memmap[VIRT_PCIE_MMIO].base, | |
435 | s->mmio_alias); | |
436 | ||
437 | s->pio_alias = g_new0(MemoryRegion, 1); | |
438 | memory_region_init_alias(s->pio_alias, OBJECT(dev), "pcie-pio", | |
439 | get_system_io(), 0, | |
440 | virt_memmap[VIRT_PCIE_PIO].size); | |
441 | memory_region_add_subregion(get_system_memory(), | |
442 | virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias); | |
443 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base); | |
444 | ||
445 | for (i = 0; i < GPEX_NUM_IRQS; i++) { | |
446 | irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i); | |
447 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); | |
448 | gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i); | |
449 | } | |
450 | msi_nonbroken = true; | |
451 | ||
452 | pci_vga_init(pci_bus); | |
453 | ||
454 | if (defaults_enabled()) { | |
455 | pci_create_simple(pci_bus, -1, "pci-ohci"); | |
456 | usb_create_simple(usb_bus_find(-1), "usb-kbd"); | |
457 | usb_create_simple(usb_bus_find(-1), "usb-tablet"); | |
458 | } | |
459 | ||
460 | for (i = 0; i < nb_nics; i++) { | |
461 | NICInfo *nd = &nd_table[i]; | |
462 | ||
463 | if (!nd->model) { | |
464 | nd->model = g_strdup("virtio"); | |
465 | } | |
466 | ||
467 | pci_nic_init_nofail(nd, pci_bus, nd->model, NULL); | |
468 | } | |
469 | } | |
470 | ||
471 | static void mips_loongson3_virt_init(MachineState *machine) | |
472 | { | |
473 | int i; | |
474 | long bios_size; | |
475 | MIPSCPU *cpu; | |
476 | Clock *cpuclk; | |
477 | CPUMIPSState *env; | |
478 | DeviceState *liointc; | |
479 | char *filename; | |
480 | const char *kernel_cmdline = machine->kernel_cmdline; | |
481 | const char *kernel_filename = machine->kernel_filename; | |
482 | const char *initrd_filename = machine->initrd_filename; | |
483 | ram_addr_t ram_size = machine->ram_size; | |
484 | MemoryRegion *address_space_mem = get_system_memory(); | |
485 | MemoryRegion *ram = g_new(MemoryRegion, 1); | |
486 | MemoryRegion *bios = g_new(MemoryRegion, 1); | |
487 | MemoryRegion *iomem = g_new(MemoryRegion, 1); | |
488 | ||
489 | /* TODO: TCG will support all CPU types */ | |
490 | if (!kvm_enabled()) { | |
491 | if (!machine->cpu_type) { | |
492 | machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A1000"); | |
493 | } | |
494 | if (!strstr(machine->cpu_type, "Loongson-3A1000")) { | |
495 | error_report("Loongson-3/TCG needs cpu type Loongson-3A1000"); | |
496 | exit(1); | |
497 | } | |
498 | } else { | |
499 | if (!machine->cpu_type) { | |
500 | machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A4000"); | |
501 | } | |
502 | if (!strstr(machine->cpu_type, "Loongson-3A4000")) { | |
503 | error_report("Loongson-3/KVM needs cpu type Loongson-3A4000"); | |
504 | exit(1); | |
505 | } | |
506 | } | |
507 | ||
508 | if (ram_size < 512 * MiB) { | |
509 | error_report("Loongson-3 machine needs at least 512MB memory"); | |
510 | exit(1); | |
511 | } | |
512 | ||
513 | /* | |
514 | * The whole MMIO range among configure registers doesn't generate | |
515 | * exception when accessing invalid memory. Create some unimplememted | |
516 | * devices to emulate this feature. | |
517 | */ | |
518 | create_unimplemented_device("mmio fallback 0", 0x10000000, 256 * MiB); | |
519 | create_unimplemented_device("mmio fallback 1", 0x30000000, 256 * MiB); | |
520 | ||
521 | liointc = qdev_new("loongson.liointc"); | |
522 | sysbus_realize_and_unref(SYS_BUS_DEVICE(liointc), &error_fatal); | |
523 | ||
524 | sysbus_mmio_map(SYS_BUS_DEVICE(liointc), 0, virt_memmap[VIRT_LIOINTC].base); | |
525 | ||
526 | serial_mm_init(address_space_mem, virt_memmap[VIRT_UART].base, 0, | |
527 | qdev_get_gpio_in(liointc, UART_IRQ), 115200, serial_hd(0), | |
528 | DEVICE_NATIVE_ENDIAN); | |
529 | ||
530 | sysbus_create_simple("goldfish_rtc", virt_memmap[VIRT_RTC].base, | |
531 | qdev_get_gpio_in(liointc, RTC_IRQ)); | |
532 | ||
533 | cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); | |
534 | clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ); | |
535 | ||
536 | for (i = 0; i < machine->smp.cpus; i++) { | |
537 | int ip; | |
538 | ||
539 | /* init CPUs */ | |
540 | cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); | |
541 | ||
542 | /* Init internal devices */ | |
543 | cpu_mips_irq_init_cpu(cpu); | |
544 | cpu_mips_clock_init(cpu); | |
545 | qemu_register_reset(main_cpu_reset, cpu); | |
546 | ||
547 | if (i >= 4) { | |
548 | continue; /* Only node-0 can be connected to LIOINTC */ | |
549 | } | |
550 | ||
551 | for (ip = 0; ip < 4 ; ip++) { | |
552 | int pin = i * 4 + ip; | |
553 | sysbus_connect_irq(SYS_BUS_DEVICE(liointc), | |
554 | pin, cpu->env.irq[ip + 2]); | |
555 | } | |
556 | } | |
557 | env = &MIPS_CPU(first_cpu)->env; | |
558 | ||
559 | /* Allocate RAM/BIOS, 0x00000000~0x10000000 is alias of 0x80000000~0x90000000 */ | |
560 | memory_region_init_rom(bios, NULL, "loongson3.bios", | |
561 | virt_memmap[VIRT_BIOS_ROM].size, &error_fatal); | |
562 | memory_region_init_alias(ram, NULL, "loongson3.lowmem", | |
563 | machine->ram, 0, virt_memmap[VIRT_LOWMEM].size); | |
564 | memory_region_init_io(iomem, NULL, &loongson3_pm_ops, | |
565 | NULL, "loongson3_pm", virt_memmap[VIRT_PM].size); | |
566 | ||
567 | memory_region_add_subregion(address_space_mem, | |
568 | virt_memmap[VIRT_LOWMEM].base, ram); | |
569 | memory_region_add_subregion(address_space_mem, | |
570 | virt_memmap[VIRT_BIOS_ROM].base, bios); | |
571 | memory_region_add_subregion(address_space_mem, | |
572 | virt_memmap[VIRT_HIGHMEM].base, machine->ram); | |
573 | memory_region_add_subregion(address_space_mem, | |
574 | virt_memmap[VIRT_PM].base, iomem); | |
575 | ||
576 | /* | |
577 | * We do not support flash operation, just loading bios.bin as raw BIOS. | |
578 | * Please use -L to set the BIOS path and -bios to set bios name. | |
579 | */ | |
580 | ||
581 | if (kernel_filename) { | |
582 | loaderparams.cpu_freq = get_cpu_freq_hz(); | |
583 | loaderparams.ram_size = ram_size; | |
584 | loaderparams.kernel_filename = kernel_filename; | |
585 | loaderparams.kernel_cmdline = kernel_cmdline; | |
586 | loaderparams.initrd_filename = initrd_filename; | |
587 | loaderparams.kernel_entry = load_kernel(env); | |
588 | ||
589 | init_boot_rom(); | |
590 | init_boot_param(); | |
591 | } else { | |
592 | filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, | |
593 | machine->firmware ?: LOONGSON3_BIOSNAME); | |
594 | if (filename) { | |
595 | bios_size = load_image_targphys(filename, | |
596 | virt_memmap[VIRT_BIOS_ROM].base, | |
597 | virt_memmap[VIRT_BIOS_ROM].size); | |
598 | g_free(filename); | |
599 | } else { | |
600 | bios_size = -1; | |
601 | } | |
602 | ||
603 | if ((bios_size < 0 || bios_size > virt_memmap[VIRT_BIOS_ROM].size) && | |
604 | !kernel_filename && !qtest_enabled()) { | |
605 | error_report("Could not load MIPS bios '%s'", machine->firmware); | |
606 | exit(1); | |
607 | } | |
608 | ||
609 | fw_conf_init(ram_size); | |
610 | } | |
611 | ||
612 | loongson3_virt_devices_init(machine, liointc); | |
613 | } | |
614 | ||
615 | static void loongson3v_machine_class_init(ObjectClass *oc, void *data) | |
616 | { | |
617 | MachineClass *mc = MACHINE_CLASS(oc); | |
618 | ||
619 | mc->desc = "Loongson-3 Virtualization Platform"; | |
620 | mc->init = mips_loongson3_virt_init; | |
621 | mc->block_default_type = IF_IDE; | |
622 | mc->max_cpus = LOONGSON_MAX_VCPUS; | |
623 | mc->default_ram_id = "loongson3.highram"; | |
624 | mc->default_ram_size = 1600 * MiB; | |
625 | mc->kvm_type = mips_kvm_type; | |
626 | mc->minimum_page_bits = 14; | |
627 | } | |
628 | ||
629 | static const TypeInfo loongson3_machine_types[] = { | |
630 | { | |
631 | .name = TYPE_LOONGSON_MACHINE, | |
632 | .parent = TYPE_MACHINE, | |
633 | .instance_size = sizeof(LoongsonMachineState), | |
634 | .class_init = loongson3v_machine_class_init, | |
635 | } | |
636 | }; | |
637 | ||
638 | DEFINE_TYPES(loongson3_machine_types) |