diff options
Diffstat (limited to 'hw/loongarch')
-rw-r--r-- | hw/loongarch/Kconfig | 6 | ||||
-rw-r--r-- | hw/loongarch/boot.c | 187 | ||||
-rw-r--r-- | hw/loongarch/fw_cfg.c | 2 | ||||
-rw-r--r-- | hw/loongarch/meson.build | 8 | ||||
-rw-r--r-- | hw/loongarch/virt-acpi-build.c (renamed from hw/loongarch/acpi-build.c) | 131 | ||||
-rw-r--r-- | hw/loongarch/virt-fdt-build.c | 534 | ||||
-rw-r--r-- | hw/loongarch/virt.c | 988 |
7 files changed, 1209 insertions, 647 deletions
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 89be737..bb2838b 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -5,19 +5,19 @@ config LOONGARCH_VIRT select DEVICE_TREE select PCI select PCI_EXPRESS_GENERIC_BRIDGE - imply VIRTIO_VGA imply PCI_DEVICES imply NVDIMM imply TPM_TIS_SYSBUS - select SERIAL + select SERIAL_MM select VIRTIO_PCI select PLATFORM_BUS - select LOONGSON_IPI + select LOONGARCH_IPI select LOONGARCH_PCH_PIC select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI select LS7A_RTC select SMBIOS + select ACPI_CPU_HOTPLUG select ACPI_PCI select ACPI_HW_REDUCED select FW_CFG_DMA diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index cb66870..14d6c52 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -12,14 +12,28 @@ #include "hw/loader.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" -#include "sysemu/qtest.h" +#include "system/reset.h" +#include "system/qtest.h" -struct memmap_entry *memmap_table; -unsigned memmap_entries; - -ram_addr_t initrd_offset; -uint64_t initrd_size; +/* + * Linux Image Format + * https://docs.kernel.org/arch/loongarch/booting.html + */ +#define LINUX_PE_MAGIC 0x818223cd +#define MZ_MAGIC 0x5a4d /* "MZ" */ + +struct loongarch_linux_hdr { + uint32_t mz_magic; + uint32_t res0; + uint64_t kernel_entry; + uint64_t kernel_size; + uint64_t load_offset; + uint64_t res1; + uint64_t res2; + uint64_t res3; + uint32_t linux_pe_magic; + uint32_t pe_header_offset; +} QEMU_PACKED; static const unsigned int slave_boot_code[] = { /* Configure reset ebase. */ @@ -74,12 +88,16 @@ static inline void *guidcpy(void *dst, const void *src) return memcpy(dst, src, sizeof(efi_guid_t)); } -static void init_efi_boot_memmap(struct efi_system_table *systab, +static void init_efi_boot_memmap(MachineState *ms, + struct efi_system_table *systab, void *p, void *start) { unsigned i; struct efi_boot_memmap *boot_memmap = p; efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + struct memmap_entry *memmap_table; + unsigned int memmap_entries; /* efi_configuration_table 1 */ guidcpy(&systab->tables[0].guid, &tbl_guid); @@ -91,6 +109,8 @@ static void init_efi_boot_memmap(struct efi_system_table *systab, boot_memmap->map_size = 0; efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap); + memmap_table = lvms->memmap_table; + memmap_entries = lvms->memmap_entries; for (i = 0; i < memmap_entries; i++) { map = (void *)boot_memmap + sizeof(*map); map[i].type = memmap_table[i].type; @@ -101,7 +121,8 @@ static void init_efi_boot_memmap(struct efi_system_table *systab, } } -static void init_efi_initrd_table(struct efi_system_table *systab, +static void init_efi_initrd_table(struct loongarch_boot_info *info, + struct efi_system_table *systab, void *p, void *start) { efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; @@ -112,8 +133,8 @@ static void init_efi_initrd_table(struct efi_system_table *systab, systab->tables[1].table = (struct efi_configuration_table *)(p - start); systab->nr_tables = 2; - initrd_table->base = initrd_offset; - initrd_table->size = initrd_size; + initrd_table->base = info->initrd_addr; + initrd_table->size = info->initrd_size; } static void init_efi_fdt_table(struct efi_system_table *systab) @@ -126,10 +147,12 @@ static void init_efi_fdt_table(struct efi_system_table *systab) systab->nr_tables = 3; } -static void init_systab(struct loongarch_boot_info *info, void *p, void *start) +static void init_systab(MachineState *ms, + struct loongarch_boot_info *info, void *p, void *start) { void *bp_tables_start; struct efi_system_table *systab = p; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); info->a2 = p - start; @@ -146,10 +169,10 @@ static void init_systab(struct loongarch_boot_info *info, void *p, void *start) systab->tables = p; bp_tables_start = p; - init_efi_boot_memmap(systab, p, start); + init_efi_boot_memmap(ms, systab, p, start); p += ROUND_UP(sizeof(struct efi_boot_memmap) + - sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); - init_efi_initrd_table(systab, p, start); + sizeof(efi_memory_desc_t) * lvms->memmap_entries, 64 * KiB); + init_efi_initrd_table(info, systab, p, start); p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); init_efi_fdt_table(systab); @@ -171,16 +194,105 @@ static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); } +static int64_t load_loongarch_linux_image(const char *filename, + uint64_t *kernel_entry, + uint64_t *kernel_low, + uint64_t *kernel_high) +{ + gsize len; + ssize_t size; + uint8_t *buffer; + struct loongarch_linux_hdr *hdr; + + /* Load as raw file otherwise */ + if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) { + return -1; + } + size = len; + + /* Unpack the image if it is a EFI zboot image */ + if (unpack_efi_zboot_image(&buffer, &size) < 0) { + g_free(buffer); + return -1; + } + + hdr = (struct loongarch_linux_hdr *)buffer; + + if (extract32(le32_to_cpu(hdr->mz_magic), 0, 16) != MZ_MAGIC || + le32_to_cpu(hdr->linux_pe_magic) != LINUX_PE_MAGIC) { + g_free(buffer); + return -1; + } + + /* Early kernel versions may have those fields in virtual address */ + *kernel_entry = extract64(le64_to_cpu(hdr->kernel_entry), + 0, TARGET_PHYS_ADDR_SPACE_BITS); + *kernel_low = extract64(le64_to_cpu(hdr->load_offset), + 0, TARGET_PHYS_ADDR_SPACE_BITS); + *kernel_high = *kernel_low + size; + + rom_add_blob_fixed(filename, buffer, size, *kernel_low); + + g_free(buffer); + + return size; +} + +static ram_addr_t alloc_initrd_memory(struct loongarch_boot_info *info, + uint64_t advice_start, ssize_t rd_size) +{ + hwaddr base, ram_size, gap, low_end; + ram_addr_t initrd_end, initrd_start; + + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + initrd_start = advice_start; + initrd_end = initrd_start + rd_size; + + ram_size = info->ram_size; + low_end = base + MIN(ram_size, gap); + if (initrd_end <= low_end) { + return initrd_start; + } + + if (ram_size <= gap) { + error_report("The low memory too small for initial ram disk '%s'," + "You need to expand the ram", + info->initrd_filename); + exit(1); + } + + /* + * Try to load initrd in the high memory + */ + ram_size -= gap; + initrd_start = VIRT_HIGHMEM_BASE; + if (rd_size <= ram_size) { + return initrd_start; + } + + error_report("The high memory too small for initial ram disk '%s'," + "You need to expand the ram", + info->initrd_filename); + exit(1); +} + static int64_t load_kernel_info(struct loongarch_boot_info *info) { - uint64_t kernel_entry, kernel_low, kernel_high; - ssize_t kernel_size; + uint64_t kernel_entry, kernel_low, kernel_high, initrd_offset = 0; + ssize_t kernel_size, initrd_size; kernel_size = load_elf(info->kernel_filename, NULL, cpu_loongarch_virt_to_phys, NULL, &kernel_entry, &kernel_low, - &kernel_high, NULL, 0, + &kernel_high, NULL, ELFDATA2LSB, EM_LOONGARCH, 1, 0); + kernel_entry = cpu_loongarch_virt_to_phys(NULL, kernel_entry); + if (kernel_size < 0) { + kernel_size = load_loongarch_linux_image(info->kernel_filename, + &kernel_entry, &kernel_low, + &kernel_high); + } if (kernel_size < 0) { error_report("could not load kernel '%s': %s", @@ -193,15 +305,10 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) initrd_size = get_image_size(info->initrd_filename); if (initrd_size > 0) { initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); - - if (initrd_offset + initrd_size > info->ram_size) { - error_report("memory too small for initial ram disk '%s'", - info->initrd_filename); - exit(1); - } - - initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, - info->ram_size - initrd_offset); + initrd_offset = alloc_initrd_memory(info, initrd_offset, + initrd_size); + initrd_size = load_image_targphys(info->initrd_filename, + initrd_offset, initrd_size); } if (initrd_size == (target_ulong)-1) { @@ -209,8 +316,9 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) info->initrd_filename); exit(1); } - } else { - initrd_size = 0; + + info->initrd_addr = initrd_offset; + info->initrd_size = initrd_size; } return kernel_entry; @@ -223,7 +331,7 @@ static void reset_load_elf(void *opaque) cpu_reset(CPU(cpu)); if (env->load_elf) { - if (cpu == LOONGARCH_CPU(first_cpu)) { + if (cpu == LOONGARCH_CPU(first_cpu)) { env->gpr[4] = env->boot_info->a0; env->gpr[5] = env->boot_info->a1; env->gpr[6] = env->boot_info->a2; @@ -265,36 +373,37 @@ static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms, fw_cfg_add_kernel_info(info, lvms->fw_cfg); } -static void init_boot_rom(struct loongarch_boot_info *info, void *p) +static void init_boot_rom(MachineState *ms, + struct loongarch_boot_info *info, void *p) { void *start = p; init_cmdline(info, p, start); p += COMMAND_LINE_SIZE; - init_systab(info, p, start); + init_systab(ms, info, p, start); } -static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) +static void loongarch_direct_kernel_boot(MachineState *ms, + struct loongarch_boot_info *info) { void *p, *bp; - int64_t kernel_addr = 0; + int64_t kernel_addr = VIRT_FLASH0_BASE; LoongArchCPU *lacpu; CPUState *cs; if (info->kernel_filename) { kernel_addr = load_kernel_info(info); } else { - if(!qtest_enabled()) { - error_report("Need kernel filename\n"); - exit(1); + if (!qtest_enabled()) { + warn_report("No kernel provided, booting from flash drive."); } } /* Load cmdline and system tables at [0 - 1 MiB] */ p = g_malloc0(1 * MiB); bp = p; - init_boot_rom(info, p); + init_boot_rom(ms, info, p); rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory); /* Load slave boot code at pflash0 . */ @@ -334,6 +443,6 @@ void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) if (lvms->bios_loaded) { loongarch_firmware_boot(lvms, info); } else { - loongarch_direct_kernel_boot(info); + loongarch_direct_kernel_boot(ms, info); } } diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c index 35aeb2d..4935636 100644 --- a/hw/loongarch/fw_cfg.c +++ b/hw/loongarch/fw_cfg.c @@ -9,7 +9,7 @@ #include "hw/loongarch/fw_cfg.h" #include "hw/loongarch/virt.h" #include "hw/nvram/fw_cfg.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index bce7eba..d494d1e 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -1,9 +1,11 @@ loongarch_ss = ss.source_set() loongarch_ss.add(files( - 'fw_cfg.c', 'boot.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')) +common_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('fw_cfg.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 72bfc35..2cd2d9d 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -18,7 +18,7 @@ #include "hw/acpi/bios-linker-loader.h" #include "migration/vmstate.h" #include "hw/mem/memory-device.h" -#include "sysemu/reset.h" +#include "system/reset.h" /* Supported chipsets: */ #include "hw/pci-host/ls7a.h" @@ -31,7 +31,8 @@ #include "hw/acpi/generic_event_device.h" #include "hw/pci-host/gpex.h" -#include "sysemu/tpm.h" +#include "system/system.h" +#include "system/tpm.h" #include "hw/platform-bus.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/hmat.h" @@ -46,6 +47,22 @@ #define ACPI_BUILD_DPRINTF(fmt, ...) #endif +static void virt_madt_cpu_entry(int uid, + const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled) +{ + uint32_t flags, apic_id = apic_ids->cpus[uid].arch_id; + + flags = apic_ids->cpus[uid].cpu || force_enabled ? 1 /* Enabled */ : 0; + + /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */ + build_append_int_noprefix(entry, 0, 1); /* Type */ + build_append_int_noprefix(entry, 8, 1); /* Length */ + build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */ + build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */ + build_append_int_noprefix(entry, flags, 4); /* Flags */ +} + /* build FADT */ static void init_common_fadt_data(AcpiFadtData *data) { @@ -111,7 +128,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, MachineState *ms = MACHINE(lvms); MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); - int i, arch_id; + int i, arch_id, flags; AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, .oem_table_id = lvms->oem_table_id }; @@ -124,13 +141,13 @@ build_madt(GArray *table_data, BIOSLinker *linker, for (i = 0; i < arch_ids->len; i++) { /* Processor Core Interrupt Controller Structure */ arch_id = arch_ids->cpus[i].arch_id; - + flags = arch_ids->cpus[i].cpu ? 1 : 0; build_append_int_noprefix(table_data, 17, 1); /* Type */ build_append_int_noprefix(table_data, 15, 1); /* Length */ build_append_int_noprefix(table_data, 1, 1); /* Version */ build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ - build_append_int_noprefix(table_data, 1, 4); /* Flags */ + build_append_int_noprefix(table_data, flags, 4); /* Flags */ } /* Extend I/O Interrupt Controller Structure */ @@ -218,7 +235,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) */ if (len >= gap) { - build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED); + build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED); len -= gap; base = VIRT_HIGHMEM_BASE; gap = machine->ram_size - VIRT_LOWMEM_SIZE; @@ -241,6 +258,48 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) acpi_table_end(linker, &table); } +/* + * Serial Port Console Redirection Table (SPCR) + * https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table + */ +static void +spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine) +{ + LoongArchVirtMachineState *lvms; + AcpiSpcrData serial = { + .interface_type = 0, /* 16550 compatible */ + .base_addr.id = AML_AS_SYSTEM_MEMORY, + .base_addr.width = 32, + .base_addr.offset = 0, + .base_addr.size = 1, + .base_addr.addr = VIRT_UART_BASE, + .interrupt_type = 0, /* Interrupt not supported */ + .pc_interrupt = 0, + .interrupt = VIRT_UART_IRQ, + .baud_rate = 7, /* 115200 */ + .parity = 0, + .stop_bits = 1, + .flow_control = 0, + .terminal_type = 3, /* ANSI */ + .language = 0, /* Language */ + .pci_device_id = 0xffff, /* not a PCI device*/ + .pci_vendor_id = 0xffff, /* not a PCI device*/ + .pci_bus = 0, + .pci_device = 0, + .pci_function = 0, + .pci_flags = 0, + .pci_segment = 0, + }; + + lvms = LOONGARCH_VIRT_MACHINE(machine); + /* + * Passing NULL as the SPCR Table for Revision 2 doesn't support + * NameSpaceString. + */ + build_spcr(table_data, linker, &serial, 2, lvms->oem_id, + lvms->oem_table_id, NULL); +} + typedef struct AcpiBuildState { /* Copy of table in RAM (for patching). */ @@ -252,23 +311,27 @@ struct AcpiBuildState { MemoryRegion *linker_mr; } AcpiBuildState; -static void build_uart_device_aml(Aml *table) +static void build_uart_device_aml(Aml *table, int index) { Aml *dev; Aml *crs; Aml *pkg0, *pkg1, *pkg2; - uint32_t uart_irq = VIRT_UART_IRQ; - - Aml *scope = aml_scope("_SB"); - dev = aml_device("COMA"); + Aml *scope; + uint32_t uart_irq; + uint64_t base; + + uart_irq = VIRT_UART_IRQ + index; + base = VIRT_UART_BASE + index * VIRT_UART_SIZE; + scope = aml_scope("_SB"); + dev = aml_device("COM%d", index); aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(index))); aml_append(dev, aml_name_decl("_CCA", aml_int(1))); crs = aml_resource_template(); aml_append(crs, aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, AML_NON_CACHEABLE, AML_READ_WRITE, - 0, VIRT_UART_BASE, VIRT_UART_BASE + VIRT_UART_SIZE - 1, + 0, base, base + VIRT_UART_SIZE - 1, 0, VIRT_UART_SIZE)); aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, AML_SHARED, &uart_irq, 1)); @@ -291,6 +354,7 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) { uint32_t event; LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + CPUHotplugFeatures opts; build_ged_aml(dsdt, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(lvms->acpi_ged), @@ -303,6 +367,18 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) AML_SYSTEM_MEMORY, VIRT_GED_MEM_ADDR); } + + if (event & ACPI_GED_CPU_HOTPLUG_EVT) { + opts.acpi_1_compatible = false; + opts.has_legacy_cphp = false; + opts.fw_unplugs_cpu = false; + opts.smi_path = NULL; + + build_cpus_aml(dsdt, machine, opts, virt_madt_cpu_entry, + VIRT_GED_CPUHP_ADDR, "\\_SB", + AML_GED_EVT_CPU_SCAN_METHOD, AML_SYSTEM_MEMORY); + } + acpi_dsdt_add_power_button(dsdt); } @@ -401,6 +477,7 @@ static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms) static void build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) { + int i; Aml *dsdt, *scope, *pkg; LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id, @@ -408,7 +485,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); - build_uart_device_aml(dsdt); + for (i = 0; i < VIRT_UART_COUNT; i++) { + build_uart_device_aml(dsdt, i); + } build_pci_device_aml(dsdt, lvms); build_la_ged_aml(dsdt, machine); build_flash_aml(dsdt, lvms); @@ -435,7 +514,7 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); GArray *table_offsets; AcpiFadtData fadt_data; - unsigned facs, rsdt, dsdt; + unsigned facs, xsdt, dsdt; uint8_t *u; GArray *tables_blob = tables->table_data; @@ -477,6 +556,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, machine); + acpi_add_table(table_offsets, tables_blob); + spcr_setup(tables_blob, tables->linker, machine); if (machine->numa_state->num_nodes) { if (machine->numa_state->have_numa_distance) { @@ -494,8 +575,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); { AcpiMcfgInfo mcfg = { - .base = cpu_to_le64(VIRT_PCI_CFG_BASE), - .size = cpu_to_le64(VIRT_PCI_CFG_SIZE), + .base = VIRT_PCI_CFG_BASE, + .size = VIRT_PCI_CFG_SIZE, }; build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id, lvms->oem_table_id); @@ -519,17 +600,17 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) } /* RSDT is pointed to by RSDP */ - rsdt = tables_blob->len; - build_rsdt(tables_blob, tables->linker, table_offsets, + xsdt = tables_blob->len; + build_xsdt(tables_blob, tables->linker, table_offsets, lvms->oem_id, lvms->oem_table_id); /* RSDP is in FSEG memory, so allocate it separately */ { AcpiRsdpData rsdp_data = { - .revision = 0, + .revision = 2, .oem_id = lvms->oem_id, - .xsdt_tbl_offset = NULL, - .rsdt_tbl_offset = &rsdt, + .xsdt_tbl_offset = &xsdt, + .rsdt_tbl_offset = NULL, }; build_rsdp(tables->rsdp, tables->linker, &rsdp_data); } @@ -604,7 +685,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; @@ -612,7 +693,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; @@ -622,7 +703,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..728ce46 --- /dev/null +++ b/hw/loongarch/virt-fdt-build.c @@ -0,0 +1,534 @@ +/* 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. + */ + 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 e592b1b..b15ada2 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -8,22 +8,23 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/boards.h" -#include "hw/char/serial.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" -#include "sysemu/rtc.h" +#include "hw/char/serial-mm.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" +#include "system/rtc.h" #include "hw/loongarch/virt.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/irq.h" #include "net/net.h" #include "hw/loader.h" #include "elf.h" -#include "hw/intc/loongson_ipi.h" +#include "hw/intc/loongarch_ipi.h" #include "hw/intc/loongarch_extioi.h" #include "hw/intc/loongarch_pch_pic.h" #include "hw/intc/loongarch_pch_msi.h" @@ -33,30 +34,19 @@ #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 "sysemu/device_tree.h" -#include <libfdt.h> -#include "hw/core/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/display/ramfb.h" +#include "hw/uefi/var-service-api.h" #include "hw/mem/pc-dimm.h" -#include "sysemu/tpm.h" -#include "sysemu/block-backend.h" +#include "system/tpm.h" +#include "system/block-backend.h" #include "hw/block/flash.h" #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" -static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) -{ - if (lvms->veiointc == ON_OFF_AUTO_OFF) { - return false; - } - return true; -} - static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -134,416 +124,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_uart_node(LoongArchVirtMachineState *lvms, - uint32_t *pch_pic_phandle) -{ - char *nodename; - hwaddr base = VIRT_UART_BASE; - 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); - qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); - qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", - VIRT_UART_IRQ - VIRT_GSI_BASE, 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); - - 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"); -} - -static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) -{ - int num; - const MachineState *ms = MACHINE(lvms); - int smp_cpus = ms->smp.cpus; - - 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 */ - for (num = smp_cpus - 1; num >= 0; num--) { - char *nodename = g_strdup_printf("/cpus/cpu@%d", num); - LoongArchCPU *cpu = LOONGARCH_CPU(qemu_get_cpu(num)); - CPUState *cs = CPU(cpu); - - 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 (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) { - qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", - ms->possible_cpus->cpus[cs->cpu_index].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 = smp_cpus - 1; num >= 0; num--) { - char *cpu_path = g_strdup_printf("/cpus/cpu@%d", num); - char *map_path; - - 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", cpu_path); - - g_free(map_path); - g_free(cpu_path); - } -} - -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[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 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 < GPEX_NUM_IRQS; dev++) { - int devfn = dev * 0x8; - - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { - int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); - 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, - GPEX_NUM_IRQS * GPEX_NUM_IRQS * - 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); @@ -556,6 +136,10 @@ static void virt_build_smbios(LoongArchVirtMachineState *lvms) return; } + if (kvm_enabled()) { + product = "KVM Virtual Machine"; + } + smbios_set_defaults("QEMU", product, mc->name); smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, @@ -576,7 +160,8 @@ 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); } static void virt_powerdown_req(Notifier *notifier, void *opaque) @@ -587,8 +172,15 @@ static void virt_powerdown_req(Notifier *notifier, void *opaque) acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); } -static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) +static void memmap_add_entry(MachineState *ms, uint64_t address, + uint64_t length, uint32_t type) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + struct memmap_entry *memmap_table; + unsigned int memmap_entries; + + memmap_table = lvms->memmap_table; + memmap_entries = lvms->memmap_entries; /* Ensure there are no duplicate entries. */ for (unsigned i = 0; i < memmap_entries; i++) { assert(memmap_table[i].address != address); @@ -601,6 +193,8 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) memmap_table[memmap_entries].type = cpu_to_le32(type); memmap_table[memmap_entries].reserved = 0; memmap_entries++; + lvms->memmap_table = memmap_table; + lvms->memmap_entries = memmap_entries; } static DeviceState *create_acpi_ged(DeviceState *pch_pic, @@ -608,11 +202,17 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, { DeviceState *dev; MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(lvms); uint32_t event = ACPI_GED_PWR_DOWN_EVT; if (ms->ram_slots) { event |= ACPI_GED_MEM_HOTPLUG_EVT; } + + if (mc->has_hotpluggable_cpus) { + event |= ACPI_GED_CPU_HOTPLUG_EVT; + } + dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -624,6 +224,10 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, /* ged regs used for reset and power down */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR); + if (mc->has_hotpluggable_cpus) { + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, VIRT_GED_CPUHP_ADDR); + } + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE)); return dev; @@ -655,9 +259,7 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic) } static void virt_devices_init(DeviceState *pch_pic, - LoongArchVirtMachineState *lvms, - uint32_t *pch_pic_phandle, - uint32_t *pch_msi_phandle) + LoongArchVirtMachineState *lvms) { MachineClass *mc = MACHINE_GET_CLASS(lvms); DeviceState *gpex_dev; @@ -697,20 +299,23 @@ static void virt_devices_init(DeviceState *pch_pic, memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE, pio_alias); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(d, i, qdev_get_gpio_in(pch_pic, 16 + i)); gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); } - /* Add pcie node */ - fdt_add_pcie_node(lvms, pch_pic_phandle, pch_msi_phandle); - - serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0, - qdev_get_gpio_in(pch_pic, - VIRT_UART_IRQ - VIRT_GSI_BASE), - 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); - fdt_add_uart_node(lvms, pch_pic_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; + serial_mm_init(get_system_memory(), base, 0, + qdev_get_gpio_in(pch_pic, irq), + 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN); + } /* Network init */ pci_init_nic_devices(pci_bus, mc->default_nic); @@ -723,7 +328,6 @@ static void virt_devices_init(DeviceState *pch_pic, sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE, qdev_get_gpio_in(pch_pic, VIRT_RTC_IRQ - VIRT_GSI_BASE)); - fdt_add_rtc_node(lvms, pch_pic_phandle); /* acpi ged */ lvms->acpi_ged = create_acpi_ged(pch_pic, lvms); @@ -731,17 +335,35 @@ 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; 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; + } + + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), + &error_abort); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), + &error_abort); + } +} + +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; - uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; + int i, start, num; /* * Extended IRQ model. @@ -788,81 +410,24 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) */ /* Create IPI device */ - ipi = qdev_new(TYPE_LOONGSON_IPI); - qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus); + ipi = qdev_new(TYPE_LOONGARCH_IPI); + lvms->ipi = ipi; sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); - /* IPI iocsr memory region */ - memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, - sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); - memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, - sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); - - /* Add cpu interrupt-controller */ - fdt_add_cpuic_node(lvms, &cpuintc_phandle); - - 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); - qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); + lvms->extioi = extioi; if (virt_is_veiointc_enabled(lvms)) { qdev_prop_set_bit(extioi, "has-virtualization-extension", true); } sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); - memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, - sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); - if (virt_is_veiointc_enabled(lvms)) { - memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, - 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)); - } - } - - /* Add Extend I/O Interrupt Controller node */ - fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); - - pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); + 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); d = SYS_BUS_DEVICE(pch_pic); sysbus_realize_and_unref(d, &error_fatal); - memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, - sysbus_mmio_get_region(d, 0)); - memory_region_add_subregion(get_system_memory(), - VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET, - sysbus_mmio_get_region(d, 1)); - memory_region_add_subregion(get_system_memory(), - VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO, - sysbus_mmio_get_region(d, 2)); - - /* Connect pch_pic irqs to extioi */ - for (i = 0; i < num; i++) { - qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); - } - - /* Add PCH PIC node */ - fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); start = num; @@ -872,16 +437,41 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) d = SYS_BUS_DEVICE(pch_msi); sysbus_realize_and_unref(d, &error_fatal); sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); - for (i = 0; i < num; i++) { - /* Connect pch_msi irqs to extioi */ - qdev_connect_gpio_out(DEVICE(d), i, - qdev_get_gpio_in(extioi, i + start)); - } - /* Add PCH MSI node */ - fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); + if (kvm_irqchip_in_kernel()) { + kvm_loongarch_init_irq_routing(); + } else { + /* IPI iocsr memory region */ + memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); + memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + + /* EXTIOI iocsr memory region */ + memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); + if (virt_is_veiointc_enabled(lvms)) { + memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); + } + + /* PCH_PIC memory region */ + memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(pch_pic), 0)); + + /* Connect pch_pic irqs to extioi */ + for (i = 0; i < VIRT_PCH_PIC_IRQ_NUM; i++) { + qdev_connect_gpio_out(DEVICE(pch_pic), i, + qdev_get_gpio_in(extioi, i)); + } - virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle); + for (i = VIRT_PCH_PIC_IRQ_NUM; i < EXTIOI_IRQS; i++) { + /* Connect pch_msi irqs to extioi */ + qdev_connect_gpio_out(DEVICE(pch_msi), i - VIRT_PCH_PIC_IRQ_NUM, + qdev_get_gpio_in(extioi, i)); + } + } + virt_devices_init(pch_pic, lvms); } static void virt_firmware_init(LoongArchVirtMachineState *lvms) @@ -941,6 +531,10 @@ static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, switch (addr) { case MISC_FUNC_REG: + if (kvm_irqchip_in_kernel()) { + return MEMTX_OK; + } + if (!virt_is_veiointc_enabled(lvms)) { return MEMTX_OK; } @@ -991,6 +585,10 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, ret = 0x303030354133ULL; /* "3A5000" */ break; case MISC_FUNC_REG: + if (kvm_irqchip_in_kernel()) { + return MEMTX_OK; + } + if (!virt_is_veiointc_enabled(lvms)) { ret |= BIT_ULL(IOCSRM_EXTIOI_EN); break; @@ -1051,13 +649,13 @@ static void fw_cfg_add_memory(MachineState *ms) } if (size >= gap) { - memmap_add_entry(base, gap, 1); + memmap_add_entry(ms, base, gap, 1); size -= gap; base = VIRT_HIGHMEM_BASE; } if (size) { - memmap_add_entry(base, size, 1); + memmap_add_entry(ms, base, size, 1); base += size; } @@ -1072,35 +670,32 @@ static void fw_cfg_add_memory(MachineState *ms) * lowram: [base, +(gap - numa_info[0].node_mem)) * highram: [VIRT_HIGHMEM_BASE, +(ram_size - gap)) */ - memmap_add_entry(base, gap - numa_info[0].node_mem, 1); + memmap_add_entry(ms, base, gap - numa_info[0].node_mem, 1); size = ram_size - gap; base = VIRT_HIGHMEM_BASE; } else { size = ram_size - numa_info[0].node_mem; } - if (size) - memmap_add_entry(base, size, 1); + if (size) { + memmap_add_entry(ms, base, size, 1); + } } static void virt_init(MachineState *machine) { - LoongArchCPU *lacpu; const char *cpu_model = machine->cpu_type; MemoryRegion *address_space_mem = get_system_memory(); LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); int i; hwaddr base, size, ram_size = machine->ram_size; - const CPUArchIdList *possible_cpus; MachineClass *mc = MACHINE_GET_CLASS(machine); - CPUState *cpu; + Object *cpuobj; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); } - create_fdt(lvms); - /* Create IOCSR space */ memory_region_init_io(&lvms->system_iocsr, OBJECT(machine), NULL, machine, "iocsr", UINT64_MAX); @@ -1111,16 +706,16 @@ static void virt_init(MachineState *machine) memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem); /* Init CPUs */ - possible_cpus = mc->possible_cpu_arch_ids(machine); - for (i = 0; i < possible_cpus->len; i++) { - cpu = cpu_create(machine->cpu_type); - cpu->cpu_index = i; - machine->possible_cpus->cpus[i].cpu = cpu; - lacpu = LOONGARCH_CPU(cpu); - lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; + mc->possible_cpu_arch_ids(machine); + for (i = 0; i < machine->smp.cpus; i++) { + cpuobj = object_new(machine->cpu_type); + if (cpuobj == NULL) { + error_report("Fail to create object with type %s ", + machine->cpu_type); + exit(EXIT_FAILURE); + } + qdev_realize_and_unref(DEVICE(cpuobj), NULL, &error_fatal); } - fdt_add_cpu_nodes(lvms); - fdt_add_memory_nodes(machine); fw_cfg_add_memory(machine); /* Node0 memory */ @@ -1169,37 +764,18 @@ static void virt_init(MachineState *machine) rom_set_fw(lvms->fw_cfg); if (lvms->fw_cfg != NULL) { fw_cfg_add_file(lvms->fw_cfg, "etc/memmap", - memmap_table, - sizeof(struct memmap_entry) * (memmap_entries)); + lvms->memmap_table, + sizeof(struct memmap_entry) * lvms->memmap_entries); } - fdt_add_fw_cfg_node(lvms); - fdt_add_flash_node(lvms); /* Initialize the IO interrupt subsystem */ virt_irq_init(lvms); - platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", - VIRT_PLATFORM_BUS_BASEADDRESS, - VIRT_PLATFORM_BUS_SIZE, - VIRT_PLATFORM_BUS_IRQ); lvms->machine_done.notify = virt_done; qemu_add_machine_init_done_notifier(&lvms->machine_done); /* connect powerdown request */ lvms->powerdown_notifier.notify = virt_powerdown_req; qemu_register_powerdown_notifier(&lvms->powerdown_notifier); - /* - * 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)); - lvms->bootinfo.ram_size = ram_size; loongarch_load_kernel(machine, &lvms->bootinfo); } @@ -1221,6 +797,48 @@ static void virt_set_acpi(Object *obj, Visitor *v, const char *name, visit_type_OnOffAuto(v, name, &lvms->acpi, errp); } +static char *virt_get_oem_id(Object *obj, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + return g_strdup(lvms->oem_id); +} + +static void virt_set_oem_id(Object *obj, const char *value, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + size_t len = strlen(value); + + if (len > 6) { + error_setg(errp, + "User specified oem-id value is bigger than 6 bytes in size"); + return; + } + + strncpy(lvms->oem_id, value, 6); +} + +static char *virt_get_oem_table_id(Object *obj, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + return g_strdup(lvms->oem_table_id); +} + +static void virt_set_oem_table_id(Object *obj, const char *value, + Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + size_t len = strlen(value); + + if (len > 8) { + error_setg(errp, + "User specified oem-table-id value is bigger than 8 bytes in size"); + return; + } + strncpy(lvms->oem_table_id, value, 8); +} + static void virt_initfn(Object *obj) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); @@ -1234,6 +852,195 @@ static void virt_initfn(Object *obj) virt_flash_create(lvms); } +static void virt_get_topo_from_index(MachineState *ms, + LoongArchCPUTopo *topo, int index) +{ + topo->socket_id = index / (ms->smp.cores * ms->smp.threads); + topo->core_id = index / ms->smp.threads % ms->smp.cores; + topo->thread_id = index % ms->smp.threads; +} + +static unsigned int topo_align_up(unsigned int count) +{ + g_assert(count >= 1); + count -= 1; + return BIT(count ? 32 - clz32(count) : 0); +} + +/* + * LoongArch Reference Manual Vol1, Chapter 7.4.12 CPU Identity + * For CPU architecture, bit0 .. bit8 is valid for CPU id, max cpuid is 512 + * However for IPI/Eiointc interrupt controller, max supported cpu id for + * irq routingis 256 + * + * Here max cpu id is 256 for virt machine + */ +static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) +{ + int arch_id, threads, cores, sockets; + + threads = topo_align_up(ms->smp.threads); + cores = topo_align_up(ms->smp.cores); + sockets = topo_align_up(ms->smp.sockets); + if ((threads * cores * sockets) > 256) { + error_report("Exceeding max cpuid 256 with sockets[%d] cores[%d]" + " threads[%d]", ms->smp.sockets, ms->smp.cores, + ms->smp.threads); + exit(1); + } + + arch_id = topo->thread_id + topo->core_id * threads; + arch_id += topo->socket_id * threads * cores; + return arch_id; +} + +/* Find cpu slot in machine->possible_cpus by arch_id */ +static CPUArchId *virt_find_cpu_slot(MachineState *ms, int arch_id) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].arch_id == arch_id) { + return &ms->possible_cpus->cpus[n]; + } + } + + return NULL; +} + +/* Find cpu slot for cold-plut CPU object where cpu is NULL */ +static CPUArchId *virt_find_empty_cpu_slot(MachineState *ms) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].cpu == NULL) { + return &ms->possible_cpus->cpus[n]; + } + } + + return NULL; +} + +static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + MachineState *ms = MACHINE(OBJECT(hotplug_dev)); + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + CPUArchId *cpu_slot; + LoongArchCPUTopo topo; + int arch_id; + + if (lvms->acpi_ged) { + if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) { + error_setg(errp, + "Invalid thread-id %u specified, must be in range 1:%u", + cpu->thread_id, ms->smp.threads - 1); + return; + } + + if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) { + error_setg(errp, + "Invalid core-id %u specified, must be in range 1:%u", + cpu->core_id, ms->smp.cores - 1); + return; + } + + if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) { + error_setg(errp, + "Invalid socket-id %u specified, must be in range 1:%u", + cpu->socket_id, ms->smp.sockets - 1); + return; + } + + topo.socket_id = cpu->socket_id; + topo.core_id = cpu->core_id; + topo.thread_id = cpu->thread_id; + arch_id = virt_get_arch_id_from_topo(ms, &topo); + cpu_slot = virt_find_cpu_slot(ms, arch_id); + if (CPU(cpu_slot->cpu)) { + error_setg(errp, + "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists", + cs->cpu_index, cpu->socket_id, cpu->core_id, + cpu->thread_id, cpu_slot->arch_id); + return; + } + } else { + /* For cold-add cpu, find empty cpu slot */ + cpu_slot = virt_find_empty_cpu_slot(ms); + topo.socket_id = cpu_slot->props.socket_id; + topo.core_id = cpu_slot->props.core_id; + topo.thread_id = cpu_slot->props.thread_id; + object_property_set_int(OBJECT(dev), "socket-id", topo.socket_id, NULL); + object_property_set_int(OBJECT(dev), "core-id", topo.core_id, NULL); + object_property_set_int(OBJECT(dev), "thread-id", topo.thread_id, NULL); + } + + cpu->env.address_space_iocsr = &lvms->as_iocsr; + cpu->phy_id = cpu_slot->arch_id; + cs->cpu_index = cpu_slot - ms->possible_cpus->cpus; + numa_cpu_pre_plug(cpu_slot, dev, errp); +} + +static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + + if (cs->cpu_index == 0) { + error_setg(errp, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", + cs->cpu_index, cpu->socket_id, + cpu->core_id, cpu->thread_id); + return; + } + + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); +} + +static void virt_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *cpu_slot; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + + /* Notify ipi and extioi irqchip to remove interrupt routing to CPU */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); + + /* Notify acpi ged CPU removed */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = NULL; +} + +static void virt_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *cpu_slot; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + + if (lvms->ipi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); + } + + if (lvms->extioi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); + } + + if (lvms->acpi_ged) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, + &error_abort); + } + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = CPU(dev); +} + static bool memhp_type_supported(DeviceState *dev) { /* we only support pc dimm now */ @@ -1252,6 +1059,8 @@ static void virt_device_pre_plug(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_pre_plug(hotplug_dev, dev, errp); } } @@ -1270,6 +1079,8 @@ static void virt_device_unplug_request(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug_request(hotplug_dev, dev, errp); } } @@ -1288,6 +1099,8 @@ static void virt_device_unplug(HotplugHandler *hotplug_dev, { if (memhp_type_supported(dev)) { virt_mem_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug(hotplug_dev, dev, errp); } } @@ -1315,6 +1128,8 @@ static void virt_device_plug_cb(HotplugHandler *hotplug_dev, } } else if (memhp_type_supported(dev)) { virt_mem_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_plug(hotplug_dev, dev, errp); } } @@ -1324,6 +1139,7 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (device_is_dynamic_sysbus(mc, dev) || + object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || memhp_type_supported(dev)) { return HOTPLUG_HANDLER(machine); @@ -1333,8 +1149,9 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) { - int n; + int n, arch_id; unsigned int max_cpus = ms->smp.max_cpus; + LoongArchCPUTopo topo; if (ms->possible_cpus) { assert(ms->possible_cpus->len == max_cpus); @@ -1345,17 +1162,17 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) sizeof(CPUArchId) * max_cpus); ms->possible_cpus->len = max_cpus; for (n = 0; n < ms->possible_cpus->len; n++) { + virt_get_topo_from_index(ms, &topo, n); + arch_id = virt_get_arch_id_from_topo(ms, &topo); ms->possible_cpus->cpus[n].type = ms->cpu_type; - ms->possible_cpus->cpus[n].arch_id = n; - + ms->possible_cpus->cpus[n].arch_id = arch_id; + ms->possible_cpus->cpus[n].vcpus_count = 1; ms->possible_cpus->cpus[n].props.has_socket_id = true; - ms->possible_cpus->cpus[n].props.socket_id = - n / (ms->smp.cores * ms->smp.threads); + ms->possible_cpus->cpus[n].props.socket_id = topo.socket_id; ms->possible_cpus->cpus[n].props.has_core_id = true; - ms->possible_cpus->cpus[n].props.core_id = - n / ms->smp.threads % ms->smp.cores; + ms->possible_cpus->cpus[n].props.core_id = topo.core_id; ms->possible_cpus->cpus[n].props.has_thread_id = true; - ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads; + ms->possible_cpus->cpus[n].props.thread_id = topo.thread_id; } return ms->possible_cpus; } @@ -1382,7 +1199,7 @@ static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) } } -static void virt_class_init(ObjectClass *oc, void *data) +static void virt_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -1390,6 +1207,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->init = virt_init; mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); mc->default_ram_id = "loongarch.ram"; + mc->desc = "QEMU LoongArch Virtual Machine"; mc->max_cpus = LOONGARCH_MAX_CPUS; mc->is_default = 1; mc->default_kernel_irqchip_split = false; @@ -1402,6 +1220,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->numa_mem_supported = true; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; + mc->has_hotpluggable_cpus = true; mc->get_hotplug_handler = virt_get_hotplug_handler; mc->default_nic = "virtio-net-pci"; hc->plug = virt_device_plug_cb; @@ -1420,9 +1239,26 @@ static void virt_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "v-eiointc", "Enable Virt Extend I/O Interrupt Controller."); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif + object_class_property_add_str(oc, "x-oem-id", + virt_get_oem_id, + virt_set_oem_id); + object_class_property_set_description(oc, "x-oem-id", + "Override the default value of field OEMID " + "in ACPI table header." + "The string may be up to 6 bytes in size"); + + + object_class_property_add_str(oc, "x-oem-table-id", + virt_get_oem_table_id, + virt_set_oem_table_id); + object_class_property_set_description(oc, "x-oem-table-id", + "Override the default value of field OEM Table ID " + "in ACPI table header." + "The string may be up to 8 bytes in size"); } static const TypeInfo virt_machine_types[] = { @@ -1432,7 +1268,7 @@ static const TypeInfo virt_machine_types[] = { .instance_size = sizeof(LoongArchVirtMachineState), .class_init = virt_class_init, .instance_init = virt_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, |