aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-02-12 09:16:36 -0500
committerStefan Hajnoczi <stefanha@redhat.com>2025-02-12 09:16:36 -0500
commitde278e54aefed143526174335f8286f7437d20be (patch)
treef5dd8165c33338074d347320f1e96177d383d661
parentd384903bb4b7def3f7f0d03e4707ca53f7584f1e (diff)
parent456739ce4347c21b6fa2ec1b6585bc4a6504446f (diff)
downloadqemu-de278e54aefed143526174335f8286f7437d20be.zip
qemu-de278e54aefed143526174335f8286f7437d20be.tar.gz
qemu-de278e54aefed143526174335f8286f7437d20be.tar.bz2
Merge tag 'pull-loongarch-20250212' of https://gitlab.com/bibo-mao/qemu into staging
loongarch queue # -----BEGIN PGP SIGNATURE----- # # iHUEABYKAB0WIQQNhkKjomWfgLCz0aQfewwSUazn0QUCZ6wQngAKCRAfewwSUazn # 0SggAQDk5mp90dBJwu05kioq+Inx/bwxmamweA+FmeqAnoQ79QEApDBPfppkrN2y # AxNZL0EL5zRFU3zECSTevpRMQ3UoVQk= # =tLFD # -----END PGP SIGNATURE----- # gpg: Signature made Tue 11 Feb 2025 22:08:14 EST # gpg: using EDDSA key 0D8642A3A2659F80B0B3D1A41F7B0C1251ACE7D1 # gpg: Good signature from "bibo mao <maobibo@loongson.cn>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 7044 3A00 19C0 E97A 31C7 13C4 8E86 8FB7 A176 9D4C # Subkey fingerprint: 0D86 42A3 A265 9F80 B0B3 D1A4 1F7B 0C12 51AC E7D1 * tag 'pull-loongarch-20250212' of https://gitlab.com/bibo-mao/qemu: hw/loongarch/virt: CPU irq line connection improvement hw/loongarch/virt: Remove unused ipistate hw/loongarch/virt: Set iocsr address space when CPU is created hw/loongarch/virt: Add separate file for fdt building hw/loongarch/virt: Rename function prefix name hw/loongarch/virt: Rename filename acpi-build with virt-acpi-build Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r--hw/loongarch/meson.build6
-rw-r--r--hw/loongarch/virt-acpi-build.c (renamed from hw/loongarch/acpi-build.c)6
-rw-r--r--hw/loongarch/virt-fdt-build.c535
-rw-r--r--hw/loongarch/virt.c593
-rw-r--r--include/hw/loongarch/virt.h5
-rw-r--r--target/loongarch/cpu.h2
6 files changed, 584 insertions, 563 deletions
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
index 005f017..d494d1e 100644
--- a/hw/loongarch/meson.build
+++ b/hw/loongarch/meson.build
@@ -3,7 +3,9 @@ loongarch_ss.add(files(
'boot.c',
))
common_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('fw_cfg.c'))
-loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c'))
-loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c'))
+loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files(
+ 'virt-fdt-build.c',
+ 'virt.c'))
+loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
hw_arch += {'loongarch': loongarch_ss}
diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/virt-acpi-build.c
index fdd62ac..9ca88d6 100644
--- a/hw/loongarch/acpi-build.c
+++ b/hw/loongarch/virt-acpi-build.c
@@ -656,7 +656,7 @@ static const VMStateDescription vmstate_acpi_build = {
},
};
-static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
+static bool virt_is_acpi_enabled(LoongArchVirtMachineState *lvms)
{
if (lvms->acpi == ON_OFF_AUTO_OFF) {
return false;
@@ -664,7 +664,7 @@ static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
return true;
}
-void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
+void virt_acpi_setup(LoongArchVirtMachineState *lvms)
{
AcpiBuildTables tables;
AcpiBuildState *build_state;
@@ -674,7 +674,7 @@ void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
return;
}
- if (!loongarch_is_acpi_enabled(lvms)) {
+ if (!virt_is_acpi_enabled(lvms)) {
ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
return;
}
diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c
new file mode 100644
index 0000000..dbc269a
--- /dev/null
+++ b/hw/loongarch/virt-fdt-build.c
@@ -0,0 +1,535 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025 Loongson Technology Corporation Limited
+ */
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/guest-random.h"
+#include <libfdt.h>
+#include "hw/acpi/generic_event_device.h"
+#include "hw/core/sysbus-fdt.h"
+#include "hw/intc/loongarch_extioi.h"
+#include "hw/loader.h"
+#include "hw/loongarch/virt.h"
+#include "hw/pci-host/gpex.h"
+#include "hw/pci-host/ls7a.h"
+#include "system/device_tree.h"
+#include "system/reset.h"
+#include "target/loongarch/cpu.h"
+
+static void create_fdt(LoongArchVirtMachineState *lvms)
+{
+ MachineState *ms = MACHINE(lvms);
+ uint8_t rng_seed[32];
+
+ ms->fdt = create_device_tree(&lvms->fdt_size);
+ if (!ms->fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ /* Header */
+ qemu_fdt_setprop_string(ms->fdt, "/", "compatible",
+ "linux,dummy-loongson3");
+ qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_add_subnode(ms->fdt, "/chosen");
+
+ /* Pass seed to RNG */
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+ qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
+}
+
+static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms)
+{
+ int num;
+ MachineState *ms = MACHINE(lvms);
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus;
+ LoongArchCPU *cpu;
+ CPUState *cs;
+ char *nodename, *map_path;
+
+ qemu_fdt_add_subnode(ms->fdt, "/cpus");
+ qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
+ qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
+
+ /* cpu nodes */
+ possible_cpus = mc->possible_cpu_arch_ids(ms);
+ for (num = 0; num < possible_cpus->len; num++) {
+ cs = possible_cpus->cpus[num].cpu;
+ if (cs == NULL) {
+ continue;
+ }
+
+ nodename = g_strdup_printf("/cpus/cpu@%d", num);
+ cpu = LOONGARCH_CPU(cs);
+
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ cpu->dtb_compatible);
+ if (possible_cpus->cpus[num].props.has_node_id) {
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id",
+ possible_cpus->cpus[num].props.node_id);
+ }
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
+ qemu_fdt_alloc_phandle(ms->fdt));
+ g_free(nodename);
+ }
+
+ /*cpu map */
+ qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map");
+ for (num = 0; num < possible_cpus->len; num++) {
+ cs = possible_cpus->cpus[num].cpu;
+ if (cs == NULL) {
+ continue;
+ }
+
+ nodename = g_strdup_printf("/cpus/cpu@%d", num);
+ if (ms->smp.threads > 1) {
+ map_path = g_strdup_printf(
+ "/cpus/cpu-map/socket%d/core%d/thread%d",
+ num / (ms->smp.cores * ms->smp.threads),
+ (num / ms->smp.threads) % ms->smp.cores,
+ num % ms->smp.threads);
+ } else {
+ map_path = g_strdup_printf(
+ "/cpus/cpu-map/socket%d/core%d",
+ num / ms->smp.cores,
+ num % ms->smp.cores);
+ }
+ qemu_fdt_add_path(ms->fdt, map_path);
+ qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", nodename);
+
+ g_free(map_path);
+ g_free(nodename);
+ }
+}
+
+static void fdt_add_memory_node(MachineState *ms,
+ uint64_t base, uint64_t size, int node_id)
+{
+ char *nodename = g_strdup_printf("/memory@%" PRIx64, base);
+
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base,
+ size >> 32, size);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory");
+
+ if (ms->numa_state && ms->numa_state->num_nodes) {
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id);
+ }
+
+ g_free(nodename);
+}
+
+static void fdt_add_memory_nodes(MachineState *ms)
+{
+ hwaddr base, size, ram_size, gap;
+ int i, nb_numa_nodes, nodes;
+ NodeInfo *numa_info;
+
+ ram_size = ms->ram_size;
+ base = VIRT_LOWMEM_BASE;
+ gap = VIRT_LOWMEM_SIZE;
+ nodes = nb_numa_nodes = ms->numa_state->num_nodes;
+ numa_info = ms->numa_state->nodes;
+ if (!nodes) {
+ nodes = 1;
+ }
+
+ for (i = 0; i < nodes; i++) {
+ if (nb_numa_nodes) {
+ size = numa_info[i].node_mem;
+ } else {
+ size = ram_size;
+ }
+
+ /*
+ * memory for the node splited into two part
+ * lowram: [base, +gap)
+ * highram: [VIRT_HIGHMEM_BASE, +(len - gap))
+ */
+ if (size >= gap) {
+ fdt_add_memory_node(ms, base, gap, i);
+ size -= gap;
+ base = VIRT_HIGHMEM_BASE;
+ gap = ram_size - VIRT_LOWMEM_SIZE;
+ }
+
+ if (size) {
+ fdt_add_memory_node(ms, base, size, i);
+ base += size;
+ gap -= size;
+ }
+ }
+}
+
+static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms)
+{
+ char *nodename;
+ hwaddr base = VIRT_FWCFG_BASE;
+ const MachineState *ms = MACHINE(lvms);
+
+ nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename,
+ "compatible", "qemu,fw-cfg-mmio");
+ qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
+ 2, base, 2, 0x18);
+ qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0);
+ g_free(nodename);
+}
+
+static void fdt_add_flash_node(LoongArchVirtMachineState *lvms)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ MemoryRegion *flash_mem;
+
+ hwaddr flash0_base;
+ hwaddr flash0_size;
+
+ hwaddr flash1_base;
+ hwaddr flash1_size;
+
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
+ flash0_base = flash_mem->addr;
+ flash0_size = memory_region_size(flash_mem);
+
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
+ flash1_base = flash_mem->addr;
+ flash1_size = memory_region_size(flash_mem);
+
+ nodename = g_strdup_printf("/flash@%" PRIx64, flash0_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash");
+ qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
+ 2, flash0_base, 2, flash0_size,
+ 2, flash1_base, 2, flash1_size);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4);
+ g_free(nodename);
+}
+
+static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms,
+ uint32_t *cpuintc_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+
+ *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/cpuic");
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,cpu-interrupt-controller");
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
+ g_free(nodename);
+}
+
+static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms,
+ uint32_t *cpuintc_phandle,
+ uint32_t *eiointc_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr extioi_base = APIC_BASE;
+ hwaddr extioi_size = EXTIOI_SIZE;
+
+ *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,ls2k2000-eiointc");
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *cpuintc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0,
+ extioi_base, 0x0, extioi_size);
+ g_free(nodename);
+}
+
+static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms,
+ uint32_t *eiointc_phandle,
+ uint32_t *pch_pic_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr pch_pic_base = VIRT_PCH_REG_BASE;
+ hwaddr pch_pic_size = VIRT_PCH_REG_SIZE;
+
+ *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,pch-pic-1.0");
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0,
+ pch_pic_base, 0, pch_pic_size);
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *eiointc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0);
+ g_free(nodename);
+}
+
+static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms,
+ uint32_t *eiointc_phandle,
+ uint32_t *pch_msi_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW;
+ hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE;
+
+ *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,pch-msi-1.0");
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg",
+ 0, pch_msi_base,
+ 0, pch_msi_size);
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *eiointc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec",
+ VIRT_PCH_PIC_IRQ_NUM);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs",
+ EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM);
+ g_free(nodename);
+}
+
+static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
+ char *nodename,
+ uint32_t *pch_pic_phandle)
+{
+ int pin, dev;
+ uint32_t irq_map_stride = 0;
+ uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 10] = {};
+ uint32_t *irq_map = full_irq_map;
+ const MachineState *ms = MACHINE(lvms);
+
+ /*
+ * This code creates a standard swizzle of interrupts such that
+ * each device's first interrupt is based on it's PCI_SLOT number.
+ * (See pci_swizzle_map_irq_fn())
+ *
+ * We only need one entry per interrupt in the table (not one per
+ * possible slot) seeing the interrupt-map-mask will allow the table
+ * to wrap to any number of devices.
+ */
+
+ for (dev = 0; dev < PCI_NUM_PINS; dev++) {
+ int devfn = dev * 0x8;
+
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
+ int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
+ int i = 0;
+
+ /* Fill PCI address cells */
+ irq_map[i] = cpu_to_be32(devfn << 8);
+ i += 3;
+
+ /* Fill PCI Interrupt cells */
+ irq_map[i] = cpu_to_be32(pin + 1);
+ i += 1;
+
+ /* Fill interrupt controller phandle and cells */
+ irq_map[i++] = cpu_to_be32(*pch_pic_phandle);
+ irq_map[i++] = cpu_to_be32(irq_nr);
+
+ if (!irq_map_stride) {
+ irq_map_stride = i;
+ }
+ irq_map += irq_map_stride;
+ }
+ }
+
+
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map,
+ PCI_NUM_PINS * PCI_NUM_PINS *
+ irq_map_stride * sizeof(uint32_t));
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask",
+ 0x1800, 0, 0, 0x7);
+}
+
+static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle,
+ uint32_t *pch_msi_phandle)
+{
+ char *nodename;
+ hwaddr base_mmio = VIRT_PCI_MEM_BASE;
+ hwaddr size_mmio = VIRT_PCI_MEM_SIZE;
+ hwaddr base_pio = VIRT_PCI_IO_BASE;
+ hwaddr size_pio = VIRT_PCI_IO_SIZE;
+ hwaddr base_pcie = VIRT_PCI_CFG_BASE;
+ hwaddr size_pcie = VIRT_PCI_CFG_SIZE;
+ hwaddr base = base_pcie;
+ const MachineState *ms = MACHINE(lvms);
+
+ nodename = g_strdup_printf("/pcie@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename,
+ "compatible", "pci-host-ecam-generic");
+ qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci");
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0,
+ PCIE_MMCFG_BUS(VIRT_PCI_CFG_SIZE - 1));
+ qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0);
+ qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
+ 2, base_pcie, 2, size_pcie);
+ qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
+ 1, FDT_PCI_RANGE_IOPORT, 2, VIRT_PCI_IO_OFFSET,
+ 2, base_pio, 2, size_pio,
+ 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
+ 2, base_mmio, 2, size_mmio);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map",
+ 0, *pch_msi_phandle, 0, 0x10000);
+ fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle);
+ g_free(nodename);
+}
+
+static void fdt_add_uart_node(LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle, hwaddr base,
+ int irq, bool chosen)
+{
+ char *nodename;
+ hwaddr size = VIRT_UART_SIZE;
+ MachineState *ms = MACHINE(lvms);
+
+ nodename = g_strdup_printf("/serial@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a");
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000);
+ if (chosen) {
+ qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
+ }
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *pch_pic_phandle);
+ g_free(nodename);
+}
+
+static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle)
+{
+ char *nodename;
+ hwaddr base = VIRT_RTC_REG_BASE;
+ hwaddr size = VIRT_RTC_LEN;
+ MachineState *ms = MACHINE(lvms);
+
+ nodename = g_strdup_printf("/rtc@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,ls7a-rtc");
+ qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
+ VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *pch_pic_phandle);
+ g_free(nodename);
+}
+
+static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms)
+{
+ char *name;
+ uint32_t ged_handle;
+ MachineState *ms = MACHINE(lvms);
+ hwaddr base = VIRT_GED_REG_ADDR;
+ hwaddr size = ACPI_GED_REG_COUNT;
+
+ ged_handle = qemu_fdt_alloc_phandle(ms->fdt);
+ name = g_strdup_printf("/ged@%" PRIx64, base);
+ qemu_fdt_add_subnode(ms->fdt, name);
+ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon");
+ qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size);
+ /* 8 bit registers */
+ qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0);
+ qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1);
+ qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle);
+ ged_handle = qemu_fdt_get_phandle(ms->fdt, name);
+ g_free(name);
+
+ name = g_strdup_printf("/reboot");
+ qemu_fdt_add_subnode(ms->fdt, name);
+ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot");
+ qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
+ qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET);
+ qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE);
+ g_free(name);
+
+ name = g_strdup_printf("/poweroff");
+ qemu_fdt_add_subnode(ms->fdt, name);
+ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff");
+ qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
+ qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL);
+ qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN |
+ (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS));
+ g_free(name);
+}
+
+void virt_fdt_setup(LoongArchVirtMachineState *lvms)
+{
+ MachineState *machine = MACHINE(lvms);
+ uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle;
+ int i;
+
+ create_fdt(lvms);
+ fdt_add_cpu_nodes(lvms);
+ fdt_add_memory_nodes(machine);
+ fdt_add_fw_cfg_node(lvms);
+ fdt_add_flash_node(lvms);
+
+ /* Add cpu interrupt-controller */
+ fdt_add_cpuic_node(lvms, &cpuintc_phandle);
+ /* Add Extend I/O Interrupt Controller node */
+ fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle);
+ /* Add PCH PIC node */
+ fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle);
+ /* Add PCH MSI node */
+ fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle);
+ /* Add pcie node */
+ fdt_add_pcie_node(lvms, &pch_pic_phandle, &pch_msi_phandle);
+
+ /*
+ * Create uart fdt node in reverse order so that they appear
+ * in the finished device tree lowest address first
+ */
+ for (i = VIRT_UART_COUNT; i-- > 0;) {
+ hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE;
+ int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE;
+ fdt_add_uart_node(lvms, &pch_pic_phandle, base, irq, i == 0);
+ }
+
+ fdt_add_rtc_node(lvms, &pch_pic_phandle);
+ fdt_add_ged_reset(lvms);
+ platform_bus_add_all_fdt_nodes(machine->fdt, "/platic",
+ VIRT_PLATFORM_BUS_BASEADDRESS,
+ VIRT_PLATFORM_BUS_SIZE,
+ VIRT_PLATFORM_BUS_IRQ);
+
+ /*
+ * Since lowmem region starts from 0 and Linux kernel legacy start address
+ * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer
+ * access. FDT size limit with 1 MiB.
+ * Put the FDT into the memory map as a ROM image: this will ensure
+ * the FDT is copied again upon reset, even if addr points into RAM.
+ */
+ qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size);
+ rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE,
+ &address_space_memory);
+ qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds,
+ rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size));
+}
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index 63fa0f4..f2aa0a9 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -33,13 +33,9 @@
#include "hw/loongarch/fw_cfg.h"
#include "target/loongarch/cpu.h"
#include "hw/firmware/smbios.h"
-#include "hw/acpi/aml-build.h"
#include "qapi/qapi-visit-common.h"
#include "hw/acpi/generic_event_device.h"
#include "hw/mem/nvdimm.h"
-#include "system/device_tree.h"
-#include <libfdt.h>
-#include "hw/core/sysbus-fdt.h"
#include "hw/platform-bus.h"
#include "hw/display/ramfb.h"
#include "hw/mem/pc-dimm.h"
@@ -48,7 +44,6 @@
#include "hw/block/flash.h"
#include "hw/virtio/virtio-iommu.h"
#include "qemu/error-report.h"
-#include "qemu/guest-random.h"
static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms)
{
@@ -135,471 +130,6 @@ static void virt_flash_map(LoongArchVirtMachineState *lvms,
virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem);
}
-static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms,
- uint32_t *cpuintc_phandle)
-{
- MachineState *ms = MACHINE(lvms);
- char *nodename;
-
- *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
- nodename = g_strdup_printf("/cpuic");
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- "loongson,cpu-interrupt-controller");
- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
- g_free(nodename);
-}
-
-static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms,
- uint32_t *cpuintc_phandle,
- uint32_t *eiointc_phandle)
-{
- MachineState *ms = MACHINE(lvms);
- char *nodename;
- hwaddr extioi_base = APIC_BASE;
- hwaddr extioi_size = EXTIOI_SIZE;
-
- *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
- nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- "loongson,ls2k2000-eiointc");
- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
- *cpuintc_phandle);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3);
- qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0,
- extioi_base, 0x0, extioi_size);
- g_free(nodename);
-}
-
-static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms,
- uint32_t *eiointc_phandle,
- uint32_t *pch_pic_phandle)
-{
- MachineState *ms = MACHINE(lvms);
- char *nodename;
- hwaddr pch_pic_base = VIRT_PCH_REG_BASE;
- hwaddr pch_pic_size = VIRT_PCH_REG_SIZE;
-
- *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
- nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- "loongson,pch-pic-1.0");
- qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0,
- pch_pic_base, 0, pch_pic_size);
- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
- *eiointc_phandle);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0);
- g_free(nodename);
-}
-
-static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms,
- uint32_t *eiointc_phandle,
- uint32_t *pch_msi_phandle)
-{
- MachineState *ms = MACHINE(lvms);
- char *nodename;
- hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW;
- hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE;
-
- *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt);
- nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- "loongson,pch-msi-1.0");
- qemu_fdt_setprop_cells(ms->fdt, nodename, "reg",
- 0, pch_msi_base,
- 0, pch_msi_size);
- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
- *eiointc_phandle);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec",
- VIRT_PCH_PIC_IRQ_NUM);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs",
- EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM);
- g_free(nodename);
-}
-
-static void fdt_add_flash_node(LoongArchVirtMachineState *lvms)
-{
- MachineState *ms = MACHINE(lvms);
- char *nodename;
- MemoryRegion *flash_mem;
-
- hwaddr flash0_base;
- hwaddr flash0_size;
-
- hwaddr flash1_base;
- hwaddr flash1_size;
-
- flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
- flash0_base = flash_mem->addr;
- flash0_size = memory_region_size(flash_mem);
-
- flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
- flash1_base = flash_mem->addr;
- flash1_size = memory_region_size(flash_mem);
-
- nodename = g_strdup_printf("/flash@%" PRIx64, flash0_base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash");
- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
- 2, flash0_base, 2, flash0_size,
- 2, flash1_base, 2, flash1_size);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4);
- g_free(nodename);
-}
-
-static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms,
- uint32_t *pch_pic_phandle)
-{
- char *nodename;
- hwaddr base = VIRT_RTC_REG_BASE;
- hwaddr size = VIRT_RTC_LEN;
- MachineState *ms = MACHINE(lvms);
-
- nodename = g_strdup_printf("/rtc@%" PRIx64, base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- "loongson,ls7a-rtc");
- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
- qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
- VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
- *pch_pic_phandle);
- g_free(nodename);
-}
-
-static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms)
-{
- char *name;
- uint32_t ged_handle;
- MachineState *ms = MACHINE(lvms);
- hwaddr base = VIRT_GED_REG_ADDR;
- hwaddr size = ACPI_GED_REG_COUNT;
-
- ged_handle = qemu_fdt_alloc_phandle(ms->fdt);
- name = g_strdup_printf("/ged@%" PRIx64, base);
- qemu_fdt_add_subnode(ms->fdt, name);
- qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon");
- qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size);
- /* 8 bit registers */
- qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0);
- qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1);
- qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle);
- ged_handle = qemu_fdt_get_phandle(ms->fdt, name);
- g_free(name);
-
- name = g_strdup_printf("/reboot");
- qemu_fdt_add_subnode(ms->fdt, name);
- qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot");
- qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
- qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET);
- qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE);
- g_free(name);
-
- name = g_strdup_printf("/poweroff");
- qemu_fdt_add_subnode(ms->fdt, name);
- qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff");
- qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle);
- qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL);
- qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN |
- (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS));
- g_free(name);
-}
-
-static void fdt_add_uart_node(LoongArchVirtMachineState *lvms,
- uint32_t *pch_pic_phandle, hwaddr base,
- int irq, bool chosen)
-{
- char *nodename;
- hwaddr size = VIRT_UART_SIZE;
- MachineState *ms = MACHINE(lvms);
-
- nodename = g_strdup_printf("/serial@%" PRIx64, base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a");
- qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000);
- if (chosen) {
- qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
- }
- qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
- *pch_pic_phandle);
- g_free(nodename);
-}
-
-static void create_fdt(LoongArchVirtMachineState *lvms)
-{
- MachineState *ms = MACHINE(lvms);
- uint8_t rng_seed[32];
-
- ms->fdt = create_device_tree(&lvms->fdt_size);
- if (!ms->fdt) {
- error_report("create_device_tree() failed");
- exit(1);
- }
-
- /* Header */
- qemu_fdt_setprop_string(ms->fdt, "/", "compatible",
- "linux,dummy-loongson3");
- qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2);
- qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2);
- qemu_fdt_add_subnode(ms->fdt, "/chosen");
-
- /* Pass seed to RNG */
- qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
- qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
-}
-
-static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms)
-{
- int num;
- MachineState *ms = MACHINE(lvms);
- MachineClass *mc = MACHINE_GET_CLASS(ms);
- const CPUArchIdList *possible_cpus;
- LoongArchCPU *cpu;
- CPUState *cs;
- char *nodename, *map_path;
-
- qemu_fdt_add_subnode(ms->fdt, "/cpus");
- qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1);
- qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0);
-
- /* cpu nodes */
- possible_cpus = mc->possible_cpu_arch_ids(ms);
- for (num = 0; num < possible_cpus->len; num++) {
- cs = possible_cpus->cpus[num].cpu;
- if (cs == NULL) {
- continue;
- }
-
- nodename = g_strdup_printf("/cpus/cpu@%d", num);
- cpu = LOONGARCH_CPU(cs);
-
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu");
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- cpu->dtb_compatible);
- if (possible_cpus->cpus[num].props.has_node_id) {
- qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id",
- possible_cpus->cpus[num].props.node_id);
- }
- qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle",
- qemu_fdt_alloc_phandle(ms->fdt));
- g_free(nodename);
- }
-
- /*cpu map */
- qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map");
- for (num = 0; num < possible_cpus->len; num++) {
- cs = possible_cpus->cpus[num].cpu;
- if (cs == NULL) {
- continue;
- }
-
- nodename = g_strdup_printf("/cpus/cpu@%d", num);
- if (ms->smp.threads > 1) {
- map_path = g_strdup_printf(
- "/cpus/cpu-map/socket%d/core%d/thread%d",
- num / (ms->smp.cores * ms->smp.threads),
- (num / ms->smp.threads) % ms->smp.cores,
- num % ms->smp.threads);
- } else {
- map_path = g_strdup_printf(
- "/cpus/cpu-map/socket%d/core%d",
- num / ms->smp.cores,
- num % ms->smp.cores);
- }
- qemu_fdt_add_path(ms->fdt, map_path);
- qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", nodename);
-
- g_free(map_path);
- g_free(nodename);
- }
-}
-
-static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms)
-{
- char *nodename;
- hwaddr base = VIRT_FWCFG_BASE;
- const MachineState *ms = MACHINE(lvms);
-
- nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename,
- "compatible", "qemu,fw-cfg-mmio");
- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
- 2, base, 2, 0x18);
- qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0);
- g_free(nodename);
-}
-
-static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
- char *nodename,
- uint32_t *pch_pic_phandle)
-{
- int pin, dev;
- uint32_t irq_map_stride = 0;
- uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 10] = {};
- uint32_t *irq_map = full_irq_map;
- const MachineState *ms = MACHINE(lvms);
-
- /* This code creates a standard swizzle of interrupts such that
- * each device's first interrupt is based on it's PCI_SLOT number.
- * (See pci_swizzle_map_irq_fn())
- *
- * We only need one entry per interrupt in the table (not one per
- * possible slot) seeing the interrupt-map-mask will allow the table
- * to wrap to any number of devices.
- */
-
- for (dev = 0; dev < PCI_NUM_PINS; dev++) {
- int devfn = dev * 0x8;
-
- for (pin = 0; pin < PCI_NUM_PINS; pin++) {
- int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
- int i = 0;
-
- /* Fill PCI address cells */
- irq_map[i] = cpu_to_be32(devfn << 8);
- i += 3;
-
- /* Fill PCI Interrupt cells */
- irq_map[i] = cpu_to_be32(pin + 1);
- i += 1;
-
- /* Fill interrupt controller phandle and cells */
- irq_map[i++] = cpu_to_be32(*pch_pic_phandle);
- irq_map[i++] = cpu_to_be32(irq_nr);
-
- if (!irq_map_stride) {
- irq_map_stride = i;
- }
- irq_map += irq_map_stride;
- }
- }
-
-
- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map,
- PCI_NUM_PINS * PCI_NUM_PINS *
- irq_map_stride * sizeof(uint32_t));
- qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask",
- 0x1800, 0, 0, 0x7);
-}
-
-static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms,
- uint32_t *pch_pic_phandle,
- uint32_t *pch_msi_phandle)
-{
- char *nodename;
- hwaddr base_mmio = VIRT_PCI_MEM_BASE;
- hwaddr size_mmio = VIRT_PCI_MEM_SIZE;
- hwaddr base_pio = VIRT_PCI_IO_BASE;
- hwaddr size_pio = VIRT_PCI_IO_SIZE;
- hwaddr base_pcie = VIRT_PCI_CFG_BASE;
- hwaddr size_pcie = VIRT_PCI_CFG_SIZE;
- hwaddr base = base_pcie;
-
- const MachineState *ms = MACHINE(lvms);
-
- nodename = g_strdup_printf("/pcie@%" PRIx64, base);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename,
- "compatible", "pci-host-ecam-generic");
- qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci");
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0);
- qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0,
- PCIE_MMCFG_BUS(VIRT_PCI_CFG_SIZE - 1));
- qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0);
- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
- 2, base_pcie, 2, size_pcie);
- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
- 1, FDT_PCI_RANGE_IOPORT, 2, VIRT_PCI_IO_OFFSET,
- 2, base_pio, 2, size_pio,
- 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
- 2, base_mmio, 2, size_mmio);
- qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map",
- 0, *pch_msi_phandle, 0, 0x10000);
-
- fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle);
-
- g_free(nodename);
-}
-
-static void fdt_add_memory_node(MachineState *ms,
- uint64_t base, uint64_t size, int node_id)
-{
- char *nodename = g_strdup_printf("/memory@%" PRIx64, base);
-
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base,
- size >> 32, size);
- qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory");
-
- if (ms->numa_state && ms->numa_state->num_nodes) {
- qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id);
- }
-
- g_free(nodename);
-}
-
-static void fdt_add_memory_nodes(MachineState *ms)
-{
- hwaddr base, size, ram_size, gap;
- int i, nb_numa_nodes, nodes;
- NodeInfo *numa_info;
-
- ram_size = ms->ram_size;
- base = VIRT_LOWMEM_BASE;
- gap = VIRT_LOWMEM_SIZE;
- nodes = nb_numa_nodes = ms->numa_state->num_nodes;
- numa_info = ms->numa_state->nodes;
- if (!nodes) {
- nodes = 1;
- }
-
- for (i = 0; i < nodes; i++) {
- if (nb_numa_nodes) {
- size = numa_info[i].node_mem;
- } else {
- size = ram_size;
- }
-
- /*
- * memory for the node splited into two part
- * lowram: [base, +gap)
- * highram: [VIRT_HIGHMEM_BASE, +(len - gap))
- */
- if (size >= gap) {
- fdt_add_memory_node(ms, base, gap, i);
- size -= gap;
- base = VIRT_HIGHMEM_BASE;
- gap = ram_size - VIRT_LOWMEM_SIZE;
- }
-
- if (size) {
- fdt_add_memory_node(ms, base, size, i);
- base += size;
- gap -= size;
- }
- }
-}
-
static void virt_build_smbios(LoongArchVirtMachineState *lvms)
{
MachineState *ms = MACHINE(lvms);
@@ -627,66 +157,12 @@ static void virt_build_smbios(LoongArchVirtMachineState *lvms)
}
}
-static void virt_fdt_setup(LoongArchVirtMachineState *lvms)
-{
- MachineState *machine = MACHINE(lvms);
- uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle;
- int i;
-
- create_fdt(lvms);
- fdt_add_cpu_nodes(lvms);
- fdt_add_memory_nodes(machine);
- fdt_add_fw_cfg_node(lvms);
- fdt_add_flash_node(lvms);
-
- /* Add cpu interrupt-controller */
- fdt_add_cpuic_node(lvms, &cpuintc_phandle);
- /* Add Extend I/O Interrupt Controller node */
- fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle);
- /* Add PCH PIC node */
- fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle);
- /* Add PCH MSI node */
- fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle);
- /* Add pcie node */
- fdt_add_pcie_node(lvms, &pch_pic_phandle, &pch_msi_phandle);
-
- /*
- * Create uart fdt node in reverse order so that they appear
- * in the finished device tree lowest address first
- */
- for (i = VIRT_UART_COUNT; i-- > 0;) {
- hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE;
- int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE;
- fdt_add_uart_node(lvms, &pch_pic_phandle, base, irq, i == 0);
- }
-
- fdt_add_rtc_node(lvms, &pch_pic_phandle);
- fdt_add_ged_reset(lvms);
- platform_bus_add_all_fdt_nodes(machine->fdt, "/platic",
- VIRT_PLATFORM_BUS_BASEADDRESS,
- VIRT_PLATFORM_BUS_SIZE,
- VIRT_PLATFORM_BUS_IRQ);
-
- /*
- * Since lowmem region starts from 0 and Linux kernel legacy start address
- * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer
- * access. FDT size limit with 1 MiB.
- * Put the FDT into the memory map as a ROM image: this will ensure
- * the FDT is copied again upon reset, even if addr points into RAM.
- */
- qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size);
- rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE,
- &address_space_memory);
- qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds,
- rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size));
-}
-
static void virt_done(Notifier *notifier, void *data)
{
LoongArchVirtMachineState *lvms = container_of(notifier,
LoongArchVirtMachineState, machine_done);
virt_build_smbios(lvms);
- loongarch_acpi_setup(lvms);
+ virt_acpi_setup(lvms);
virt_fdt_setup(lvms);
}
@@ -842,16 +318,43 @@ static void virt_devices_init(DeviceState *pch_pic,
lvms->platform_bus_dev = create_platform_bus(pch_pic);
}
-static void virt_irq_init(LoongArchVirtMachineState *lvms)
+static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms)
{
+ int num, pin;
MachineState *ms = MACHINE(lvms);
- DeviceState *pch_pic, *pch_msi, *cpudev;
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus;
+ CPUState *cs;
+
+ /* cpu nodes */
+ possible_cpus = mc->possible_cpu_arch_ids(ms);
+ for (num = 0; num < possible_cpus->len; num++) {
+ cs = possible_cpus->cpus[num].cpu;
+ if (cs == NULL) {
+ continue;
+ }
+
+ /* connect ipi irq to cpu irq */
+ qdev_connect_gpio_out(lvms->ipi, num,
+ qdev_get_gpio_in(DEVICE(cs), IRQ_IPI));
+
+ /*
+ * connect ext irq to the cpu irq
+ * cpu_pin[9:2] <= intc_pin[7:0]
+ */
+ for (pin = 0; pin < LS3A_INTC_IP; pin++) {
+ qdev_connect_gpio_out(lvms->extioi, (num * LS3A_INTC_IP + pin),
+ qdev_get_gpio_in(DEVICE(cs), pin + 2));
+ }
+ }
+}
+
+static void virt_irq_init(LoongArchVirtMachineState *lvms)
+{
+ DeviceState *pch_pic, *pch_msi;
DeviceState *ipi, *extioi;
SysBusDevice *d;
- LoongArchCPU *lacpu;
- CPULoongArchState *env;
- CPUState *cpu_state;
- int cpu, pin, i, start, num;
+ int i, start, num;
/*
* Extended IRQ model.
@@ -899,6 +402,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms)
/* Create IPI device */
ipi = qdev_new(TYPE_LOONGARCH_IPI);
+ lvms->ipi = ipi;
sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal);
/* IPI iocsr memory region */
@@ -907,20 +411,9 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms)
memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR,
sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1));
- for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
- cpu_state = qemu_get_cpu(cpu);
- cpudev = DEVICE(cpu_state);
- lacpu = LOONGARCH_CPU(cpu_state);
- env = &(lacpu->env);
- env->address_space_iocsr = &lvms->as_iocsr;
-
- /* connect ipi irq to cpu irq */
- qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI));
- env->ipistate = ipi;
- }
-
/* Create EXTIOI device */
extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
+ lvms->extioi = extioi;
if (virt_is_veiointc_enabled(lvms)) {
qdev_prop_set_bit(extioi, "has-virtualization-extension", true);
}
@@ -932,18 +425,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms)
sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1));
}
- /*
- * connect ext irq to the cpu irq
- * cpu_pin[9:2] <= intc_pin[7:0]
- */
- for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
- cpudev = DEVICE(qemu_get_cpu(cpu));
- for (pin = 0; pin < LS3A_INTC_IP; pin++) {
- qdev_connect_gpio_out(extioi, (cpu * 8 + pin),
- qdev_get_gpio_in(cpudev, pin + 2));
- }
- }
-
+ virt_cpu_irq_init(lvms);
pch_pic = qdev_new(TYPE_LOONGARCH_PIC);
num = VIRT_PCH_PIC_IRQ_NUM;
qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num);
@@ -1213,6 +695,7 @@ static void virt_init(MachineState *machine)
machine->possible_cpus->cpus[i].cpu = cpu;
lacpu = LOONGARCH_CPU(cpu);
lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id;
+ lacpu->env.address_space_iocsr = &lvms->as_iocsr;
}
fw_cfg_add_memory(machine);
diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h
index 9ba4779..661efae 100644
--- a/include/hw/loongarch/virt.h
+++ b/include/hw/loongarch/virt.h
@@ -60,9 +60,12 @@ struct LoongArchVirtMachineState {
MemoryRegion iocsr_mem;
AddressSpace as_iocsr;
struct loongarch_boot_info bootinfo;
+ DeviceState *ipi;
+ DeviceState *extioi;
};
#define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE)
-void loongarch_acpi_setup(LoongArchVirtMachineState *lvms);
+void virt_acpi_setup(LoongArchVirtMachineState *lvms);
+void virt_fdt_setup(LoongArchVirtMachineState *lvms);
#endif
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 8eee49a..f2a23b7 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -385,8 +385,6 @@ typedef struct CPUArchState {
bool load_elf;
uint64_t elf_address;
uint32_t mp_state;
- /* Store ipistate to access from this struct */
- DeviceState *ipistate;
struct loongarch_boot_info *boot_info;
#endif