+
+ ret = ((ret + 32) & ~31);
+
+ boot_params_buf = (void *)(params_buf + ret);
+ boot_params_p = boot_params_buf + align(sizeof(struct boot_params));
+
+ init_boot_param(boot_params_buf);
+
+ rom_add_blob_fixed("params", params_buf, params_size,
+ BOOTPARAM_PHYADDR);
+ loaderparams.a0 = 2;
+ loaderparams.a1 = 0xffffffff80000000ULL + BOOTPARAM_PHYADDR;
+ loaderparams.a2 = 0xffffffff80000000ULL + BOOTPARAM_PHYADDR + ret;
+
+ return 0;
+}
+
+static int64_t load_kernel(CPUMIPSState *env)
+{
+ long kernel_size;
+ ram_addr_t initrd_offset;
+ int64_t kernel_entry, kernel_low, kernel_high, initrd_size;
+
+ kernel_size = load_elf(loaderparams.kernel_filename, NULL,
+ cpu_mips_kseg0_to_phys, NULL,
+ (uint64_t *)&kernel_entry,
+ (uint64_t *)&kernel_low, (uint64_t *)&kernel_high,
+ NULL, 0, EM_MIPS, 1, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s': %s",
+ loaderparams.kernel_filename,
+ load_elf_strerror(kernel_size));
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size(loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) &
+ INITRD_PAGE_MASK;
+ initrd_offset = MAX(initrd_offset, INITRD_OFFSET);
+
+ if (initrd_offset + initrd_size > ram_size) {
+ error_report("memory too small for initial ram disk '%s'",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset,
+ ram_size - initrd_offset);
+ }
+
+ if (initrd_size == (target_ulong) -1) {
+ error_report("could not load initial ram disk '%s'",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Setup prom parameters. */
+ set_prom_bootparam(initrd_offset, initrd_size);
+
+ return kernel_entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+ CPUMIPSState *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+
+ /* Loongson-3 reset stuff */
+ if (loaderparams.kernel_filename) {
+ if (cpu == MIPS_CPU(first_cpu)) {
+ env->active_tc.gpr[4] = loaderparams.a0;
+ env->active_tc.gpr[5] = loaderparams.a1;
+ env->active_tc.gpr[6] = loaderparams.a2;
+ env->active_tc.PC = loaderparams.kernel_entry;
+ }
+ env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
+ }
+}
+
+static void loongson3_isa_init(qemu_irq intc)
+{
+ qemu_irq *i8259;
+ ISABus *isa_bus;
+
+ isa_bus = isa_bus_new(NULL, get_system_memory(), get_system_io(),
&error_abort);
+
+ /* Interrupt controller */
+ /* The 8259 -> IP3 */
+ i8259 = i8259_init(isa_bus, intc);
+ isa_bus_irqs(isa_bus, i8259);
+ /* init other devices */
+ isa_create_simple(isa_bus, "i8042");
+ mc146818_rtc_init(isa_bus, 2000, NULL);
+}
+
+static inline void loongson3_pcie_init(MachineState *machine, DeviceState *pic)
+{
+ int i;
+ qemu_irq irq;
+ PCIBus *pci_bus;
+ DeviceState *dev;
+ MemoryRegion *pio_alias;
+ MemoryRegion *mmio_alias, *mmio_reg;
+ MemoryRegion *ecam_alias, *ecam_reg;
+
+ dev = qdev_create(NULL, TYPE_GPEX_HOST);
+
+ qdev_init_nofail(dev);
+ pci_bus = PCI_HOST_BRIDGE(dev)->bus;
+
+ ecam_alias = g_new0(MemoryRegion, 1);
+ ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_init_alias(ecam_alias, OBJECT(dev), "pcie-ecam",
+ ecam_reg, 0, VIRT_PCI_ECAM_SIZE);
+ memory_region_add_subregion(get_system_memory(), VIRT_PCI_ECAM_BASE,
ecam_alias);
+
+ mmio_alias = g_new0(MemoryRegion, 1);
+ mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
+ memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio",
+ mmio_reg, VIRT_PCI_MEM_BASE, VIRT_PCI_MEM_SIZE);
+ memory_region_add_subregion(get_system_memory(), VIRT_PCI_MEM_BASE,
mmio_alias);
+
+ pio_alias = g_new0(MemoryRegion, 1);
+ memory_region_init_alias(pio_alias, OBJECT(dev), "pcie-pio",
+ get_system_io(), 0, VIRT_PCI_IO_SIZE);
+ memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE,
pio_alias);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_PCI_IO_BASE);
+
+ for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
+ gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i);
+ }
+
+ pci_vga_init(pci_bus);
+
+ for (i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (!nd->model) {
+ nd->model = g_strdup("virtio");
+ }
+
+ pci_nic_init_nofail(nd, pci_bus, nd->model, NULL);
+ }
+}
+
+static void mips_loongson3_init(MachineState *machine)
+{
+ int i;
+ long bios_size;
+ MIPSCPU *cpu;
+ CPUMIPSState *env;
+ char *filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ ram_addr_t ram_size = machine->ram_size;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *bios = g_new(MemoryRegion, 1);
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);
+
+ if (!kvm_enabled()) {
+ if (!machine->cpu_type) {
+ machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A1000");
+ }
+ if (!strstr(machine->cpu_type, "Loongson-3A1000")) {
+ error_report("Loongson-3/TCG need cpu type Loongson-3A1000");
+ exit(1);
+ }
+ } else {
+ if (!machine->cpu_type) {
+ machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A4000");
+ }
+ if (!strstr(machine->cpu_type, "Loongson-3A4000")) {
+ error_report("Loongson-3/KVM need cpu type Loongson-3A4000");
+ exit(1);
+ }
+ }
+
+ if (ram_size < 256 * 0x100000) {
+ error_report("Loongson-3 need at least 256MB memory");
+ exit(1);
+ }
+
+ for (i = 0; i < machine->smp.cpus; i++) {
+ /* init CPUs */
+ cpu = MIPS_CPU(cpu_create(machine->cpu_type));
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(cpu);
+ cpu_mips_clock_init(cpu);
+ qemu_register_reset(main_cpu_reset, cpu);
+ }
+ env = &MIPS_CPU(first_cpu)->env;
+
+ /* Allocate RAM/BIOS, 0x00000000~0x10000000 is alias of
0x80000000~0x90000000 */
+ memory_region_init_rom(bios, NULL, "loongson3.bios",
+ BIOS_SIZE, &error_fatal);
+ memory_region_init_alias(ram, NULL, "loongson3.lowram",
+ machine->ram, 0, 256 * 0x100000);
+ memory_region_init_io(iomem, NULL, &loongson3_pm_ops,
+ NULL, "loongson3_pm", PM_MMIO_SIZE);
+
+ memory_region_add_subregion(address_space_mem, 0x00000000LL, ram);
+ memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios);
+ memory_region_add_subregion(address_space_mem, 0x80000000LL, machine->ram);
+ memory_region_add_subregion(address_space_mem, PM_MMIO_ADDR, iomem);
+
+ /*
+ * We do not support flash operation, just loading pmon.bin as raw BIOS.
+ * Please use -L to set the BIOS path and -bios to set bios name.
+ */
+
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ loaderparams.kernel_entry = load_kernel(env);
+ rom_add_blob_fixed("bios",
+ bios_boot_code, sizeof(bios_boot_code), 0x1fc00000LL);
+ } else {
+ if (bios_name == NULL) {
+ bios_name = LOONGSON3_BIOSNAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0x1fc00000LL,
+ BIOS_SIZE);
+ g_free(filename);
+ } else {
+ bios_size = -1;
+ }
+
+ if ((bios_size < 0 || bios_size > BIOS_SIZE) &&
+ !kernel_filename && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", bios_name);
+ exit(1);
+ }
+
+ fw_conf_init(ram_size);
+ rom_add_blob_fixed("fw_conf",
+ &fw_config, sizeof(fw_config), FW_CONF_ADDR);
+ }
+
+ msi_nonbroken = true;