 65cb7129f4
			
		
	
	
		65cb7129f4
		
	
	
	
	
		
			
			- Ignore writes to CNTP_CTL_EL0 on HVF ARM (Alexander)
 - Add '-d invalid_mem' logging option (Zoltan)
 - Create QOM containers explicitly (Peter)
 - Rename sysemu/ -> system/ (Philippe)
 - Re-orderning of include/exec/ headers (Philippe)
   Move a lot of declarations from these legacy mixed bag headers:
     . "exec/cpu-all.h"
     . "exec/cpu-common.h"
     . "exec/cpu-defs.h"
     . "exec/exec-all.h"
     . "exec/translate-all"
   to these more specific ones:
     . "exec/page-protection.h"
     . "exec/translation-block.h"
     . "user/cpu_loop.h"
     . "user/guest-host.h"
     . "user/page-protection.h"
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmdlnyAACgkQ4+MsLN6t
 wN6mBw//QFWi7CrU+bb8KMM53kOU9C507tjn99LLGFb5or73/umDsw6eo/b8DHBt
 KIwGLgATel42oojKfNKavtAzLK5rOrywpboPDpa3SNeF1onW+99NGJ52LQUqIX6K
 A6bS0fPdGG9ZzEuPpbjDXlp++0yhDcdSgZsS42fEsT7Dyj5gzJYlqpqhiXGqpsn8
 4Y0UMxSL21K3HEexlzw2hsoOBFA3tUm2ujNDhNkt8QASr85yQVLCypABJnuoe///
 5Ojl5wTBeDwhANET0rhwHK8eIYaNboiM9fHopJYhvyw1bz6yAu9jQwzF/MrL3s/r
 xa4OBHBy5mq2hQV9Shcl3UfCQdk/vDaYaWpgzJGX8stgMGYfnfej1SIl8haJIfcl
 VMX8/jEFdYbjhO4AeGRYcBzWjEJymkDJZoiSWp2NuEDi6jqIW+7yW1q0Rnlg9lay
 ShAqLK5Pv4zUw3t0Jy3qv9KSW8sbs6PQxtzXjk8p97rTf76BJ2pF8sv1tVzmsidP
 9L92Hv5O34IqzBu2oATOUZYJk89YGmTIUSLkpT7asJZpBLwNM2qLp5jO00WVU0Sd
 +kAn324guYPkko/TVnjC/AY7CMu55EOtD9NU35k3mUAnxXT9oDUeL4NlYtfgrJx6
 x1Nzr2FkS68+wlPAFKNSSU5lTjsjNaFM0bIJ4LCNtenJVP+SnRo=
 =cjz8
 -----END PGP SIGNATURE-----
Merge tag 'exec-20241220' of https://github.com/philmd/qemu into staging
Accel & Exec patch queue
- Ignore writes to CNTP_CTL_EL0 on HVF ARM (Alexander)
- Add '-d invalid_mem' logging option (Zoltan)
- Create QOM containers explicitly (Peter)
- Rename sysemu/ -> system/ (Philippe)
- Re-orderning of include/exec/ headers (Philippe)
  Move a lot of declarations from these legacy mixed bag headers:
    . "exec/cpu-all.h"
    . "exec/cpu-common.h"
    . "exec/cpu-defs.h"
    . "exec/exec-all.h"
    . "exec/translate-all"
  to these more specific ones:
    . "exec/page-protection.h"
    . "exec/translation-block.h"
    . "user/cpu_loop.h"
    . "user/guest-host.h"
    . "user/page-protection.h"
 # -----BEGIN PGP SIGNATURE-----
 #
 # iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmdlnyAACgkQ4+MsLN6t
 # wN6mBw//QFWi7CrU+bb8KMM53kOU9C507tjn99LLGFb5or73/umDsw6eo/b8DHBt
 # KIwGLgATel42oojKfNKavtAzLK5rOrywpboPDpa3SNeF1onW+99NGJ52LQUqIX6K
 # A6bS0fPdGG9ZzEuPpbjDXlp++0yhDcdSgZsS42fEsT7Dyj5gzJYlqpqhiXGqpsn8
 # 4Y0UMxSL21K3HEexlzw2hsoOBFA3tUm2ujNDhNkt8QASr85yQVLCypABJnuoe///
 # 5Ojl5wTBeDwhANET0rhwHK8eIYaNboiM9fHopJYhvyw1bz6yAu9jQwzF/MrL3s/r
 # xa4OBHBy5mq2hQV9Shcl3UfCQdk/vDaYaWpgzJGX8stgMGYfnfej1SIl8haJIfcl
 # VMX8/jEFdYbjhO4AeGRYcBzWjEJymkDJZoiSWp2NuEDi6jqIW+7yW1q0Rnlg9lay
 # ShAqLK5Pv4zUw3t0Jy3qv9KSW8sbs6PQxtzXjk8p97rTf76BJ2pF8sv1tVzmsidP
 # 9L92Hv5O34IqzBu2oATOUZYJk89YGmTIUSLkpT7asJZpBLwNM2qLp5jO00WVU0Sd
 # +kAn324guYPkko/TVnjC/AY7CMu55EOtD9NU35k3mUAnxXT9oDUeL4NlYtfgrJx6
 # x1Nzr2FkS68+wlPAFKNSSU5lTjsjNaFM0bIJ4LCNtenJVP+SnRo=
 # =cjz8
 # -----END PGP SIGNATURE-----
 # gpg: Signature made Fri 20 Dec 2024 11:45:20 EST
 # gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
 # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [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: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE
* tag 'exec-20241220' of https://github.com/philmd/qemu: (59 commits)
  util/qemu-timer: fix indentation
  meson: Do not define CONFIG_DEVICES on user emulation
  system/accel-ops: Remove unnecessary 'exec/cpu-common.h' header
  system/numa: Remove unnecessary 'exec/cpu-common.h' header
  hw/xen: Remove unnecessary 'exec/cpu-common.h' header
  target/mips: Drop left-over comment about Jazz machine
  target/mips: Remove tswap() calls in semihosting uhi_fstat_cb()
  target/xtensa: Remove tswap() calls in semihosting simcall() helper
  accel/tcg: Un-inline translator_is_same_page()
  accel/tcg: Include missing 'exec/translation-block.h' header
  accel/tcg: Move tcg_cflags_has/set() to 'exec/translation-block.h'
  accel/tcg: Restrict curr_cflags() declaration to 'internal-common.h'
  qemu/coroutine: Include missing 'qemu/atomic.h' header
  exec/translation-block: Include missing 'qemu/atomic.h' header
  accel/tcg: Declare cpu_loop_exit_requested() in 'exec/cpu-common.h'
  exec/cpu-all: Include 'cpu.h' earlier so MMU_USER_IDX is always defined
  target/sparc: Move sparc_restore_state_to_opc() to cpu.c
  target/sparc: Uninline cpu_get_tb_cpu_state()
  target/loongarch: Declare loongarch_cpu_dump_state() locally
  user: Move various declarations out of 'exec/exec-all.h'
  ...
Conflicts:
	hw/char/riscv_htif.c
	hw/intc/riscv_aplic.c
	target/s390x/cpu.c
	Apply sysemu header path changes to not in the pull request.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
		
	
			
		
			
				
	
	
		
			713 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			713 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0-or-later */
 | |
| /*
 | |
|  * Support for generating ACPI tables and passing them to Guests
 | |
|  *
 | |
|  * Copyright (C) 2021 Loongson Technology Corporation Limited
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qapi/error.h"
 | |
| #include "qemu/error-report.h"
 | |
| #include "qemu/bitmap.h"
 | |
| #include "hw/pci/pci.h"
 | |
| #include "hw/core/cpu.h"
 | |
| #include "target/loongarch/cpu.h"
 | |
| #include "hw/acpi/acpi-defs.h"
 | |
| #include "hw/acpi/acpi.h"
 | |
| #include "hw/nvram/fw_cfg.h"
 | |
| #include "hw/acpi/bios-linker-loader.h"
 | |
| #include "migration/vmstate.h"
 | |
| #include "hw/mem/memory-device.h"
 | |
| #include "system/reset.h"
 | |
| 
 | |
| /* Supported chipsets: */
 | |
| #include "hw/pci-host/ls7a.h"
 | |
| #include "hw/loongarch/virt.h"
 | |
| 
 | |
| #include "hw/acpi/utils.h"
 | |
| #include "hw/acpi/pci.h"
 | |
| 
 | |
| #include "qom/qom-qobject.h"
 | |
| 
 | |
| #include "hw/acpi/generic_event_device.h"
 | |
| #include "hw/pci-host/gpex.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"
 | |
| 
 | |
| #define ACPI_BUILD_ALIGN_SIZE             0x1000
 | |
| #define ACPI_BUILD_TABLE_SIZE             0x20000
 | |
| 
 | |
| #ifdef DEBUG_ACPI_BUILD
 | |
| #define ACPI_BUILD_DPRINTF(fmt, ...)        \
 | |
|     do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
 | |
| #else
 | |
| #define ACPI_BUILD_DPRINTF(fmt, ...)
 | |
| #endif
 | |
| 
 | |
| /* build FADT */
 | |
| static void init_common_fadt_data(AcpiFadtData *data)
 | |
| {
 | |
|     AcpiFadtData fadt = {
 | |
|         /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */
 | |
|         .rev = 5,
 | |
|         .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) |
 | |
|                   (1 << ACPI_FADT_F_RESET_REG_SUP)),
 | |
| 
 | |
|         /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */
 | |
|         .sleep_ctl = {
 | |
|             .space_id = AML_AS_SYSTEM_MEMORY,
 | |
|             .bit_width = 8,
 | |
|             .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL,
 | |
|         },
 | |
|         .sleep_sts = {
 | |
|             .space_id = AML_AS_SYSTEM_MEMORY,
 | |
|             .bit_width = 8,
 | |
|             .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS,
 | |
|         },
 | |
| 
 | |
|         /* ACPI 5.0: 4.8.3.6 Reset Register */
 | |
|         .reset_reg = {
 | |
|             .space_id = AML_AS_SYSTEM_MEMORY,
 | |
|             .bit_width = 8,
 | |
|             .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET,
 | |
|         },
 | |
|         .reset_val = ACPI_GED_RESET_VALUE,
 | |
|     };
 | |
|     *data = fadt;
 | |
| }
 | |
| 
 | |
| static void acpi_align_size(GArray *blob, unsigned align)
 | |
| {
 | |
|     /*
 | |
|      * Align size to multiple of given size. This reduces the chance
 | |
|      * we need to change size in the future (breaking cross version migration).
 | |
|      */
 | |
|     g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
 | |
| }
 | |
| 
 | |
| /* build FACS */
 | |
| static void
 | |
| build_facs(GArray *table_data)
 | |
| {
 | |
|     const char *sig = "FACS";
 | |
|     const uint8_t reserved[40] = {};
 | |
| 
 | |
|     g_array_append_vals(table_data, sig, 4); /* Signature */
 | |
|     build_append_int_noprefix(table_data, 64, 4); /* Length */
 | |
|     build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */
 | |
|     build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */
 | |
|     build_append_int_noprefix(table_data, 0, 4); /* Global Lock */
 | |
|     build_append_int_noprefix(table_data, 0, 4); /* Flags */
 | |
|     g_array_append_vals(table_data, reserved, 40); /* Reserved */
 | |
| }
 | |
| 
 | |
| /* build MADT */
 | |
| static void
 | |
| build_madt(GArray *table_data, BIOSLinker *linker,
 | |
|            LoongArchVirtMachineState *lvms)
 | |
| {
 | |
|     MachineState *ms = MACHINE(lvms);
 | |
|     MachineClass *mc = MACHINE_GET_CLASS(ms);
 | |
|     const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
 | |
|     int i, arch_id;
 | |
|     AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id,
 | |
|                         .oem_table_id = lvms->oem_table_id };
 | |
| 
 | |
|     acpi_table_begin(&table, table_data);
 | |
| 
 | |
|     /* Local APIC Address */
 | |
|     build_append_int_noprefix(table_data, 0, 4);
 | |
|     build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
 | |
| 
 | |
|     for (i = 0; i < arch_ids->len; i++) {
 | |
|         /* Processor Core Interrupt Controller Structure */
 | |
|         arch_id = arch_ids->cpus[i].arch_id;
 | |
| 
 | |
|         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 */
 | |
|     }
 | |
| 
 | |
|     /* Extend I/O Interrupt Controller Structure */
 | |
|     build_append_int_noprefix(table_data, 20, 1);        /* Type */
 | |
|     build_append_int_noprefix(table_data, 13, 1);        /* Length */
 | |
|     build_append_int_noprefix(table_data, 1, 1);         /* Version */
 | |
|     build_append_int_noprefix(table_data, 3, 1);         /* Cascade */
 | |
|     build_append_int_noprefix(table_data, 0, 1);         /* Node */
 | |
|     build_append_int_noprefix(table_data, 0xffff, 8);    /* Node map */
 | |
| 
 | |
|     /* MSI Interrupt Controller Structure */
 | |
|     build_append_int_noprefix(table_data, 21, 1);        /* Type */
 | |
|     build_append_int_noprefix(table_data, 19, 1);        /* Length */
 | |
|     build_append_int_noprefix(table_data, 1, 1);         /* Version */
 | |
|     build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */
 | |
|     build_append_int_noprefix(table_data, 0x40, 4);      /* Start */
 | |
|     build_append_int_noprefix(table_data, 0xc0, 4);      /* Count */
 | |
| 
 | |
|     /* Bridge I/O Interrupt Controller Structure */
 | |
|     build_append_int_noprefix(table_data, 22, 1);        /* Type */
 | |
|     build_append_int_noprefix(table_data, 17, 1);        /* Length */
 | |
|     build_append_int_noprefix(table_data, 1, 1);         /* Version */
 | |
|     build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */
 | |
|     build_append_int_noprefix(table_data, 0x1000, 2);    /* Size */
 | |
|     build_append_int_noprefix(table_data, 0, 2);         /* Id */
 | |
|     build_append_int_noprefix(table_data, 0x40, 2);      /* Base */
 | |
| 
 | |
|     acpi_table_end(linker, &table);
 | |
| }
 | |
| 
 | |
| /* build SRAT */
 | |
| static void
 | |
| build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
 | |
| {
 | |
|     int i, arch_id, node_id;
 | |
|     hwaddr len, base, gap;
 | |
|     NodeInfo *numa_info;
 | |
|     int nodes, nb_numa_nodes = machine->numa_state->num_nodes;
 | |
|     LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
 | |
|     MachineClass *mc = MACHINE_GET_CLASS(lvms);
 | |
|     const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine);
 | |
|     AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id,
 | |
|                         .oem_table_id = lvms->oem_table_id };
 | |
| 
 | |
|     acpi_table_begin(&table, table_data);
 | |
|     build_append_int_noprefix(table_data, 1, 4); /* Reserved */
 | |
|     build_append_int_noprefix(table_data, 0, 8); /* Reserved */
 | |
| 
 | |
|     for (i = 0; i < arch_ids->len; ++i) {
 | |
|         arch_id = arch_ids->cpus[i].arch_id;
 | |
|         node_id = arch_ids->cpus[i].props.node_id;
 | |
| 
 | |
|         /* Processor Local APIC/SAPIC Affinity Structure */
 | |
|         build_append_int_noprefix(table_data, 0, 1);  /* Type  */
 | |
|         build_append_int_noprefix(table_data, 16, 1); /* Length */
 | |
|         /* Proximity Domain [7:0] */
 | |
|         build_append_int_noprefix(table_data, node_id, 1);
 | |
|         build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */
 | |
|         /* Flags, Table 5-36 */
 | |
|         build_append_int_noprefix(table_data, 1, 4);
 | |
|         build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */
 | |
|         /* Proximity Domain [31:8] */
 | |
|         build_append_int_noprefix(table_data, 0, 3);
 | |
|         build_append_int_noprefix(table_data, 0, 4); /* Reserved */
 | |
|     }
 | |
| 
 | |
|     base = VIRT_LOWMEM_BASE;
 | |
|     gap = VIRT_LOWMEM_SIZE;
 | |
|     numa_info = machine->numa_state->nodes;
 | |
|     nodes = nb_numa_nodes;
 | |
|     if (!nodes) {
 | |
|         nodes = 1;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < nodes; i++) {
 | |
|         if (nb_numa_nodes) {
 | |
|             len = numa_info[i].node_mem;
 | |
|         } else {
 | |
|             len = machine->ram_size;
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|          * memory for the node splited into two part
 | |
|          *   lowram:  [base, +gap)
 | |
|          *   highram: [VIRT_HIGHMEM_BASE, +(len - gap))
 | |
|          */
 | |
|         if (len >= gap) {
 | |
|             build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED);
 | |
|             len -= gap;
 | |
|             base = VIRT_HIGHMEM_BASE;
 | |
|             gap = machine->ram_size - VIRT_LOWMEM_SIZE;
 | |
|         }
 | |
| 
 | |
|         if (len) {
 | |
|             build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED);
 | |
|             base += len;
 | |
|             gap  -= len;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (machine->device_memory) {
 | |
|         build_srat_memory(table_data, machine->device_memory->base,
 | |
|                           memory_region_size(&machine->device_memory->mr),
 | |
|                           nodes - 1,
 | |
|                           MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
 | |
|     }
 | |
| 
 | |
|     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). */
 | |
|     MemoryRegion *table_mr;
 | |
|     /* Is table patched? */
 | |
|     uint8_t patched;
 | |
|     void *rsdp;
 | |
|     MemoryRegion *rsdp_mr;
 | |
|     MemoryRegion *linker_mr;
 | |
| } AcpiBuildState;
 | |
| 
 | |
| static void build_uart_device_aml(Aml *table, int index)
 | |
| {
 | |
|     Aml *dev;
 | |
|     Aml *crs;
 | |
|     Aml *pkg0, *pkg1, *pkg2;
 | |
|     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(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, 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));
 | |
|     aml_append(dev, aml_name_decl("_CRS", crs));
 | |
|     pkg0 = aml_package(0x2);
 | |
|     aml_append(pkg0, aml_int(0x05F5E100));
 | |
|     aml_append(pkg0, aml_string("clock-frenquency"));
 | |
|     pkg1 = aml_package(0x1);
 | |
|     aml_append(pkg1, pkg0);
 | |
|     pkg2 = aml_package(0x2);
 | |
|     aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
 | |
|     aml_append(pkg2, pkg1);
 | |
|     aml_append(dev, aml_name_decl("_DSD", pkg2));
 | |
|     aml_append(scope, dev);
 | |
|     aml_append(table, scope);
 | |
| }
 | |
| 
 | |
| static void
 | |
| build_la_ged_aml(Aml *dsdt, MachineState *machine)
 | |
| {
 | |
|     uint32_t event;
 | |
|     LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
 | |
| 
 | |
|     build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
 | |
|                   HOTPLUG_HANDLER(lvms->acpi_ged),
 | |
|                   VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
 | |
|                   VIRT_GED_EVT_ADDR);
 | |
|     event = object_property_get_uint(OBJECT(lvms->acpi_ged),
 | |
|                                      "ged-event", &error_abort);
 | |
|     if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
 | |
|         build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL,
 | |
|                                  AML_SYSTEM_MEMORY,
 | |
|                                  VIRT_GED_MEM_ADDR);
 | |
|     }
 | |
|     acpi_dsdt_add_power_button(dsdt);
 | |
| }
 | |
| 
 | |
| static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms)
 | |
| {
 | |
|     struct GPEXConfig cfg = {
 | |
|         .mmio64.base = VIRT_PCI_MEM_BASE,
 | |
|         .mmio64.size = VIRT_PCI_MEM_SIZE,
 | |
|         .pio.base    = VIRT_PCI_IO_BASE,
 | |
|         .pio.size    = VIRT_PCI_IO_SIZE,
 | |
|         .ecam.base   = VIRT_PCI_CFG_BASE,
 | |
|         .ecam.size   = VIRT_PCI_CFG_SIZE,
 | |
|         .irq         = VIRT_GSI_BASE + VIRT_DEVICE_IRQS,
 | |
|         .bus         = lvms->pci_bus,
 | |
|     };
 | |
| 
 | |
|     acpi_dsdt_add_gpex(scope, &cfg);
 | |
| }
 | |
| 
 | |
| static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms)
 | |
| {
 | |
|     Aml *dev, *crs;
 | |
|     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);
 | |
| 
 | |
|     dev = aml_device("FLS0");
 | |
|     aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
 | |
|     aml_append(dev, aml_name_decl("_UID", aml_int(0)));
 | |
| 
 | |
|     crs = aml_resource_template();
 | |
|     aml_append(crs, aml_memory32_fixed(flash0_base, flash0_size,
 | |
|                                        AML_READ_WRITE));
 | |
|     aml_append(dev, aml_name_decl("_CRS", crs));
 | |
|     aml_append(scope, dev);
 | |
| 
 | |
|     dev = aml_device("FLS1");
 | |
|     aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
 | |
|     aml_append(dev, aml_name_decl("_UID", aml_int(1)));
 | |
| 
 | |
|     crs = aml_resource_template();
 | |
|     aml_append(crs, aml_memory32_fixed(flash1_base, flash1_size,
 | |
|                                        AML_READ_WRITE));
 | |
|     aml_append(dev, aml_name_decl("_CRS", crs));
 | |
|     aml_append(scope, dev);
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_TPM
 | |
| static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms)
 | |
| {
 | |
|     PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
 | |
|     hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS;
 | |
|     SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find());
 | |
|     MemoryRegion *sbdev_mr;
 | |
|     hwaddr tpm_base;
 | |
| 
 | |
|     if (!sbdev) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
 | |
|     assert(tpm_base != -1);
 | |
| 
 | |
|     tpm_base += pbus_base;
 | |
| 
 | |
|     sbdev_mr = sysbus_mmio_get_region(sbdev, 0);
 | |
| 
 | |
|     Aml *dev = aml_device("TPM0");
 | |
|     aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
 | |
|     aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device")));
 | |
|     aml_append(dev, aml_name_decl("_UID", aml_int(0)));
 | |
| 
 | |
|     Aml *crs = aml_resource_template();
 | |
|     aml_append(crs,
 | |
|                aml_memory32_fixed(tpm_base,
 | |
|                                   (uint32_t)memory_region_size(sbdev_mr),
 | |
|                                   AML_READ_WRITE));
 | |
|     aml_append(dev, aml_name_decl("_CRS", crs));
 | |
|     aml_append(scope, dev);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* build DSDT */
 | |
| 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,
 | |
|                         .oem_table_id = lvms->oem_table_id };
 | |
| 
 | |
|     acpi_table_begin(&table, table_data);
 | |
|     dsdt = init_aml_allocator();
 | |
|     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);
 | |
| #ifdef CONFIG_TPM
 | |
|     acpi_dsdt_add_tpm(dsdt, lvms);
 | |
| #endif
 | |
|     /* System State Package */
 | |
|     scope = aml_scope("\\");
 | |
|     pkg = aml_package(4);
 | |
|     aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5));
 | |
|     aml_append(pkg, aml_int(0)); /* ignored */
 | |
|     aml_append(pkg, aml_int(0)); /* reserved */
 | |
|     aml_append(pkg, aml_int(0)); /* reserved */
 | |
|     aml_append(scope, aml_name_decl("_S5", pkg));
 | |
|     aml_append(dsdt, scope);
 | |
|     /* Copy AML table into ACPI tables blob and patch header there */
 | |
|     g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
 | |
|     acpi_table_end(linker, &table);
 | |
|     free_aml_allocator();
 | |
| }
 | |
| 
 | |
| static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
 | |
| {
 | |
|     LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
 | |
|     GArray *table_offsets;
 | |
|     AcpiFadtData fadt_data;
 | |
|     unsigned facs, rsdt, dsdt;
 | |
|     uint8_t *u;
 | |
|     GArray *tables_blob = tables->table_data;
 | |
| 
 | |
|     init_common_fadt_data(&fadt_data);
 | |
| 
 | |
|     table_offsets = g_array_new(false, true, sizeof(uint32_t));
 | |
|     ACPI_BUILD_DPRINTF("init ACPI tables\n");
 | |
| 
 | |
|     bios_linker_loader_alloc(tables->linker,
 | |
|                              ACPI_BUILD_TABLE_FILE, tables_blob,
 | |
|                              64, false);
 | |
| 
 | |
|     /*
 | |
|      * FACS is pointed to by FADT.
 | |
|      * We place it first since it's the only table that has alignment
 | |
|      * requirements.
 | |
|      */
 | |
|     facs = tables_blob->len;
 | |
|     build_facs(tables_blob);
 | |
| 
 | |
|     /* DSDT is pointed to by FADT */
 | |
|     dsdt = tables_blob->len;
 | |
|     build_dsdt(tables_blob, tables->linker, machine);
 | |
| 
 | |
|     /* ACPI tables pointed to by RSDT */
 | |
|     acpi_add_table(table_offsets, tables_blob);
 | |
|     fadt_data.facs_tbl_offset = &facs;
 | |
|     fadt_data.dsdt_tbl_offset = &dsdt;
 | |
|     fadt_data.xdsdt_tbl_offset = &dsdt;
 | |
|     build_fadt(tables_blob, tables->linker, &fadt_data,
 | |
|                lvms->oem_id, lvms->oem_table_id);
 | |
| 
 | |
|     acpi_add_table(table_offsets, tables_blob);
 | |
|     build_madt(tables_blob, tables->linker, lvms);
 | |
| 
 | |
|     acpi_add_table(table_offsets, tables_blob);
 | |
|     build_pptt(tables_blob, tables->linker, machine,
 | |
|                lvms->oem_id, lvms->oem_table_id);
 | |
| 
 | |
|     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) {
 | |
|             acpi_add_table(table_offsets, tables_blob);
 | |
|             build_slit(tables_blob, tables->linker, machine, lvms->oem_id,
 | |
|                        lvms->oem_table_id);
 | |
|         }
 | |
|         if (machine->numa_state->hmat_enabled) {
 | |
|             acpi_add_table(table_offsets, tables_blob);
 | |
|             build_hmat(tables_blob, tables->linker, machine->numa_state,
 | |
|                        lvms->oem_id, lvms->oem_table_id);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     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),
 | |
|         };
 | |
|         build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id,
 | |
|                    lvms->oem_table_id);
 | |
|     }
 | |
| 
 | |
| #ifdef CONFIG_TPM
 | |
|     /* TPM info */
 | |
|     if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) {
 | |
|         acpi_add_table(table_offsets, tables_blob);
 | |
|         build_tpm2(tables_blob, tables->linker,
 | |
|                    tables->tcpalog, lvms->oem_id,
 | |
|                    lvms->oem_table_id);
 | |
|     }
 | |
| #endif
 | |
|     /* Add tables supplied by user (if any) */
 | |
|     for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
 | |
|         unsigned len = acpi_table_len(u);
 | |
| 
 | |
|         acpi_add_table(table_offsets, tables_blob);
 | |
|         g_array_append_vals(tables_blob, u, len);
 | |
|     }
 | |
| 
 | |
|     /* RSDT is pointed to by RSDP */
 | |
|     rsdt = tables_blob->len;
 | |
|     build_rsdt(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,
 | |
|             .oem_id = lvms->oem_id,
 | |
|             .xsdt_tbl_offset = NULL,
 | |
|             .rsdt_tbl_offset = &rsdt,
 | |
|         };
 | |
|         build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * The align size is 128, warn if 64k is not enough therefore
 | |
|      * the align size could be resized.
 | |
|      */
 | |
|     if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
 | |
|         warn_report("ACPI table size %u exceeds %d bytes,"
 | |
|                     " migration may not work",
 | |
|                     tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
 | |
|         error_printf("Try removing CPUs, NUMA nodes, memory slots"
 | |
|                      " or PCI bridges.\n");
 | |
|     }
 | |
| 
 | |
|     acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE);
 | |
| 
 | |
|     /* Cleanup memory that's no longer used. */
 | |
|     g_array_free(table_offsets, true);
 | |
| }
 | |
| 
 | |
| static void acpi_ram_update(MemoryRegion *mr, GArray *data)
 | |
| {
 | |
|     uint32_t size = acpi_data_len(data);
 | |
| 
 | |
|     /*
 | |
|      * Make sure RAM size is correct - in case it got changed
 | |
|      * e.g. by migration
 | |
|      */
 | |
|     memory_region_ram_resize(mr, size, &error_abort);
 | |
| 
 | |
|     memcpy(memory_region_get_ram_ptr(mr), data->data, size);
 | |
|     memory_region_set_dirty(mr, 0, size);
 | |
| }
 | |
| 
 | |
| static void acpi_build_update(void *build_opaque)
 | |
| {
 | |
|     AcpiBuildState *build_state = build_opaque;
 | |
|     AcpiBuildTables tables;
 | |
| 
 | |
|     /* No state to update or already patched? Nothing to do. */
 | |
|     if (!build_state || build_state->patched) {
 | |
|         return;
 | |
|     }
 | |
|     build_state->patched = 1;
 | |
| 
 | |
|     acpi_build_tables_init(&tables);
 | |
| 
 | |
|     acpi_build(&tables, MACHINE(qdev_get_machine()));
 | |
| 
 | |
|     acpi_ram_update(build_state->table_mr, tables.table_data);
 | |
|     acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
 | |
|     acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
 | |
| 
 | |
|     acpi_build_tables_cleanup(&tables, true);
 | |
| }
 | |
| 
 | |
| static void acpi_build_reset(void *build_opaque)
 | |
| {
 | |
|     AcpiBuildState *build_state = build_opaque;
 | |
|     build_state->patched = 0;
 | |
| }
 | |
| 
 | |
| static const VMStateDescription vmstate_acpi_build = {
 | |
|     .name = "acpi_build",
 | |
|     .version_id = 1,
 | |
|     .minimum_version_id = 1,
 | |
|     .fields = (const VMStateField[]) {
 | |
|         VMSTATE_UINT8(patched, AcpiBuildState),
 | |
|         VMSTATE_END_OF_LIST()
 | |
|     },
 | |
| };
 | |
| 
 | |
| static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
 | |
| {
 | |
|     if (lvms->acpi == ON_OFF_AUTO_OFF) {
 | |
|         return false;
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
 | |
| {
 | |
|     AcpiBuildTables tables;
 | |
|     AcpiBuildState *build_state;
 | |
| 
 | |
|     if (!lvms->fw_cfg) {
 | |
|         ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!loongarch_is_acpi_enabled(lvms)) {
 | |
|         ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     build_state = g_malloc0(sizeof *build_state);
 | |
| 
 | |
|     acpi_build_tables_init(&tables);
 | |
|     acpi_build(&tables, MACHINE(lvms));
 | |
| 
 | |
|     /* Now expose it all to Guest */
 | |
|     build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
 | |
|                                               build_state, tables.table_data,
 | |
|                                               ACPI_BUILD_TABLE_FILE);
 | |
|     assert(build_state->table_mr != NULL);
 | |
| 
 | |
|     build_state->linker_mr =
 | |
|         acpi_add_rom_blob(acpi_build_update, build_state,
 | |
|                           tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE);
 | |
| 
 | |
|     build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update,
 | |
|                                              build_state, tables.rsdp,
 | |
|                                              ACPI_BUILD_RSDP_FILE);
 | |
| 
 | |
|     fw_cfg_add_file(lvms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data,
 | |
|                     acpi_data_len(tables.tcpalog));
 | |
| 
 | |
|     qemu_register_reset(acpi_build_reset, build_state);
 | |
|     acpi_build_reset(build_state);
 | |
|     vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
 | |
| 
 | |
|     /*
 | |
|      * Cleanup tables but don't free the memory: we track it
 | |
|      * in build_state.
 | |
|      */
 | |
|     acpi_build_tables_cleanup(&tables, false);
 | |
| }
 |