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