Merge tag 'pull-hex-20260619' of https://github.com/qualcomm/qemu into staging Add hexagon system emulation support # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEEPWaq5HRZSCTIjOD4GlSvuOVkbDIFAmo1xScACgkQGlSvuOVk # bDJxsQ//djqZ2pyKMDDJWqSGhd1nsLF8YYgbOj2LTS/OdMIP7EjF+ZS/zTd/JKK8 # zc5KpmkjiUxKZbRlR5ZaSosQD1zeKo+vi6Vs2PuKLt+/cf2hwrRPAfudmlfBlz2R # KNIFSpjNspBz/Hi/S+x1TQkjjHnvtjSJiautoz8XwUHxiNvMlxJXpU6icTHKVthr # DkC5ssj6NM9TMekQizkTsbcamzb8ket1DaTFEzmxQaxpKdwmMYYC5AmipKEeT2xF # SGhX0owXWmJJ8Au5S705braA8vYZ97Hw1khRaBjG6FqtLNBXuYnWfMfAB7WHhK+G # mhif3FCKF8izVTpr4iKiwOfYAPqsEFvbQny35pY2jm7XQR2Mo8YDEbOCRqUOqSV3 # OktbuO3VN1zvX1rcAMsaLTTcvJdClW7LsCojLSRio9gJwOfKH5dfqx/fDGUzDpwK # uqNzNPSAODUKG5jOdFiGEdXVFt4CiVt2WOAiMXsgIJPkufM2qMqNHJWQtV5bbCMq # vzSB9T4oYxLg29IANuSuQWaReJrIKj6rV0qNedVjKjZYIzfiZCQig7UmtpMattI9 # w3Uvq2Oth/sz9jeDiqBMBZN14KMGIkGkBrr68WPA35ZCMVswJWn1X+8UayzzdbFP # rg2S294+QovqHUb1TrVrD0O0snofLJgVQxs9OukUA1yof8RvBvE= # =jlYK # -----END PGP SIGNATURE----- # gpg: Signature made Fri 19 Jun 2026 18:39:35 EDT # gpg: using RSA key 3D66AAE474594824C88CE0F81A54AFB8E5646C32 # gpg: Good signature from "Brian Cain (OSS Qualcomm) <brian.cain@oss.qualcomm.com>" [unknown] # gpg: aka "Brian Cain <bcain@kernel.org>" [unknown] # gpg: aka "Brian Cain (QuIC) <bcain@quicinc.com>" [unknown] # gpg: aka "Brian Cain (CAF) <bcain@codeaurora.org>" [unknown] # gpg: aka "bcain" [unknown] # gpg: aka "Brian Cain (QUIC) <quic_bcain@quicinc.com>" [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: 6350 20F9 67A7 7164 79EF 49E0 175C 464E 541B 6D47 # Subkey fingerprint: 3D66 AAE4 7459 4824 C88C E0F8 1A54 AFB8 E564 6C32 * tag 'pull-hex-20260619' of https://github.com/qualcomm/qemu: (74 commits) target/hexagon/idef-parser: open input file in binary mode tests/qtest: Add hexagon boot-serial-test hw/hexagon: Define hexagon "virt" machine target/hexagon: add build config for softmmu hw/hexagon: Add support for cfgbase hw/hexagon: Add v68, sa8775-cdsp0 defs hw/hexagon: Add machine configs for sysemu hw/hexagon: Add hexagon TLB device implementation hw/hexagon: Add global register tracing hw/hexagon: Add globalreg model target/hexagon: Add guest reg reading functionality target/hexagon: Add support for loadw_phys target/hexagon: Add TCG overrides for transfer insts target/hexagon: Add PC to raise_exception, use fTRAP() helper target/hexagon: Add k0 {un,}lock target/hexagon: Define system, guest reg names target/hexagon: Add implicit sysreg writes target/hexagon: Add next_PC, {s,g}reg writes target/hexagon: Add pkt_ends_tb to translation target/hexagon: Implement modify_ssr, resched, pending_interrupt ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
diff --git a/MAINTAINERS b/MAINTAINERS index 93df53d..cda4d57 100644 --- a/MAINTAINERS +++ b/MAINTAINERS
@@ -256,8 +256,12 @@ F: tests/tcg/hexagon/ F: disas/hexagon.c F: configs/targets/hexagon-linux-user.mak +F: configs/targets/hexagon-softmmu.mak F: tests/docker/dockerfiles/debian-hexagon-cross.docker F: gdbstub/gdb-xml/hexagon*.xml +F: docs/system/target-hexagon.rst +F: docs/system/hexagon/ +F: docs/devel/hexagon-sys.rst T: git https://github.com/qualcomm/qemu.git hex-next Hexagon idef-parser @@ -1341,6 +1345,18 @@ F: roms/seabios-hppa/ F: tests/functional/hppa/ +Hexagon Machines +---------------- +V66G_1024, V68N_1024, sa8775-cdsp0 +M: Brian Cain <brian.cain@oss.qualcomm.com> +R: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> +S: Supported +F: hw/hexagon/ +F: include/hw/hexagon/ +F: configs/devices/hexagon-softmmu/default.mak +F: docs/system/hexagon/ +F: docs/devel/hexagon-sys.rst + LoongArch Machines ------------------ Virt
diff --git a/configs/devices/hexagon-softmmu/default.mak b/configs/devices/hexagon-softmmu/default.mak new file mode 100644 index 0000000..6f20077 --- /dev/null +++ b/configs/devices/hexagon-softmmu/default.mak
@@ -0,0 +1,7 @@ +# Default configuration for hexagon-softmmu + +# Uncomment the following lines to disable these optional devices: + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_HEX_VIRT=y +# CONFIG_HEX_DSP=y
diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak new file mode 100644 index 0000000..a77c100 --- /dev/null +++ b/configs/targets/hexagon-softmmu.mak
@@ -0,0 +1,8 @@ +# Default configuration for hexagon-softmmu + +TARGET_ARCH=hexagon +TARGET_XML_FILES=hexagon-core.xml hexagon-hvx.xml +TARGET_LONG_BITS=32 +TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y +TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y +TARGET_NEED_FDT=y
diff --git a/docs/devel/hexagon-sys.rst b/docs/devel/hexagon-sys.rst new file mode 100644 index 0000000..92ebc32 --- /dev/null +++ b/docs/devel/hexagon-sys.rst
@@ -0,0 +1,112 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. _Hexagon-System-arch: + +Hexagon System Architecture +=========================== + +The hexagon architecture has some unique elements which are described here. + +Interrupts +---------- +When interrupts arrive at a Hexagon DSP core, they are priority-steered to +be handled by an eligible hardware thread with the lowest priority. + +Memory +------ +Each hardware thread has an ``SSR.ASID`` field that contains its Address +Space Identifier. This value is catenated with a 32-bit virtual address - +the MMU can then resolve this extended virtual address to a physical address. + +TLBs +---- +The format of a TLB entry is shown below. + +.. note:: + The Small Core DSPs have a different TLB format which is not yet + supported. + +.. admonition:: Diagram + + .. code:: text + + 6 5 4 3 + 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |v|g|x|A|A| | | + |a|l|P|1|0| ASID | Virtual Page | + |l|b| | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 3 2 1 0 + 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | | | | + |x|w|r|u|Cacheab| Physical Page |S| + | | | | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +* ASID: the address-space identifier +* A1, A0: the behavior of these cache line attributes are not modeled by QEMU. +* xP: the extra-physical bit is the most significant physical address bit. +* S: the S bit and the LSBs of the physical page indicate the page size +* val: this is the 'valid' bit, when set it indicates that page matching + should consider this entry. + +.. list-table:: Page sizes + :widths: 25 25 50 + :header-rows: 1 + + * - S-bit + - Phys page LSBs + - Page size + * - 1 + - N/A + - 4kb + * - 0 + - 0b1 + - 16kb + * - 0 + - 0b10 + - 64kb + * - 0 + - 0b100 + - 256kb + * - 0 + - 0b1000 + - 1MB + * - 0 + - 0b10000 + - 4MB + * - 0 + - 0b100000 + - 16MB + +* glb: if the global bit is set, the ASID is not considered when matching + TLBs. +* Cacheab: the cacheability attributes of TLBs are not modeled, these bits + are ignored. +* RWX: read-, write-, execute-, enable bits. Indicates if user programs + are permitted to read/write/execute the given page. +* U: indicates if user programs can access this page. + +Scheduler +--------- +The Hexagon system architecture has a feature to assist the guest OS +task scheduler. The guest OS can enable this feature by setting +``SCHEDCFG.EN``. The ``BESTWAIT`` register is programmed by the guest OS +to indicate the priority of the highest priority task waiting to run on a +hardware thread. The reschedule interrupt is triggered when any hardware +thread's priority in ``STID.PRIO`` is worse than the ``BESTWAIT``. When +it is triggered, the ``BESTWAIT.PRIO`` value is reset to 0x1ff. + +HVX Coprocessor +--------------- +The Supervisor Status Register field ``SSR.XA`` binds a DSP hardware thread +to one of the eight possible HVX contexts. The guest OS is responsible for +managing this resource. + +.. seealso:: + + ``target/hexagon/README`` in the QEMU source tree for more info about Hexagon.
diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index 7a0678c..0471db8 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst
@@ -14,6 +14,7 @@ block-coroutine-wrapper clocks ebpf_rss + hexagon-sys migration/index multi-process reset
diff --git a/docs/system/hexagon/cdsp.rst b/docs/system/hexagon/cdsp.rst new file mode 100644 index 0000000..2375292 --- /dev/null +++ b/docs/system/hexagon/cdsp.rst
@@ -0,0 +1,12 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Compute DSP +=========== + +A Hexagon CDSP is designed as a computation offload device for an SoC. The +``V66G_1024`` machine contains: + +* L2VIC interrupt controller +* QTimer timer device + +This machine will support any Hexagon CPU, but will default to ``v66``.
diff --git a/docs/system/hexagon/emulation.rst b/docs/system/hexagon/emulation.rst new file mode 100644 index 0000000..36f54f0 --- /dev/null +++ b/docs/system/hexagon/emulation.rst
@@ -0,0 +1,15 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. _Hexagon Emulation: + +Hexagon CPU architecture support +================================ + +QEMU's TCG emulation includes support for v65, v66, v67, v68, v69, v71, v73. +It also has support for the following architecture extensions: + +- HVX (Hexagon Vector eXtensions) + +For information on the specifics of the HVX extension, please refer +to the `Qualcomm Hexagon V73 HVX Programmer's Reference Manual +<https://docs.qualcomm.com/bundle/publicresource/80-N2040-53.pdf>`_.
diff --git a/docs/system/target-hexagon.rst b/docs/system/target-hexagon.rst new file mode 100644 index 0000000..416b8f7 --- /dev/null +++ b/docs/system/target-hexagon.rst
@@ -0,0 +1,103 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. _Hexagon-System-emulator: + +Hexagon System emulator +----------------------- + +Use the ``qemu-system-hexagon`` executable to simulate a 32-bit Hexagon +machine. + +Hexagon Machines +================ + +Hexagon DSPs are suited to various functions and generally appear in a +"DSP subsystem" of a larger system-on-chip (SoC). + +Hexagon DSPs are often included in a subsystem that looks like the diagram +below. Instructions are loaded into DDR before the DSP is brought out of +reset and the first instructions are fetched from DDR via the EVB/reset vector. + +In a real system, a TBU/SMMU would normally arbitrate AXI accesses but +we don't have a need to model that for QEMU. + +Hexagon DSP cores use simultaneous multithreading (SMT) with as many as 8 +hardware threads. + +.. admonition:: Diagram + + .. code:: text + + AHB (local) bus AXI (global) bus + │ │ + │ │ + ┌─────────┐ │ ┌─────────────────┐ │ + │ L2VIC ├──┤ │ │ │ + │ ├──┼───────► ├───────┤ + └─────▲───┘ │ │ Hexagon DSP │ │ + │ │ │ │ │ ┌─────┐ + │ │ │ N threads │ │ │ DDR │ + │ ├───────┤ │ │ │ │ + ┌────┴──┐ │ │ │ ├────────┤ │ + │QTimer ├───┤ │ │ │ │ │ + │ │ │ │ │ │ │ │ + └───────┘ │ │ ┌─────────┐ │ │ │ │ + │ │ ┌─────────┐│ │ │ │ │ + ┌───────┐ │ │ │ HVX xM ││ │ │ │ │ + │QDSP6SS├───┤ │ │ │┘ │ │ │ │ + └───────┘ │ │ └─────────┘ │ │ └─────┘ + │ │ │ │ + ┌───────┐ │ └─────────────────┘ │ + │ CSR ├───┤ + └───────┘ │ ┌──────┐ ┌───────────┐ + │ │ TCM │ │ VTCM │ + │ │ │ │ + └──────┘ │ │ + │ │ + │ │ + │ │ + └───────────┘ + +Components +---------- +Other than l2vic and HVX, the components below are not implemented in QEMU. + +* L2VIC: the L2 vectored interrupt controller. Supports 1024 input + interrupts, edge- or level-triggered. The core ISA has system registers + ``VID``, ``VID1`` which read through to the L2VIC device. +* QTimer: ARMSSE-based programmable timer device. Its interrupts are + wired to the L2VIC. System registers ``TIMER``, ``UTIMER`` read + through to the QTimer device. +* QDSP6SS: DSP subsystem features, accessible to the entire SoC, including + DSP NMI, watchdog, reset, etc. +* CSR: Configuration/Status Registers. +* TCM: DSP-exclusive tightly-coupled memory. This memory can be used for + DSPs when isolated from DDR and in some bootstrapping modes. +* VTCM: DSP-exclusive vector tightly-coupled memory. This memory is accessed + by some HVX instructions. +* HVX: the vector coprocessor supports 64 and 128-byte vector registers. + 64-byte mode is not implemented in QEMU. + + +Bootstrapping +------------- +Hexagon systems do not generally have access to a block device. So, for +QEMU the typical use case involves loading a binary or ELF file into memory +and executing from the indicated start address:: + + $ qemu-system-hexagon -kernel ./prog -append 'arg1 arg2' + +Semihosting +----------- +Hexagon supports a semihosting interface similar to other architectures'. +The ``trap0`` instruction can activate these semihosting calls so that the +guest software can access the host console and filesystem. Semihosting +is not yet implemented in QEMU hexagon. + + +Hexagon Features +================ +.. toctree:: + hexagon/emulation + hexagon/cdsp +
diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 5b12858..5ebdd0f 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst
@@ -30,3 +30,4 @@ target-sparc64 target-i386 target-xtensa + target-hexagon
diff --git a/hw/Kconfig b/hw/Kconfig index c109f55..c92ca2b 100644 --- a/hw/Kconfig +++ b/hw/Kconfig
@@ -53,6 +53,7 @@ source cpu/Kconfig source alpha/Kconfig source avr/Kconfig +source hexagon/Kconfig source hppa/Kconfig source i386/Kconfig source loongarch/Kconfig
diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig new file mode 100644 index 0000000..52065ab --- /dev/null +++ b/hw/hexagon/Kconfig
@@ -0,0 +1,14 @@ +config HEX_DSP + bool + default y + depends on HEXAGON + +config HEX_VIRT + bool + default y + depends on HEX_DSP && FDT + select DEVICE_TREE + select VIRTIO_MMIO + select PL011 + select VIRTIO_BLK + select VIRTIO_SCSI
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c new file mode 100644 index 0000000..aa49399 --- /dev/null +++ b/hw/hexagon/hexagon_dsp.c
@@ -0,0 +1,225 @@ +/* + * Hexagon DSP Subsystem emulation. This represents a generic DSP + * subsystem with few peripherals, like the Compute DSP. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "system/address-spaces.h" +#include "hw/core/boards.h" +#include "hw/core/qdev-properties.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/core/loader.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "elf.h" +#include "cpu.h" +#include "migration/cpu.h" +#include "system/system.h" +#include "target/hexagon/internal.h" +#include "system/physmem.h" +#include "system/reset.h" + +#include "machine_cfg_v66g_1024.h.inc" + +#define TYPE_HEXAGON_DSP_MACHINE "hexagon-dsp-machine" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonDspMachineState, HEXAGON_DSP_MACHINE) + +struct HexagonDspMachineState { + HexagonCommonMachineState parent_obj; + + hwaddr isdb_secure_flag; + hwaddr isdb_trusted_flag; +}; + +static HexagonDspMachineState *current_dms; + +static void hex_symbol_callback(const char *st_name, int st_info, + uint64_t st_value, uint64_t st_size) +{ + if (!g_strcmp0("isdb_secure_flag", st_name)) { + current_dms->isdb_secure_flag = st_value; + } + if (!g_strcmp0("isdb_trusted_flag", st_name)) { + current_dms->isdb_trusted_flag = st_value; + } +} + +/* Board init. */ +static struct hexagon_board_boot_info hexagon_binfo; + +static void hexagon_load_kernel(HexagonDspMachineState *dms, HexagonCPU *cpu) +{ + uint64_t pentry; + long kernel_size; + + current_dms = dms; + kernel_size = load_elf_ram_sym(hexagon_binfo.kernel_filename, NULL, NULL, + NULL, &pentry, NULL, NULL, + &hexagon_binfo.kernel_elf_flags, 0, EM_HEXAGON, 0, 0, + &address_space_memory, false, hex_symbol_callback); + current_dms = NULL; + + if (kernel_size <= 0) { + error_report("no kernel file '%s'", + hexagon_binfo.kernel_filename); + exit(1); + } + + qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", pentry); +} + +static void hexagon_init_bootstrap(HexagonDspMachineState *dms, HexagonCPU *cpu) +{ + MachineState *machine = MACHINE(dms); + + if (machine->kernel_filename) { + uint32_t mem = 1; + + hexagon_load_kernel(dms, cpu); + if (dms->isdb_secure_flag) { + physical_memory_write(dms->isdb_secure_flag, + &mem, sizeof(mem)); + } + if (dms->isdb_trusted_flag) { + physical_memory_write(dms->isdb_trusted_flag, + &mem, sizeof(mem)); + } + } +} + +static void do_cpu_reset(void *opaque) +{ + HexagonCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + cpu_reset(cs); +} + +static void hexagon_common_init(MachineState *machine, Rev_t rev, + const struct hexagon_machine_config *m_cfg) +{ + HexagonCommonMachineState *hms = HEXAGON_COMMON_MACHINE(machine); + HexagonDspMachineState *dms = HEXAGON_DSP_MACHINE(machine); + MemoryRegion *address_space; + DeviceState *glob_regs_dev; + DeviceState *tlb_dev; + + memset(&hexagon_binfo, 0, sizeof(hexagon_binfo)); + if (machine->kernel_filename) { + hexagon_binfo.ram_size = machine->ram_size; + hexagon_binfo.kernel_filename = machine->kernel_filename; + } + + machine->enable_graphics = 0; + + address_space = get_system_memory(); + + memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", + sizeof(m_cfg->cfgtable), &error_fatal); + memory_region_add_subregion(address_space, m_cfg->cfgbase, + &hms->cfgtable_rom); + + memory_region_init_ram(&hms->ram, NULL, "ddr.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(address_space, 0x0, &hms->ram); + + glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); + object_property_add_child(OBJECT(machine), "global-regs", + OBJECT(glob_regs_dev)); + qdev_prop_set_uint64(glob_regs_dev, "config-table-addr", m_cfg->cfgbase); + qdev_prop_set_uint32(glob_regs_dev, "dsp-rev", rev); + sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs_dev), &error_fatal); + + tlb_dev = qdev_new(TYPE_HEXAGON_TLB); + object_property_add_child(OBJECT(machine), "tlb", OBJECT(tlb_dev)); + qdev_prop_set_uint32(tlb_dev, "num-entries", + m_cfg->cfgtable.jtlb_size_entries); + sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); + + for (int i = 0; i < machine->smp.cpus; i++) { + HexagonCPU *cpu = HEXAGON_CPU(object_new(machine->cpu_type)); + qemu_register_reset(do_cpu_reset, cpu); + + /* + * CPU #0 is the only CPU running at boot, others must be + * explicitly enabled via start instruction. + */ + qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); + if (i == 0) { + hexagon_init_bootstrap(dms, cpu); + } + object_property_set_link(OBJECT(cpu), "global-regs", + OBJECT(glob_regs_dev), &error_fatal); + object_property_set_link(OBJECT(cpu), "tlb", + OBJECT(tlb_dev), &error_fatal); + qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + } + + rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, + sizeof(m_cfg->cfgtable), m_cfg->cfgbase, + &address_space_memory); +} + +static void init_mc(MachineClass *mc) +{ + mc->block_default_type = IF_SD; + mc->default_ram_size = 4 * GiB; + mc->no_parallel = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_serial = 1; + mc->is_default = false; + mc->max_cpus = 8; +} + +/* ----------------------------------------------------------------- */ +/* Core-specific configuration settings are defined below this line. */ +/* Config table values defined in machine_configs.h.inc */ +/* ----------------------------------------------------------------- */ + +static void v66g_1024_config_init(MachineState *machine) +{ + hexagon_common_init(machine, v66_rev, &v66g_1024); +} + +static void v66g_1024_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Hexagon V66G_1024"; + mc->init = v66g_1024_config_init; + init_mc(mc); + mc->is_default = true; + mc->default_cpu_type = TYPE_HEXAGON_CPU_V66; + mc->default_cpus = 4; +} + +static const TypeInfo hexagon_machine_types[] = { + { + .name = TYPE_HEXAGON_COMMON_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(HexagonCommonMachineState), + .abstract = true, + }, + { + .name = TYPE_HEXAGON_DSP_MACHINE, + .parent = TYPE_HEXAGON_COMMON_MACHINE, + .instance_size = sizeof(HexagonDspMachineState), + .abstract = true, + }, + { + .name = MACHINE_TYPE_NAME("V66G_1024"), + .parent = TYPE_HEXAGON_DSP_MACHINE, + .class_init = v66g_1024_init, + }, +}; + +DEFINE_TYPES(hexagon_machine_types)
diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c new file mode 100644 index 0000000..b5e5913 --- /dev/null +++ b/hw/hexagon/hexagon_globalreg.c
@@ -0,0 +1,316 @@ +/* + * Hexagon Global Registers + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/sysbus.h" +#include "hw/core/resettable.h" +#include "migration/vmstate.h" +#include "qom/object.h" +#include "target/hexagon/cpu.h" +#include "target/hexagon/hex_regs.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" + +#define IMMUTABLE (~0) +#define INVALID_REG_VAL 0xdeadbeef + +static const char *hex_sreg_names[] = { + [HEX_SREG_SGP0] = "sgp0", + [HEX_SREG_SGP1] = "sgp1", + [HEX_SREG_STID] = "stid", + [HEX_SREG_ELR] = "elr", + [HEX_SREG_BADVA0] = "badva0", + [HEX_SREG_BADVA1] = "badva1", + [HEX_SREG_SSR] = "ssr", + [HEX_SREG_CCR] = "ccr", + [HEX_SREG_HTID] = "htid", + [HEX_SREG_BADVA] = "badva", + [HEX_SREG_IMASK] = "imask", + [HEX_SREG_GEVB] = "gevb", + [HEX_SREG_EVB] = "evb", + [HEX_SREG_MODECTL] = "modectl", + [HEX_SREG_SYSCFG] = "syscfg", + [HEX_SREG_IPENDAD] = "ipendad", + [HEX_SREG_VID] = "vid", + [HEX_SREG_VID1] = "vid1", + [HEX_SREG_BESTWAIT] = "bestwait", + [HEX_SREG_IEL] = "iel", + [HEX_SREG_SCHEDCFG] = "schedcfg", + [HEX_SREG_IAHL] = "iahl", + [HEX_SREG_CFGBASE] = "cfgbase", + [HEX_SREG_DIAG] = "diag", + [HEX_SREG_REV] = "rev", + [HEX_SREG_PCYCLELO] = "pcyclelo", + [HEX_SREG_PCYCLEHI] = "pcyclehi", + [HEX_SREG_ISDBST] = "isdbst", + [HEX_SREG_ISDBCFG0] = "isdbcfg0", + [HEX_SREG_ISDBCFG1] = "isdbcfg1", + [HEX_SREG_LIVELOCK] = "livelock", + [HEX_SREG_BRKPTPC0] = "brkptpc0", + [HEX_SREG_BRKPTCFG0] = "brkptcfg0", + [HEX_SREG_BRKPTPC1] = "brkptpc1", + [HEX_SREG_BRKPTCFG1] = "brkptcfg1", + [HEX_SREG_ISDBMBXIN] = "isdbmbxin", + [HEX_SREG_ISDBMBXOUT] = "isdbmbxout", + [HEX_SREG_ISDBEN] = "isdben", + [HEX_SREG_ISDBGPR] = "isdbgpr", + [HEX_SREG_PMUCNT4] = "pmucnt4", + [HEX_SREG_PMUCNT5] = "pmucnt5", + [HEX_SREG_PMUCNT6] = "pmucnt6", + [HEX_SREG_PMUCNT7] = "pmucnt7", + [HEX_SREG_PMUCNT0] = "pmucnt0", + [HEX_SREG_PMUCNT1] = "pmucnt1", + [HEX_SREG_PMUCNT2] = "pmucnt2", + [HEX_SREG_PMUCNT3] = "pmucnt3", + [HEX_SREG_PMUEVTCFG] = "pmuevtcfg", + [HEX_SREG_PMUSTID0] = "pmustid0", + [HEX_SREG_PMUEVTCFG1] = "pmuevtcfg1", + [HEX_SREG_PMUSTID1] = "pmustid1", + [HEX_SREG_TIMERLO] = "timerlo", + [HEX_SREG_TIMERHI] = "timerhi", + [HEX_SREG_PMUCFG] = "pmucfg", + [HEX_SREG_S59] = "s59", + [HEX_SREG_S60] = "s60", + [HEX_SREG_S61] = "s61", + [HEX_SREG_S62] = "s62", + [HEX_SREG_S63] = "s63", +}; + +static const char *get_sreg_name(uint32_t reg) +{ + if (reg < ARRAY_SIZE(hex_sreg_names) && hex_sreg_names[reg]) { + return hex_sreg_names[reg]; + } + return "UNKNOWN"; +} + +/* Global system register mutability masks */ +static const uint32_t global_sreg_immut_masks[NUM_SREGS] = { + [HEX_SREG_EVB] = 0x000000ff, + [HEX_SREG_MODECTL] = IMMUTABLE, + [HEX_SREG_SYSCFG] = 0x80001c00, + [HEX_SREG_IPENDAD] = IMMUTABLE, + [HEX_SREG_VID] = 0xfc00fc00, + [HEX_SREG_VID1] = 0xfc00fc00, + [HEX_SREG_BESTWAIT] = 0xfffffe00, + [HEX_SREG_IAHL] = 0x00000000, + [HEX_SREG_SCHEDCFG] = 0xfffffee0, + [HEX_SREG_CFGBASE] = IMMUTABLE, + [HEX_SREG_DIAG] = 0x00000000, + [HEX_SREG_REV] = IMMUTABLE, + [HEX_SREG_ISDBST] = IMMUTABLE, + [HEX_SREG_ISDBCFG0] = 0xe0000000, + [HEX_SREG_BRKPTPC0] = 0x00000003, + [HEX_SREG_BRKPTCFG0] = 0xfc007000, + [HEX_SREG_BRKPTPC1] = 0x00000003, + [HEX_SREG_BRKPTCFG1] = 0xfc007000, + [HEX_SREG_ISDBMBXIN] = IMMUTABLE, + [HEX_SREG_ISDBMBXOUT] = 0x00000000, + [HEX_SREG_ISDBEN] = 0xfffffffe, + [HEX_SREG_TIMERLO] = IMMUTABLE, + [HEX_SREG_TIMERHI] = IMMUTABLE, +}; + +static void hexagon_globalreg_init(Object *obj) +{ + HexagonGlobalRegState *s = HEXAGON_GLOBALREG(obj); + + memset(s->regs, 0, sizeof(s->regs)); +} + +static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val, + uint32_t reg_mask) +{ + if (reg_mask) { + return (new_val & ~reg_mask) | (cur_val & reg_mask); + } + return new_val; +} + +uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, + uint32_t htid) +{ + uint32_t value; + + if (!s) { + return 0; + } + g_assert(reg < NUM_SREGS); + g_assert(reg >= HEX_SREG_GLB_START); + + value = s->regs[reg]; + + trace_hexagon_globalreg_read(htid, get_sreg_name(reg), value); + return value; +} + +void hexagon_globalreg_write(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value, uint32_t htid) +{ + if (!s) { + return; + } + g_assert(reg < NUM_SREGS); + g_assert(reg >= HEX_SREG_GLB_START); + s->regs[reg] = value; + trace_hexagon_globalreg_write(htid, get_sreg_name(reg), value); +} + +uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value) +{ + uint32_t reg_mask; + + if (!s) { + return value; + } + g_assert(reg < NUM_SREGS); + g_assert(reg >= HEX_SREG_GLB_START); + reg_mask = global_sreg_immut_masks[reg]; + return reg_mask == IMMUTABLE ? + s->regs[reg] : + apply_write_mask(value, s->regs[reg], reg_mask); +} + +void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value) +{ + if (!s) { + return; + } + s->regs[reg] = hexagon_globalreg_masked_value(s, reg, value); +} + +uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s) +{ + g_assert(s); + return s->g_pcycle_base; +} + +void hexagon_globalreg_set_pcycle_base(HexagonGlobalRegState *s, + uint64_t value) +{ + g_assert(s); + s->g_pcycle_base = value; +} + +static void do_hexagon_globalreg_reset(HexagonGlobalRegState *s) +{ + uint32_t isdben_val = 0; + + g_assert(s); + memset(s->regs, 0, sizeof(s->regs)); + + s->g_pcycle_base = 0; + + s->regs[HEX_SREG_EVB] = s->boot_evb; + s->regs[HEX_SREG_CFGBASE] = HEXAGON_CFG_ADDR_BASE(s->config_table_addr); + s->regs[HEX_SREG_REV] = s->dsp_rev; + + if (s->isdben_etm_enable) { + isdben_val |= (1 << 0); /* ETM enable bit */ + } + if (s->isdben_dfd_enable) { + isdben_val |= (1 << 1); /* DFD enable bit */ + } + if (s->isdben_trusted) { + isdben_val |= (1 << 2); /* Trusted bit */ + } + if (s->isdben_secure) { + isdben_val |= (1 << 3); /* Secure bit */ + } + s->regs[HEX_SREG_ISDBEN] = isdben_val; + s->regs[HEX_SREG_MODECTL] = 0x1; + + /* + * These register indices are placeholders in these arrays + * and their actual values are synthesized from state elsewhere. + * We can initialize these with invalid values so that if we + * mistakenly generate reads, they will look obviously wrong. + */ + s->regs[HEX_SREG_PCYCLELO] = INVALID_REG_VAL; + s->regs[HEX_SREG_PCYCLEHI] = INVALID_REG_VAL; + s->regs[HEX_SREG_TIMERLO] = INVALID_REG_VAL; + s->regs[HEX_SREG_TIMERHI] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT0] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT1] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT2] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT3] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT4] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT5] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT6] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT7] = INVALID_REG_VAL; +} + +static void hexagon_globalreg_reset_hold(Object *obj, ResetType type) +{ + HexagonGlobalRegState *s = HEXAGON_GLOBALREG(obj); + do_hexagon_globalreg_reset(s); +} + +static const VMStateDescription vmstate_hexagon_globalreg = { + .name = "hexagon_globalreg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]){ + VMSTATE_UINT32_ARRAY(regs, HexagonGlobalRegState, NUM_SREGS), + VMSTATE_UINT64(g_pcycle_base, HexagonGlobalRegState), + VMSTATE_UINT32(boot_evb, HexagonGlobalRegState), + VMSTATE_UINT64(config_table_addr, HexagonGlobalRegState), + VMSTATE_UINT32(dsp_rev, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_etm_enable, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_dfd_enable, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_trusted, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_secure, HexagonGlobalRegState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property hexagon_globalreg_properties[] = { + DEFINE_PROP_UINT32("boot-evb", HexagonGlobalRegState, boot_evb, 0x0), + DEFINE_PROP_UINT64("config-table-addr", HexagonGlobalRegState, + config_table_addr, 0xffffffffULL), + DEFINE_PROP_UINT32("dsp-rev", HexagonGlobalRegState, dsp_rev, 0), + DEFINE_PROP_BOOL("isdben-etm-enable", HexagonGlobalRegState, + isdben_etm_enable, false), + DEFINE_PROP_BOOL("isdben-dfd-enable", HexagonGlobalRegState, + isdben_dfd_enable, false), + DEFINE_PROP_BOOL("isdben-trusted", HexagonGlobalRegState, + isdben_trusted, false), + DEFINE_PROP_BOOL("isdben-secure", HexagonGlobalRegState, + isdben_secure, false), +}; + +static void hexagon_globalreg_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = hexagon_globalreg_reset_hold; + dc->vmsd = &vmstate_hexagon_globalreg; + dc->user_creatable = false; + device_class_set_props(dc, hexagon_globalreg_properties); +} + +static const TypeInfo hexagon_globalreg_info = { + .name = TYPE_HEXAGON_GLOBALREG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HexagonGlobalRegState), + .instance_init = hexagon_globalreg_init, + .class_init = hexagon_globalreg_class_init, +}; + +static void hexagon_globalreg_register_types(void) +{ + type_register_static(&hexagon_globalreg_info); +} + +type_init(hexagon_globalreg_register_types)
diff --git a/hw/hexagon/hexagon_tlb.c b/hw/hexagon/hexagon_tlb.c new file mode 100644 index 0000000..b6d4aff --- /dev/null +++ b/hw/hexagon/hexagon_tlb.c
@@ -0,0 +1,467 @@ +/* + * Hexagon TLB QOM Device + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/resettable.h" +#include "migration/vmstate.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "target/hexagon/cpu.h" +#include "target/hexagon/cpu_bits.h" + +/* PTE (TLB entry) field extraction */ +#define GET_PTE_PPD(entry) extract64((entry), 0, 24) +#define GET_PTE_C(entry) extract64((entry), 24, 4) +#define GET_PTE_U(entry) extract64((entry), 28, 1) +#define GET_PTE_R(entry) extract64((entry), 29, 1) +#define GET_PTE_W(entry) extract64((entry), 30, 1) +#define GET_PTE_X(entry) extract64((entry), 31, 1) +#define GET_PTE_VPN(entry) extract64((entry), 32, 20) +#define GET_PTE_ASID(entry) extract64((entry), 52, 7) +#define GET_PTE_ATR0(entry) extract64((entry), 59, 1) +#define GET_PTE_ATR1(entry) extract64((entry), 60, 1) +#define GET_PTE_PA35(entry) extract64((entry), 61, 1) +#define GET_PTE_G(entry) extract64((entry), 62, 1) +#define GET_PTE_V(entry) extract64((entry), 63, 1) + +/* PPD (physical page descriptor) */ +static inline uint64_t GET_PPD(uint64_t entry) +{ + return GET_PTE_PPD(entry) | (GET_PTE_PA35(entry) << 24); +} + +#define NO_ASID (1 << 8) + +typedef enum { + PGSIZE_4K, + PGSIZE_16K, + PGSIZE_64K, + PGSIZE_256K, + PGSIZE_1M, + PGSIZE_4M, + PGSIZE_16M, + PGSIZE_64M, + PGSIZE_256M, + PGSIZE_1G, +} tlb_pgsize_t; + +#define NUM_PGSIZE_TYPES (PGSIZE_1G + 1) + +static const char *pgsize_str[NUM_PGSIZE_TYPES] = { + "4K", + "16K", + "64K", + "256K", + "1M", + "4M", + "16M", + "64M", + "256M", + "1G", +}; + +#define INVALID_MASK 0xffffffffLL + +static const uint64_t encmask_2_mask[] = { + 0x0fffLL, /* 4k, 0000 */ + 0x3fffLL, /* 16k, 0001 */ + 0xffffLL, /* 64k, 0010 */ + 0x3ffffLL, /* 256k, 0011 */ + 0xfffffLL, /* 1m, 0100 */ + 0x3fffffLL, /* 4m, 0101 */ + 0xffffffLL, /* 16m, 0110 */ + 0x3ffffffLL, /* 64m, 0111 */ + 0xfffffffLL, /* 256m, 1000 */ + 0x3fffffffLL, /* 1g, 1001 */ + INVALID_MASK, /* RSVD, 1010 */ +}; + +static inline tlb_pgsize_t hex_tlb_pgsize_type(uint64_t entry) +{ + if (entry == 0) { + qemu_log_mask(CPU_LOG_MMU, "%s: Supplied TLB entry was 0!\n", + __func__); + return 0; + } + tlb_pgsize_t size = ctz64(entry); + g_assert(size < NUM_PGSIZE_TYPES); + return size; +} + +static inline uint64_t hex_tlb_page_size_bytes(uint64_t entry) +{ + return 1ull << (qemu_target_page_bits() + 2 * hex_tlb_pgsize_type(entry)); +} + +static inline uint64_t hex_tlb_phys_page_num(uint64_t entry) +{ + uint32_t ppd = GET_PPD(entry); + return ppd >> 1; +} + +static inline uint64_t hex_tlb_phys_addr(uint64_t entry) +{ + uint64_t pagemask = encmask_2_mask[hex_tlb_pgsize_type(entry)]; + uint64_t pagenum = hex_tlb_phys_page_num(entry); + uint64_t PA = (pagenum << qemu_target_page_bits()) & (~pagemask); + return PA; +} + +static inline uint64_t hex_tlb_virt_addr(uint64_t entry) +{ + return (uint64_t)GET_PTE_VPN(entry) << qemu_target_page_bits(); +} + +bool hexagon_tlb_dump_entry(Monitor *mon, uint64_t entry) +{ + if (GET_PTE_V(entry)) { + uint64_t PA = hex_tlb_phys_addr(entry); + uint64_t VA = hex_tlb_virt_addr(entry); + monitor_printf(mon, "0x%016" PRIx64 ": ", entry); + monitor_printf(mon, "V:%" PRId64 " G:%" PRId64 + " A1:%" PRId64 " A0:%" PRId64, + GET_PTE_V(entry), + GET_PTE_G(entry), + GET_PTE_ATR1(entry), + GET_PTE_ATR0(entry)); + monitor_printf(mon, " ASID:0x%02" PRIx64 " VA:0x%08" PRIx64, + GET_PTE_ASID(entry), VA); + monitor_printf(mon, + " X:%" PRId64 " W:%" PRId64 " R:%" PRId64 + " U:%" PRId64 " C:%" PRId64, + GET_PTE_X(entry), + GET_PTE_W(entry), + GET_PTE_R(entry), + GET_PTE_U(entry), + GET_PTE_C(entry)); + monitor_printf(mon, " PA:0x%09" PRIx64 " SZ:%s (0x%" PRIx64 ")", + PA, pgsize_str[hex_tlb_pgsize_type(entry)], + hex_tlb_page_size_bytes(entry)); + monitor_printf(mon, "\n"); + return true; + } + + /* Not valid */ + return false; +} + +static inline bool hex_tlb_entry_match_noperm(uint64_t entry, uint32_t asid, + uint64_t VA) +{ + if (GET_PTE_V(entry)) { + if (GET_PTE_G(entry)) { + /* Global entry - ignore ASID */ + } else if (asid != NO_ASID) { + uint32_t tlb_asid = GET_PTE_ASID(entry); + if (tlb_asid != asid) { + return false; + } + } + + uint64_t page_size = hex_tlb_page_size_bytes(entry); + uint64_t page_start = + ROUND_DOWN(hex_tlb_virt_addr(entry), page_size); + if (page_start <= VA && VA < page_start + page_size) { + return true; + } + } + return false; +} + +static inline void hex_tlb_entry_get_perm(uint64_t entry, + MMUAccessType access_type, + int mmu_idx, int *prot, + int32_t *excp, int *cause_code) +{ + bool perm_x = GET_PTE_X(entry); + bool perm_w = GET_PTE_W(entry); + bool perm_r = GET_PTE_R(entry); + bool perm_u = GET_PTE_U(entry); + bool user_idx = mmu_idx == MMU_USER_IDX; + + if (mmu_idx == MMU_KERNEL_IDX) { + *prot = PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return; + } + + *prot = PAGE_VALID; + switch (access_type) { + case MMU_INST_FETCH: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_FETCH_NO_UPAGE; + } else if (!perm_x) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_FETCH_NO_XPAGE; + } + break; + case MMU_DATA_LOAD: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_UREAD; + } else if (!perm_r) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_READ; + } + break; + case MMU_DATA_STORE: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_UWRITE; + } else if (!perm_w) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_WRITE; + } + break; + } + + if (!user_idx || perm_u) { + if (perm_x) { + *prot |= PAGE_EXEC; + } + if (perm_r) { + *prot |= PAGE_READ; + } + if (perm_w) { + *prot |= PAGE_WRITE; + } + } +} + +static inline bool hex_tlb_entry_match(uint64_t entry, uint8_t asid, + uint32_t VA, + MMUAccessType access_type, hwaddr *PA, + int *prot, uint64_t *size, + int32_t *excp, int *cause_code, + int mmu_idx) +{ + if (hex_tlb_entry_match_noperm(entry, asid, VA)) { + hex_tlb_entry_get_perm(entry, access_type, mmu_idx, prot, excp, + cause_code); + *PA = hex_tlb_phys_addr(entry); + *size = hex_tlb_page_size_bytes(entry); + return true; + } + return false; +} + +static bool hex_tlb_is_match(uint64_t entry1, uint64_t entry2, + bool consider_gbit) +{ + bool valid1 = GET_PTE_V(entry1); + bool valid2 = GET_PTE_V(entry2); + uint64_t size1 = hex_tlb_page_size_bytes(entry1); + uint64_t vaddr1 = ROUND_DOWN(hex_tlb_virt_addr(entry1), size1); + uint64_t size2 = hex_tlb_page_size_bytes(entry2); + uint64_t vaddr2 = ROUND_DOWN(hex_tlb_virt_addr(entry2), size2); + int asid1 = GET_PTE_ASID(entry1); + int asid2 = GET_PTE_ASID(entry2); + bool gbit1 = GET_PTE_G(entry1); + bool gbit2 = GET_PTE_G(entry2); + + if (!valid1 || !valid2) { + return false; + } + + if (((vaddr1 <= vaddr2) && (vaddr2 < (vaddr1 + size1))) || + ((vaddr2 <= vaddr1) && (vaddr1 < (vaddr2 + size2)))) { + if (asid1 == asid2) { + return true; + } + if ((consider_gbit && gbit1) || gbit2) { + return true; + } + } + return false; +} + +/* Public API */ + +uint64_t hexagon_tlb_read(HexagonTLBState *tlb, uint32_t index) +{ + g_assert(index < tlb->num_entries); + return tlb->entries[index]; +} + +void hexagon_tlb_write(HexagonTLBState *tlb, uint32_t index, uint64_t value) +{ + g_assert(index < tlb->num_entries); + tlb->entries[index] = value; +} + +bool hexagon_tlb_find_match(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, MMUAccessType access_type, + hwaddr *PA, int *prot, uint64_t *size, + int32_t *excp, int *cause_code, int mmu_idx) +{ + *PA = 0; + *prot = 0; + *size = 0; + *excp = 0; + *cause_code = 0; + + for (uint32_t i = 0; i < tlb->num_entries; i++) { + if (hex_tlb_entry_match(tlb->entries[i], asid, VA, access_type, + PA, prot, size, excp, cause_code, mmu_idx)) { + return true; + } + } + return false; +} + +uint32_t hexagon_tlb_lookup(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, int *cause_code) +{ + uint32_t not_found = 0x80000000; + uint32_t idx = not_found; + + for (uint32_t i = 0; i < tlb->num_entries; i++) { + uint64_t entry = tlb->entries[i]; + if (hex_tlb_entry_match_noperm(entry, asid, VA)) { + if (idx != not_found) { + *cause_code = HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH; + break; + } + idx = i; + } + } + + if (idx == not_found) { + qemu_log_mask(CPU_LOG_MMU, + "%s: 0x%" PRIx32 ", 0x%08" PRIx32 " => NOT FOUND\n", + __func__, asid, VA); + } else { + qemu_log_mask(CPU_LOG_MMU, + "%s: 0x%" PRIx32 ", 0x%08" PRIx32 " => %d\n", + __func__, asid, VA, idx); + } + + return idx; +} + +/* + * Return codes: + * 0 or positive index of match + * -1 multiple matches + * -2 no match + */ +int hexagon_tlb_check_overlap(HexagonTLBState *tlb, uint64_t entry, + uint64_t index) +{ + int matches = 0; + int last_match = 0; + + for (uint32_t i = 0; i < tlb->num_entries; i++) { + if (hex_tlb_is_match(entry, tlb->entries[i], false)) { + matches++; + last_match = i; + } + } + + if (matches == 1) { + return last_match; + } + if (matches == 0) { + return -2; + } + return -1; +} + +void hexagon_tlb_dump(Monitor *mon, HexagonTLBState *tlb) +{ + for (uint32_t i = 0; i < tlb->num_entries; i++) { + hexagon_tlb_dump_entry(mon, tlb->entries[i]); + } +} + +uint32_t hexagon_tlb_get_num_entries(HexagonTLBState *tlb) +{ + return tlb->num_entries; +} + +/* QOM lifecycle */ + +static void hexagon_tlb_init(Object *obj) +{ +} + +static void hexagon_tlb_realize(DeviceState *dev, Error **errp) +{ + HexagonTLBState *s = HEXAGON_TLB(dev); + + if (s->num_entries == 0 || s->num_entries > MAX_TLB_ENTRIES) { + error_setg(errp, "Invalid TLB num-entries: %" PRIu32, + s->num_entries); + return; + } + s->entries = g_new0(uint64_t, s->num_entries); +} + +static void hexagon_tlb_unrealize(DeviceState *dev) +{ + HexagonTLBState *s = HEXAGON_TLB(dev); + g_free(s->entries); + s->entries = NULL; +} + +static void hexagon_tlb_reset_hold(Object *obj, ResetType type) +{ + HexagonTLBState *s = HEXAGON_TLB(obj); + if (s->entries) { + memset(s->entries, 0, sizeof(uint64_t) * s->num_entries); + } +} + +static const VMStateDescription vmstate_hexagon_tlb = { + .name = "hexagon-tlb", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(num_entries, HexagonTLBState), + VMSTATE_VARRAY_UINT32_ALLOC(entries, HexagonTLBState, num_entries, + 0, vmstate_info_uint64, uint64_t), + VMSTATE_END_OF_LIST() + }, +}; + +static const Property hexagon_tlb_properties[] = { + DEFINE_PROP_UINT32("num-entries", HexagonTLBState, num_entries, + MAX_TLB_ENTRIES), +}; + +static void hexagon_tlb_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = hexagon_tlb_realize; + dc->unrealize = hexagon_tlb_unrealize; + rc->phases.hold = hexagon_tlb_reset_hold; + dc->vmsd = &vmstate_hexagon_tlb; + dc->user_creatable = false; + device_class_set_props(dc, hexagon_tlb_properties); +} + +static const TypeInfo hexagon_tlb_info = { + .name = TYPE_HEXAGON_TLB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HexagonTLBState), + .instance_init = hexagon_tlb_init, + .class_init = hexagon_tlb_class_init, +}; + +static void hexagon_tlb_register_types(void) +{ + type_register_static(&hexagon_tlb_info); +} + +type_init(hexagon_tlb_register_types)
diff --git a/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc b/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc new file mode 100644 index 0000000..442cbe3 --- /dev/null +++ b/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc
@@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +static const struct hexagon_machine_config SA8775P_cdsp0 = { + .cfgbase = 0x24000000 + 0x180000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0x26300000 + 0x90000, + .l2vic_size = 0x00001000, + .csr_base = 0x26300000, + .qtmr_region = 0x26300000 + 0xA1000, + .cfgtable = { + .l2tcm_base = 0x00002400, + .reserved0 = 0x00000000, + .subsystem_base = 0x00002638, + .etm_base = 0x00002419, + .l2cfg_base = 0x0000241a, + .reserved1 = 0x0000241b, + .l1s0_base = 0x00002500, + .axi2_lowaddr = 0x00000000, + .streamer_base = 0x00000000, + .reserved2 = 0x00000000, + .fastl2vic_base = 0x0000241e, + .jtlb_size_entries = 0x00000080, + .coproc_present = 0x00000001, + .ext_contexts = 0x00000004, + .vtcm_base = 0x00002500, + .vtcm_size_kb = 0x00002000, + .l2tag_size = 0x00000400, + .l2ecomem_size = 0x00000000, + .thread_enable_mask = 0x0000003f, + .eccreg_base = 0x0000241f, + .l2line_size = 0x00000080, + .tiny_core = 0x00000000, + .l2itcm_size = 0x00000000, + .l2itcm_base = 0x00002400, + .reserved3 = 0x00000000, + .dtm_present = 0x00000000, + .dma_version = 0x00000003, + .hvx_vec_log_length = 0x00000007, + .core_id = 0x00000000, + .core_count = 0x00000000, + .coproc2_reg0 = 0x00000040, + .coproc2_reg1 = 0x00000020, + .v2x_mode = 0x00000001, + .coproc2_reg2 = 0x00000008, + .coproc2_reg3 = 0x00000020, + .coproc2_reg4 = 0x00000000, + .coproc2_reg5 = 0x00000002, + .coproc2_reg6 = 0x00000016, + .coproc2_reg7 = 0x00000006, + .acd_preset = 0x00000001, + .mnd_preset = 0x00000000, + .l1d_size_kb = 0x00000010, + .l1i_size_kb = 0x00000020, + .l1d_write_policy = 0x00000002, + .vtcm_bank_width = 0x00000080, + .reserved4 = 0x00000001, + .reserved5 = 0x00000000, + .reserved6 = 0x00000003, + .coproc2_cvt_mpy_size = 0x0000000a, + .consistency_domain = 0x000000e0, + .capacity_domain = 0x00000080, + .axi3_lowaddr = 0x00000000, + }, +};
diff --git a/hw/hexagon/machine_cfg_v66g_1024.h.inc b/hw/hexagon/machine_cfg_v66g_1024.h.inc new file mode 100644 index 0000000..cc4d89b --- /dev/null +++ b/hw/hexagon/machine_cfg_v66g_1024.h.inc
@@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +static const struct hexagon_machine_config v66g_1024 = { + .cfgbase = 0xd8180000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0xfc910000, + .l2vic_size = 0x00001000, + .csr_base = 0xfc900000, + .qtmr_region = 0xfc921000, + .cfgtable = { + .l2tcm_base = 0x0000d800, + .reserved0 = 0x0000d400, + .subsystem_base = 0x0000fc90, + .etm_base = 0x0000d805, + .l2cfg_base = 0x0000d81a, + .reserved1 = 0x00000000, + .l1s0_base = 0x0000d820, + .axi2_lowaddr = 0x00003000, + .streamer_base = 0x00000000, + .reserved2 = 0x0000d819, + .fastl2vic_base = 0x0000d81e, + .jtlb_size_entries = 0x00000080, + .coproc_present = 0x00000001, + .ext_contexts = 0x00000004, + .vtcm_base = 0x0000d820, + .vtcm_size_kb = 0x00000100, + .l2tag_size = 0x00000400, + .l2ecomem_size = 0x00000400, + .thread_enable_mask = 0x0000000f, + .eccreg_base = 0x0000d81f, + .l2line_size = 0x00000080, + .tiny_core = 0x00000000, + .l2itcm_size = 0x00000000, + .l2itcm_base = 0x0000d820, + .reserved3 = 0x00000000, + .dtm_present = 0x00000000, + .dma_version = 0x00000000, + .hvx_vec_log_length = 0x00000080, + .core_id = 0x00000000, + .core_count = 0x00000000, + .coproc2_reg0 = 0x00000000, + .coproc2_reg1 = 0x00000000, + .v2x_mode = 0x00000000, + .coproc2_reg2 = 0x00000000, + .coproc2_reg3 = 0x00000000, + .coproc2_reg4 = 0x00000000, + .coproc2_reg5 = 0x00000000, + .coproc2_reg6 = 0x00000000, + .coproc2_reg7 = 0x00000000, + .acd_preset = 0x00000000, + .mnd_preset = 0x00000000, + .l1d_size_kb = 0x00000000, + .l1i_size_kb = 0x00000000, + .l1d_write_policy = 0x00000000, + .vtcm_bank_width = 0x00000000, + .reserved4 = 0x00000000, + .reserved5 = 0x00000000, + .reserved6 = 0x00000000, + .coproc2_cvt_mpy_size = 0x00000000, + .consistency_domain = 0x00000000, + .capacity_domain = 0x00000000, + .axi3_lowaddr = 0x00000000, + }, +};
diff --git a/hw/hexagon/machine_cfg_v68n_1024.h.inc b/hw/hexagon/machine_cfg_v68n_1024.h.inc new file mode 100644 index 0000000..82619c4 --- /dev/null +++ b/hw/hexagon/machine_cfg_v68n_1024.h.inc
@@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +static const struct hexagon_machine_config v68n_1024 = { + .cfgbase = 0xde000000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0xfc910000, + .l2vic_size = 0x00001000, + .csr_base = 0xfc900000, + .qtmr_region = 0xfc921000, + .cfgtable = { + .l2tcm_base = 0x0000d800, + .reserved0 = 0x00000000, + .subsystem_base = 0x0000fc90, + .etm_base = 0x0000d819, + .l2cfg_base = 0x0000d81a, + .reserved1 = 0x00000000, + .l1s0_base = 0x0000d840, + .axi2_lowaddr = 0x00003000, + .streamer_base = 0x0000d81c, + .reserved2 = 0x0000d81d, + .fastl2vic_base = 0x0000d81e, + .jtlb_size_entries = 0x00000080, + .coproc_present = 0x00000001, + .ext_contexts = 0x00000004, + .vtcm_base = 0x0000d840, + .vtcm_size_kb = 0x00001000, + .l2tag_size = 0x00000400, + .l2ecomem_size = 0x00000400, + .thread_enable_mask = 0x0000003f, + .eccreg_base = 0x0000d81f, + .l2line_size = 0x00000080, + .tiny_core = 0x00000000, + .l2itcm_size = 0x00000000, + .l2itcm_base = 0x0000d820, + .reserved3 = 0x00000000, + .dtm_present = 0x00000000, + .dma_version = 0x00000001, + .hvx_vec_log_length = 0x00000007, + .core_id = 0x00000000, + .core_count = 0x00000000, + .coproc2_reg0 = 0x00000040, + .coproc2_reg1 = 0x00000020, + .v2x_mode = 0x1f1f1f1f, + .coproc2_reg2 = 0x1f1f1f1f, + .coproc2_reg3 = 0x1f1f1f1f, + .coproc2_reg4 = 0x1f1f1f1f, + .coproc2_reg5 = 0x1f1f1f1f, + .coproc2_reg6 = 0x1f1f1f1f, + .coproc2_reg7 = 0x1f1f1f1f, + .acd_preset = 0x1f1f1f1f, + .mnd_preset = 0x1f1f1f1f, + .l1d_size_kb = 0x1f1f1f1f, + .l1i_size_kb = 0x1f1f1f1f, + .l1d_write_policy = 0x1f1f1f1f, + .vtcm_bank_width = 0x1f1f1f1f, + .reserved4 = 0x1f1f1f1f, + .reserved5 = 0x1f1f1f1f, + .reserved6 = 0x1f1f1f1f, + .coproc2_cvt_mpy_size = 0x1f1f1f1f, + .consistency_domain = 0x1f1f1f1f, + .capacity_domain = 0x1f1f1f1f, + .axi3_lowaddr = 0x1f1f1f1f, + }, +}; +
diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build new file mode 100644 index 0000000..bade3a3 --- /dev/null +++ b/hw/hexagon/meson.build
@@ -0,0 +1,7 @@ +hexagon_ss = ss.source_set() +hexagon_ss.add(files('hexagon_tlb.c')) +hexagon_ss.add(files('hexagon_globalreg.c')) +hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c')) +hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c')) + +hw_arch += {'hexagon': hexagon_ss}
diff --git a/hw/hexagon/trace-events b/hw/hexagon/trace-events new file mode 100644 index 0000000..5d623ed --- /dev/null +++ b/hw/hexagon/trace-events
@@ -0,0 +1,3 @@ +# Hexagon global register access +hexagon_globalreg_read(uint32_t htid, const char *reg_name, uint32_t value) "htid=%u reg=%s value=0x%x" +hexagon_globalreg_write(uint32_t htid, const char *reg_name, uint32_t value) "htid=%u reg=%s value=0x%x"
diff --git a/hw/hexagon/trace.h b/hw/hexagon/trace.h new file mode 100644 index 0000000..9e0b39f --- /dev/null +++ b/hw/hexagon/trace.h
@@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "trace/trace-hw_hexagon.h"
diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c new file mode 100644 index 0000000..a363899 --- /dev/null +++ b/hw/hexagon/virt.c
@@ -0,0 +1,347 @@ +/* + * Hexagon virt emulation + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hexagon/virt.h" +#include "elf.h" +#include "hw/char/pl011.h" +#include "hw/core/clock.h" +#include "hw/core/sysbus-fdt.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/core/loader.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/qdev-clock.h" +#include "hw/core/register.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/units.h" +#include "machine_cfg_v68n_1024.h.inc" +#include "system/address-spaces.h" +#include "system/device_tree.h" +#include "system/reset.h" +#include "system/system.h" +#include <libfdt.h> + +enum { + VIRT_UART0, + VIRT_FDT, +}; + +static const MemMapEntry base_memmap[] = { + [VIRT_UART0] = { 0x10000000, 0x00000200 }, + [VIRT_FDT] = { 0x99800000, 0x00400000 }, +}; + + +static void create_fdt(HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + void *fdt = create_device_tree(&vms->fdt_size); + uint8_t rng_seed[32]; + + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + ms->fdt = fdt; + + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1); + qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150"); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); +} + +static void fdt_add_hvx(HexagonVirtMachineState *vms, + const struct hexagon_machine_config *m_cfg) +{ + const MachineState *ms = MACHINE(vms); + uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024; + if (vtcm_size_bytes > 0) { + memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes, + &error_fatal); + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16, + &vms->vtcm); + + qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm"); + qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible", + "qcom,hexagon_vtcm"); + + assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t)); + qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0, + m_cfg->cfgtable.vtcm_base << 16, + vtcm_size_bytes); + } + + if (m_cfg->cfgtable.ext_contexts > 0) { + qemu_fdt_add_subnode(ms->fdt, "/soc/hvx"); + qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible", + "qcom,hexagon-hvx"); + qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts", + m_cfg->cfgtable.ext_contexts); + qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength", + m_cfg->cfgtable.hvx_vec_log_length); + } +} + +static int32_t fdt_add_clocks(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + int32_t clk_phandle = qemu_fdt_alloc_phandle(ms->fdt); + + qemu_fdt_add_subnode(ms->fdt, "/apb-pclk"); + qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible", "fixed-clock"); + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0); + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency", 24000000); + qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names", + "clk24mhz"); + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clk_phandle); + + return clk_phandle; +} + +static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, + int32_t clk_phandle) +{ + char *nodename; + hwaddr base = base_memmap[uart].base; + hwaddr size = base_memmap[uart].size; + assert(uart == 0); + const char compat[] = "arm,pl011\0arm,primecell"; + const char clocknames[] = "uartclk\0apb_pclk"; + MachineState *ms = MACHINE(vms); + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_new(TYPE_PL011); + s = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + qdev_connect_clock_in(dev, "clk", vms->apb_clk); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, base); + + nodename = g_strdup_printf("/pl011@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + + /* Note that we can't use setprop_string because of the embedded NUL */ + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clk_phandle, + clk_phandle); + qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, + sizeof(clocknames)); + + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_add_subnode(ms->fdt, "/aliases"); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename); + + g_free(nodename); +} + +static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + 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 (int num = ms->smp.cpus - 1; num >= 0; num--) { + char *nodename = g_strdup_printf("/cpus/cpu@%d", num); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); + 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); + } +} + + + +static void virt_instance_init(Object *obj) +{ + HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj); + + create_fdt(vms); +} + +void hexagon_load_fdt(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + hwaddr fdt_addr = base_memmap[VIRT_FDT].base; + uint32_t fdtsize = vms->fdt_size; + + g_assert(fdtsize <= base_memmap[VIRT_FDT].size); + /* copy in the device tree */ + rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr, + &address_space_memory); + qemu_register_reset_nosnapshotload( + qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize)); +} + +static uint64_t load_kernel(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + uint64_t entry = 0; + if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry, NULL, + NULL, NULL, 0, EM_HEXAGON, 0, 0, &address_space_memory, + false, NULL) > 0) { + return entry; + } + error_report("error loading '%s'", ms->kernel_filename); + exit(1); +} + +static uint64_t load_bios(HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + uint64_t bios_addr = 0x0; /* Load BIOS at reset vector address 0x0 */ + int bios_size; + + bios_size = load_image_targphys(ms->firmware ?: "", + bios_addr, 64 * 1024, NULL); + if (bios_size < 0) { + error_report("Could not load BIOS '%s'", ms->firmware ?: ""); + exit(1); + } + + return bios_addr; /* Return entry point at address 0x0 */ +} + +static void do_cpu_reset(void *opaque) +{ + HexagonCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + cpu_reset(cs); +} + +static void virt_init(MachineState *ms) +{ + HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms); + const struct hexagon_machine_config *m_cfg = &v68n_1024; + DeviceState *gsregs_dev; + DeviceState *tlb_dev; + DeviceState *cpu0; + int32_t clk_phandle; + + qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline); + + vms->sys = get_system_memory(); + + /* Create APB clock for peripherals */ + vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk"); + clock_set_hz(vms->apb_clk, 24000000); + + memory_region_init_ram(&vms->parent_obj.ram, NULL, "ddr.ram", + ms->ram_size, &error_fatal); + memory_region_add_subregion(vms->sys, 0x0, &vms->parent_obj.ram); + + if (m_cfg->l2tcm_size) { + memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size, + &error_fatal); + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base << 16, + &vms->tcm); + } + + memory_region_init_rom(&vms->parent_obj.cfgtable_rom, NULL, + "config_table.rom", sizeof(m_cfg->cfgtable), + &error_fatal); + memory_region_add_subregion(vms->sys, m_cfg->cfgbase, + &vms->parent_obj.cfgtable_rom); + fdt_add_hvx(vms, m_cfg); + + gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); + object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev)); + qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase); + qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev); + sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal); + + tlb_dev = qdev_new(TYPE_HEXAGON_TLB); + object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev)); + qdev_prop_set_uint32(tlb_dev, "num-entries", + m_cfg->cfgtable.jtlb_size_entries); + sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); + + cpu0 = NULL; + for (int i = 0; i < ms->smp.cpus; i++) { + HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type)); + qemu_register_reset(do_cpu_reset, cpu); + + if (i == 0) { + cpu0 = DEVICE(cpu); + if (ms->kernel_filename) { + uint64_t entry = load_kernel(vms); + qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + } else if (ms->firmware) { + uint64_t entry = load_bios(vms); + qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + } + } + qdev_prop_set_uint32(DEVICE(cpu), "htid", i); + qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); + object_property_set_link(OBJECT(cpu), "global-regs", + OBJECT(gsregs_dev), &error_fatal); + object_property_set_link(OBJECT(cpu), "tlb", + OBJECT(tlb_dev), &error_fatal); + + qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + } + fdt_add_cpu_nodes(vms); + clk_phandle = fdt_add_clocks(vms); + fdt_add_uart(vms, VIRT_UART0, clk_phandle); + + rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, + sizeof(m_cfg->cfgtable), m_cfg->cfgbase, + &address_space_memory); + + hexagon_load_fdt(vms); +} + + +static void virt_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Hexagon Virtual Machine"; + mc->init = virt_init; + mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v68"); + mc->default_ram_size = 4 * GiB; + mc->max_cpus = 8; + mc->default_cpus = 8; + mc->is_default = false; + mc->default_kernel_irqchip_split = false; + mc->block_default_type = IF_VIRTIO; + mc->default_boot_order = NULL; + mc->no_cdrom = 1; + mc->numa_mem_supported = false; + mc->default_nic = "virtio-mmio-bus"; +} + + +static const TypeInfo virt_machine_types[] = { { + .name = TYPE_HEXAGON_VIRT_MACHINE, + .parent = TYPE_HEXAGON_COMMON_MACHINE, + .instance_size = sizeof(HexagonVirtMachineState), + .class_init = virt_class_init, + .instance_init = virt_instance_init, +} }; + +DEFINE_TYPES(virt_machine_types)
diff --git a/hw/meson.build b/hw/meson.build index ef65ba5..7fa81db 100644 --- a/hw/meson.build +++ b/hw/meson.build
@@ -3,6 +3,7 @@ subdir('arm') subdir('avr') subdir('hppa') +subdir('hexagon') subdir('xenpv') # i386 uses it subdir('i386') subdir('loongarch')
diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h new file mode 100644 index 0000000..1034b09 --- /dev/null +++ b/include/hw/hexagon/hexagon.h
@@ -0,0 +1,161 @@ +/* + * Hexagon Baseboard System emulation. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#ifndef HW_HEXAGON_H +#define HW_HEXAGON_H + +#include "system/memory.h" +#include "hw/core/boards.h" + +struct hexagon_board_boot_info { + uint64_t ram_size; + const char *kernel_filename; + uint32_t kernel_elf_flags; +}; + +typedef enum { + unknown_rev = 0, + v66_rev = 0xa666, + v67_rev = 0x2667, + v68_rev = 0x8d68, + v69_rev = 0x8c69, + v71_rev = 0x8c71, + v73_rev = 0x8c73, + v73m_rev = 0xcc73, +} Rev_t; +#define HEXAGON_LATEST_REV v73 +#define HEXAGON_LATEST_REV_UPPER V73 + +/* + * Config table address bases represent bits [35:16]. + */ +#define HEXAGON_CFG_ADDR_BASE(addr) (((addr) >> 16) & 0x0fffff) + +#define HEXAGON_CFGSPACE_ENTRIES (128) + +union hexagon_config_table { + struct { + /* Base address of L2TCM space */ + uint32_t l2tcm_base; + uint32_t reserved0; + /* Base address of subsystem space */ + uint32_t subsystem_base; + /* Base address of ETM space */ + uint32_t etm_base; + /* Base address of L2 configuration space */ + uint32_t l2cfg_base; + uint32_t reserved1; + /* Base address of L1S */ + uint32_t l1s0_base; + /* Base address of AXI2 */ + uint32_t axi2_lowaddr; + /* Base address of streamer base */ + uint32_t streamer_base; + uint32_t reserved2; + /* Base address of fast L2VIC */ + uint32_t fastl2vic_base; + /* Number of entries in JTLB */ + uint32_t jtlb_size_entries; + /* Coprocessor type */ + uint32_t coproc_present; + /* Number of extension execution contexts available */ + uint32_t ext_contexts; + /* Base address of Hexagon Vector Tightly Coupled Memory (VTCM) */ + uint32_t vtcm_base; + /* Size of VTCM (in KB) */ + uint32_t vtcm_size_kb; + /* L2 tag size */ + uint32_t l2tag_size; + /* Amount of physical L2 memory in released version */ + uint32_t l2ecomem_size; + /* Hardware threads available on the core */ + uint32_t thread_enable_mask; + /* Base address of the ECC registers */ + uint32_t eccreg_base; + /* L2 line size */ + uint32_t l2line_size; + /* Small Core processor (also implies audio extension) */ + uint32_t tiny_core; + /* Size of L2TCM */ + uint32_t l2itcm_size; + /* Base address of L2-ITCM */ + uint32_t l2itcm_base; + uint32_t reserved3; + /* DTM is present */ + uint32_t dtm_present; + /* Version of the DMA */ + uint32_t dma_version; + /* Native HVX vector length in log of bytes */ + uint32_t hvx_vec_log_length; + /* Core ID of the multi-core */ + uint32_t core_id; + /* Number of multi-core cores */ + uint32_t core_count; + uint32_t coproc2_reg0; + uint32_t coproc2_reg1; + /* Supported HVX vector length */ + uint32_t v2x_mode; + uint32_t coproc2_reg2; + uint32_t coproc2_reg3; + uint32_t coproc2_reg4; + uint32_t coproc2_reg5; + uint32_t coproc2_reg6; + uint32_t coproc2_reg7; + /* Voltage droop mitigation technique parameter */ + uint32_t acd_preset; + /* Voltage droop mitigation technique parameter */ + uint32_t mnd_preset; + /* L1 data cache size (in KB) */ + uint32_t l1d_size_kb; + /* L1 instruction cache size in (KB) */ + uint32_t l1i_size_kb; + /* L1 data cache write policy: see HexagonL1WritePolicy */ + uint32_t l1d_write_policy; + /* VTCM bank width */ + uint32_t vtcm_bank_width; + uint32_t reserved4; + uint32_t reserved5; + uint32_t reserved6; + uint32_t coproc2_cvt_mpy_size; + uint32_t consistency_domain; + uint32_t capacity_domain; + uint32_t axi3_lowaddr; + uint32_t coproc2_int8_subcolumns; + uint32_t corecfg_present; + uint32_t coproc2_fp16_acc_exp; + uint32_t AXIM2_secondary_base; + }; + uint32_t raw[HEXAGON_CFGSPACE_ENTRIES]; +}; + +struct hexagon_machine_config { + /* Base address of config table */ + uint32_t cfgbase; + /* Size of L2 TCM */ + uint32_t l2tcm_size; + /* Base address of L2VIC */ + uint32_t l2vic_base; + /* Size of L2VIC region */ + uint32_t l2vic_size; + /* QTimer csr base */ + uint32_t csr_base; + uint32_t qtmr_region; + union hexagon_config_table cfgtable; +}; + +#define TYPE_HEXAGON_COMMON_MACHINE "hexagon-common-machine" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonCommonMachineState, HEXAGON_COMMON_MACHINE) + +struct HexagonCommonMachineState { + MachineState parent_obj; + + MemoryRegion ram; + MemoryRegion cfgtable_rom; +}; + +#endif
diff --git a/include/hw/hexagon/hexagon_globalreg.h b/include/hw/hexagon/hexagon_globalreg.h new file mode 100644 index 0000000..9500998 --- /dev/null +++ b/include/hw/hexagon/hexagon_globalreg.h
@@ -0,0 +1,55 @@ +/* + * Hexagon Global Registers QOM Object + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_GLOBALREG_H +#define HEXAGON_GLOBALREG_H + +#include "hw/core/qdev.h" +#include "hw/core/sysbus.h" +#include "qom/object.h" +#include "target/hexagon/cpu.h" + +#define TYPE_HEXAGON_GLOBALREG "hexagon-globalreg" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonGlobalRegState, HEXAGON_GLOBALREG) + +struct HexagonGlobalRegState { + SysBusDevice parent_obj; + + /* Array of system registers */ + uint32_t regs[NUM_SREGS]; + + /* Global performance cycle counter base */ + uint64_t g_pcycle_base; + + /* Properties for global register reset values */ + uint32_t boot_evb; /* Boot Exception Vector Base (HEX_SREG_EVB) */ + uint64_t config_table_addr; /* Configuration table base */ + uint32_t dsp_rev; /* DSP revision register (HEX_SREG_REV) */ + + /* ISDB properties */ + bool isdben_etm_enable; /* ISDB ETM enable bit */ + bool isdben_dfd_enable; /* ISDB DFD enable bit */ + bool isdben_trusted; /* ISDB trusted mode bit */ + bool isdben_secure; /* ISDB secure mode bit */ +}; + +/* Public interface functions */ +uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, + uint32_t htid); +void hexagon_globalreg_write(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value, uint32_t htid); +uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value); +void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value); + +/* Global performance cycle counter access */ +uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s); +void hexagon_globalreg_set_pcycle_base(HexagonGlobalRegState *s, + uint64_t value); + +#endif /* HEXAGON_GLOBALREG_H */
diff --git a/include/hw/hexagon/hexagon_tlb.h b/include/hw/hexagon/hexagon_tlb.h new file mode 100644 index 0000000..90d9ed8 --- /dev/null +++ b/include/hw/hexagon/hexagon_tlb.h
@@ -0,0 +1,46 @@ +/* + * Hexagon TLB QOM Device + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HEXAGON_TLB_H +#define HW_HEXAGON_TLB_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" +#include "exec/hwaddr.h" +#include "exec/mmu-access-type.h" +#include "monitor/monitor.h" +#define TYPE_HEXAGON_TLB "hexagon-tlb" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonTLBState, HEXAGON_TLB) + +struct HexagonTLBState { + SysBusDevice parent_obj; + + uint32_t num_entries; + uint64_t *entries; +}; + +uint64_t hexagon_tlb_read(HexagonTLBState *tlb, uint32_t index); +void hexagon_tlb_write(HexagonTLBState *tlb, uint32_t index, uint64_t value); + +bool hexagon_tlb_find_match(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, MMUAccessType access_type, + hwaddr *PA, int *prot, uint64_t *size, + int32_t *excp, int *cause_code, int mmu_idx); + +uint32_t hexagon_tlb_lookup(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, int *cause_code); + +int hexagon_tlb_check_overlap(HexagonTLBState *tlb, uint64_t entry, + uint64_t index); + +void hexagon_tlb_dump(Monitor *mon, HexagonTLBState *tlb); + +bool hexagon_tlb_dump_entry(Monitor *mon, uint64_t entry); + +uint32_t hexagon_tlb_get_num_entries(HexagonTLBState *tlb); + +#endif /* HW_HEXAGON_TLB_H */
diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h new file mode 100644 index 0000000..fcb4776 --- /dev/null +++ b/include/hw/hexagon/virt.h
@@ -0,0 +1,30 @@ +/* + * Definitions for hexagon virt board. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HEXAGONVIRT_H +#define HW_HEXAGONVIRT_H + +#include "hw/hexagon/hexagon.h" +#include "target/hexagon/cpu.h" + +struct HexagonVirtMachineState { + HexagonCommonMachineState parent_obj; + + int fdt_size; + MemoryRegion *sys; + MemoryRegion tcm; + MemoryRegion vtcm; + MemoryRegion bios; + Clock *apb_clk; +}; + +void hexagon_load_fdt(const struct HexagonVirtMachineState *vms); + +#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt") +OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE) + +#endif /* HW_HEXAGONVIRT_H */
diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 9464246..0958c51 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c
@@ -22,6 +22,7 @@ #include "qemu.h" #include "user-internals.h" #include "user/cpu_loop.h" +#include "target/hexagon/internal.h" #include "signal-common.h" #include "internal.h" @@ -60,6 +61,28 @@ env->gpr[0] = ret; } break; + case HEX_EVENT_PRECISE: + switch (env->cause_code) { + case HEX_CAUSE_FETCH_NO_UPAGE: + case HEX_CAUSE_PRIV_NO_UREAD: + case HEX_CAUSE_PRIV_NO_UWRITE: + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, + env->gpr[HEX_REG_PC]); + + break; + case HEX_CAUSE_PRIV_USER_NO_GINSN: + case HEX_CAUSE_PRIV_USER_NO_SINSN: + case HEX_CAUSE_INVALID_PACKET: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, + env->gpr[HEX_REG_PC]); + break; + default: + EXCP_DUMP(env, "\nqemu: unhandled CPU precise exception " + "cause code 0x%x - aborting\n", + env->cause_code); + exit(EXIT_FAILURE); + } + break; case HEX_CAUSE_PC_NOT_ALIGNED: force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, env->gpr[HEX_REG_R31]);
diff --git a/meson.build b/meson.build index e026851..d4206a5 100644 --- a/meson.build +++ b/meson.build
@@ -3599,6 +3599,7 @@ 'hw/display', 'hw/dma', 'hw/fsi', + 'hw/hexagon', 'hw/hyperv', 'hw/i2c', 'hw/i3c',
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index dfc95a0..00fed79 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c
@@ -71,7 +71,7 @@ QEMU_ARCH_SPARC | \ QEMU_ARCH_XTENSA) #define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X) -#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K) +#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K | QEMU_ARCH_HEXAGON) /* Please keep this table sorted by typename. */ static const QDevAlias qdev_alias_table[] = {
diff --git a/target/Kconfig b/target/Kconfig index 3c73e3b..0288a3f 100644 --- a/target/Kconfig +++ b/target/Kconfig
@@ -16,6 +16,7 @@ source sparc/Kconfig source tricore/Kconfig source xtensa/Kconfig +source hexagon/Kconfig config TARGET_BIG_ENDIAN bool
diff --git a/target/hexagon/Kconfig b/target/hexagon/Kconfig new file mode 100644 index 0000000..7e556f3 --- /dev/null +++ b/target/hexagon/Kconfig
@@ -0,0 +1,2 @@ +config HEXAGON + bool
diff --git a/target/hexagon/arch.c b/target/hexagon/arch.c index e17e714..0a400bf 100644 --- a/target/hexagon/arch.c +++ b/target/hexagon/arch.c
@@ -208,6 +208,11 @@ * model it in qemu user mode. */ #define RAISE_FP_EXCEPTION do {} while (0) +#else + /* + * To be implemented. + */ +#define RAISE_FP_EXCEPTION do { g_assert_not_reached(); } while (0) #endif #define SOFTFLOAT_TEST_FLAG(FLAG, MYF, MYE) \
diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 9e3a05f..6c55063 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc
@@ -52,6 +52,9 @@ DEF_ATTRIB(REGWRSIZE_8B, "Memory width is 8 bytes", "", "") DEF_ATTRIB(MEMLIKE, "Memory-like instruction", "", "") DEF_ATTRIB(MEMLIKE_PACKET_RULES, "follows Memory-like packet rules", "", "") +DEF_ATTRIB(CACHEOP, "Cache operation", "", "") +DEF_ATTRIB(COPBYADDRESS, "Cache operation by address", "", "") +DEF_ATTRIB(COPBYIDX, "Cache operation by index", "", "") DEF_ATTRIB(RELEASE, "Releases a lock", "", "") DEF_ATTRIB(ACQUIRE, "Acquires a lock", "", "") @@ -101,7 +104,9 @@ /* access to implicit registers */ DEF_ATTRIB(IMPLICIT_WRITES_LR, "Writes the link register", "", "UREG.LR") +DEF_ATTRIB(IMPLICIT_READS_PC, "Reads the program counter", "UREG.PC", "") DEF_ATTRIB(IMPLICIT_WRITES_SP, "Writes the stack pointer", "", "UREG.SP") +DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the stack pointer", "UREG.SP", "") DEF_ATTRIB(IMPLICIT_WRITES_FP, "Writes the frame pointer", "", "UREG.FP") DEF_ATTRIB(IMPLICIT_WRITES_LC0, "Writes loop count for loop 0", "", "UREG.LC0") DEF_ATTRIB(IMPLICIT_WRITES_LC1, "Writes loop count for loop 1", "", "UREG.LC1") @@ -111,13 +116,19 @@ DEF_ATTRIB(IMPLICIT_WRITES_P1, "Writes Predicate 1", "", "UREG.P1") DEF_ATTRIB(IMPLICIT_WRITES_P2, "Writes Predicate 1", "", "UREG.P2") DEF_ATTRIB(IMPLICIT_WRITES_P3, "May write Predicate 3", "", "UREG.P3") -DEF_ATTRIB(IMPLICIT_READS_PC, "Reads the PC register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P0, "Reads the P0 register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P1, "Reads the P1 register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P2, "Reads the P2 register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P3, "Reads the P3 register", "", "") +DEF_ATTRIB(IMPLICIT_READS_P0, "Reads Predicate 0", "UREG.P0", "") +DEF_ATTRIB(IMPLICIT_READS_P1, "Reads Predicate 1", "UREG.P1", "") +DEF_ATTRIB(IMPLICIT_READS_P3, "Reads Predicate 3", "UREG.P3", "") DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "") -DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the SP register", "", "") +DEF_ATTRIB(IMPLICIT_WRITES_CCR, "Writes CCR register", "", "UREG.CCR") +DEF_ATTRIB(IMPLICIT_WRITES_GOSP, "Writes GOSP register", "", "UREG.GOSP") +DEF_ATTRIB(IMPLICIT_WRITES_SSR, "Writes SSR register", "", "UREG.SSR") +DEF_ATTRIB(IMPLICIT_WRITES_SGP0, "Writes SGP0 register", "", "UREG.SGP0") +DEF_ATTRIB(IMPLICIT_WRITES_SGP1, "Writes SGP1 register", "", "UREG.SGP1") +DEF_ATTRIB(IMPLICIT_WRITES_IMASK_ANYTHREAD, + "Writes IMASK for any thread", "", "") +DEF_ATTRIB(IMPLICIT_WRITES_STID_PRIO_ANYTHREAD, + "Writes STID priority for any thread", "", "") DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "") DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "") DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "") @@ -137,9 +148,14 @@ DEF_ATTRIB(RESTRICT_NOSLOT1, "No slot 1 instruction in parallel", "", "") DEF_ATTRIB(RESTRICT_PREFERSLOT0, "Try to encode into slot 0", "", "") DEF_ATTRIB(RESTRICT_PACKET_AXOK, "May exist with A-type or X-type", "", "") +DEF_ATTRIB(RESTRICT_SLOT1_AOK, "Slot 1 is allowed", "", "") DEF_ATTRIB(ICOP, "Instruction cache op", "", "") +DEF_ATTRIB(EXCEPTION_SWI, "Software interrupt exception", "", "") +DEF_ATTRIB(DMA, "DMA instruction", "", "") +DEF_ATTRIB(NO_TIMING_LOG, "Does not get logged to the timing model", "", "") + DEF_ATTRIB(HWLOOP0_END, "Ends HW loop0", "", "") DEF_ATTRIB(HWLOOP1_END, "Ends HW loop1", "", "") DEF_ATTRIB(RET_TYPE, "return type", "", "") @@ -151,6 +167,10 @@ DEF_ATTRIB(L2FETCH, "Instruction is l2fetch type", "", "") +DEF_ATTRIB(DCTAGOP, "Data cache tag operation", "", "") +DEF_ATTRIB(ICTAGOP, "Instruction cache tag operation", "", "") +DEF_ATTRIB(L2TAGOP, "L2 cache tag operation", "", "") + DEF_ATTRIB(ICINVA, "icinva", "", "") DEF_ATTRIB(DCCLEANINVA, "dccleaninva", "", "") @@ -166,6 +186,9 @@ DEF_ATTRIB(NOTE_NVSLOT0, "Can execute only in slot 0 (ST)", "", "") DEF_ATTRIB(NOTE_NOVP, "Cannot be paired with a HVX permute instruction", "", "") DEF_ATTRIB(NOTE_VA_UNARY, "Combined with HVX ALU op (must be unary)", "", "") +DEF_ATTRIB(NOTE_SLOT1_AOK, "Slot 1 is allowed", "", "") +DEF_ATTRIB(NOTE_GUEST, "Guest mode instruction", "", "") +DEF_ATTRIB(NOTE_BADTAG_UNDEF, "Bad tag results in undefined behavior", "", "") /* V6 MMVector Notes for Documentation */ DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "")
diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 1f0f22a..bfe9a86 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h
@@ -18,7 +18,7 @@ #ifndef HEXAGON_CPU_PARAM_H #define HEXAGON_CPU_PARAM_H -#define TARGET_PAGE_BITS 16 /* 64K pages */ +#define TARGET_PAGE_BITS 12 /* 4K pages */ #define TARGET_VIRT_ADDR_SPACE_BITS 32
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 2c53f2c..42d93e5 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c
@@ -16,16 +16,34 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/qemu-print.h" #include "cpu.h" #include "internal.h" +#include "exec/cputlb.h" #include "exec/translation-block.h" #include "qapi/error.h" #include "hw/core/qdev-properties.h" #include "fpu/softfloat-helpers.h" +#include "hw/hexagon/hexagon_tlb.h" #include "tcg/tcg.h" #include "exec/gdbstub.h" #include "accel/tcg/cpu-ops.h" +#include "cpu_helper.h" +#include "hex_mmu.h" + +#ifndef CONFIG_USER_ONLY +#include "macros.h" +#include "sys_macros.h" +#include "accel/tcg/cpu-ldst.h" +#include "qemu/main-loop.h" +#include "hex_interrupts.h" +#include "hexswi.h" +#include "exec/cpu-interrupt.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "hw/hexagon/hexagon_globalreg.h" +#endif static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) { @@ -43,6 +61,14 @@ } static const Property hexagon_cpu_properties[] = { +#ifndef CONFIG_USER_ONLY + DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB, + HexagonTLBState *), + DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffff), + DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs, + TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *), + DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0), +#endif DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false), DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, qdev_prop_uint32, target_ulong), @@ -60,6 +86,35 @@ "c24", "c25", "c26", "c27", "c28", "c29", "c30", "c31", }; +#ifndef CONFIG_USER_ONLY +const char * const hexagon_sregnames[] = { + "sgp0", "sgp1", "stid", "elr", "badva0", + "badva1", "ssr", "ccr", "htid", "badva", + "imask", "gevb", "vwctrl", "s13", "s14", + "s15", "evb", "modectl", "syscfg", "segment", + "ipendad", "vid", "vid1", "bestwait", "s24", + "schedcfg", "s26", "cfgbase", "diag", "rev", + "pcyclelo", "pcyclehi", "isdbst", "isdbcfg0", "isdbcfg1", + "livelock", "brkptpc0", "brkptcfg0", "brkptpc1", "brkptcfg1", + "isdbmbxin", "isdbmbxout", "isdben", "isdbgpr", "pmucnt4", + "pmucnt5", "pmucnt6", "pmucnt7", "pmucnt0", "pmucnt1", + "pmucnt2", "pmucnt3", "pmuevtcfg", "pmustid0", "pmuevtcfg1", + "pmustid1", "timerlo", "timerhi", "pmucfg", "s59", + "s60", "s61", "s62", "s63", +}; + +G_STATIC_ASSERT(NUM_SREGS == ARRAY_SIZE(hexagon_sregnames)); + +const char * const hexagon_gregnames[] = { + "gelr", "gsr", "gosp", "gbadva", "gcommit1t", + "gcommit2t", "gcommit3t", "gcommit4t", "gcommit5t", "gcommit6t", + "gpcycle1t", "gpcycle2t", "gpcycle3t", "gpcycle4t", "gpcycle5t", + "gpcycle6t", "gpmucnt4", "gpmucnt5", "gpmucnt6", "gpmucnt7", + "gcommit7t", "gcommit8t", "gpcycle7t", "gpcycle8t", "gpcyclelo", + "gpcyclehi", "gpmucnt0", "gpmucnt1", "gpmucnt2", "gpmucnt3", + "g30", "g31", +}; +#endif /* * One of the main debugging techniques is to use "-d cpu" and compare against * LLDB output when single stepping. However, the target and qemu put the @@ -110,6 +165,14 @@ hexagon_regnames[regnum], value); } +#ifndef CONFIG_USER_ONLY +static void print_t_sreg(FILE *f, const CPUHexagonState *env, int regnum) +{ + qemu_fprintf(f, " %s = 0x" TARGET_FMT_lx "\n", + hexagon_sregnames[regnum], env->t_sreg[regnum]); +} +#endif + static void print_vreg(FILE *f, CPUHexagonState *env, int regnum, bool skip_if_zero) { @@ -209,8 +272,7 @@ qemu_fprintf(f, " cs0 = 0x00000000\n"); qemu_fprintf(f, " cs1 = 0x00000000\n"); #else - print_reg(f, env, HEX_REG_CAUSE); - print_reg(f, env, HEX_REG_BADVA); + print_t_sreg(f, env, HEX_SREG_BADVA); print_reg(f, env, HEX_REG_CS0); print_reg(f, env, HEX_REG_CS1); #endif @@ -261,6 +323,14 @@ hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); } +#ifndef CONFIG_USER_ONLY + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, + cpu_mmu_index(env_cpu(env), false)); + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, PCYCLE_ENABLED, 1); +#else + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, MMU_USER_IDX); +#endif + return (TCGTBCPUState){ .pc = pc, .flags = hex_flags }; } @@ -271,6 +341,36 @@ cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } +#ifndef CONFIG_USER_ONLY +bool hexagon_thread_is_enabled(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t modectl; + uint32_t thread_enabled_mask; + bool E_bit; + + if (!cpu->globalregs) { + return true; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + E_bit = thread_enabled_mask & (0x1 << env->threadId); + + return E_bit; +} + +static bool hexagon_cpu_has_work(CPUState *cs) +{ + CPUHexagonState *env = cpu_env(cs); + + return hexagon_thread_is_enabled(env) && + (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_SWI + | CPU_INTERRUPT_K0_UNLOCK | CPU_INTERRUPT_TLB_UNLOCK)); +} +#endif + static void hexagon_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) @@ -278,11 +378,36 @@ cpu_env(cs)->gpr[HEX_REG_PC] = data[0]; } + +#ifndef CONFIG_USER_ONLY +void hexagon_cpu_soft_reset(CPUHexagonState *env) +{ + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + env->t_sreg[HEX_SREG_SSR] = 0; + hexagon_ssr_set_cause(env, HEX_CAUSE_RESET); + + cpu = env_archcpu(env); + if (cpu->globalregs) { + uint32_t evb = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB, + env->threadId); + env->gpr[HEX_REG_PC] = evb; + } else { + env->gpr[HEX_REG_PC] = cpu->boot_addr; + } +} +#endif + static void hexagon_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj); CPUHexagonState *env = cpu_env(cs); +#ifndef CONFIG_USER_ONLY + HexagonCPU *cpu = HEXAGON_CPU(cs); +#endif if (mcc->parent_phases.hold) { mcc->parent_phases.hold(obj, type); @@ -292,6 +417,22 @@ set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); /* Default NaN value: sign bit set, all frac bits set */ set_float_default_nan_pattern(0b11111111, &env->fp_status); +#ifndef CONFIG_USER_ONLY + memset(env->t_sreg, 0, sizeof(uint32_t) * NUM_SREGS); + memset(env->greg, 0, sizeof(uint32_t) * NUM_GREGS); + env->wait_next_pc = 0; + env->tlb_lock_state = HEX_LOCK_UNLOCKED; + env->k0_lock_state = HEX_LOCK_UNLOCKED; + env->tlb_lock_count = 0; + env->k0_lock_count = 0; + env->next_PC = 0; + + env->t_sreg[HEX_SREG_HTID] = cpu->htid; + env->threadId = cpu->htid; + hexagon_cpu_soft_reset(env); + env->cause_code = HEX_EVENT_NONE; + env->gpr[HEX_REG_PC] = cpu->boot_addr; +#endif } static void hexagon_cpu_disas_set_info(const CPUState *cs, @@ -319,21 +460,263 @@ hexagon_hvx_gdb_write_register, gdb_find_static_feature("hexagon-hvx.xml")); - qemu_init_vcpu(cs); - cpu_reset(cs); +#ifndef CONFIG_USER_ONLY + if (!HEXAGON_CPU(dev)->tlb) { + error_setg(errp, "hexagon cpu requires 'tlb' link property to be set"); + return; + } +#endif + qemu_init_vcpu(cs); + + cpu_reset(cs); mcc->parent_realize(dev, errp); } static int hexagon_cpu_mmu_index(CPUState *cs, bool ifetch) { +#ifndef CONFIG_USER_ONLY + CPUHexagonState *env = cpu_env(cs); + HexagonCPU *cpu = HEXAGON_CPU(cs); + int cpu_mode; + + BQL_LOCK_GUARD(); + if (cpu->globalregs) { + uint32_t syscfg = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId); + uint8_t mmuen = GET_SYSCFG_FIELD(SYSCFG_MMUEN, syscfg); + if (!mmuen) { + return MMU_KERNEL_IDX; + } + } + + cpu_mode = get_cpu_mode(env); + if (cpu_mode == HEX_CPU_MODE_MONITOR) { + return MMU_KERNEL_IDX; + } else if (cpu_mode == HEX_CPU_MODE_GUEST) { + return MMU_GUEST_IDX; + } +#endif + return MMU_USER_IDX; } +#ifndef CONFIG_USER_ONLY +static void hexagon_cpu_set_irq(void *opaque, int irq, int level) +{ + HexagonCPU *cpu = HEXAGON_CPU(opaque); + CPUState *cs = CPU(cpu); + CPUHexagonState *env = cpu_env(cs); + + switch (irq) { + case HEXAGON_CPU_IRQ_0 ... HEXAGON_CPU_IRQ_7: + qemu_log_mask(CPU_LOG_INT, "%s: irq %d, level %d\n", + __func__, irq, level); + if (level) { + hex_raise_interrupts(env, 1 << irq, CPU_INTERRUPT_HARD); + } + break; + default: + g_assert_not_reached(); + } +} +#endif + static void hexagon_cpu_init(Object *obj) { +#ifndef CONFIG_USER_ONLY + HexagonCPU *cpu = HEXAGON_CPU(obj); + qdev_init_gpio_in(DEVICE(cpu), hexagon_cpu_set_irq, 8); +#endif } +#ifndef CONFIG_USER_ONLY +static bool get_physical_address(CPUHexagonState *env, hwaddr *phys, int *prot, + uint64_t *size, int32_t *excp, + uint32_t address, + MMUAccessType access_type, int mmu_idx) + +{ + if (hexagon_cpu_mmu_enabled(env)) { + return hex_tlb_find_match(env, address, access_type, phys, prot, size, + excp, mmu_idx); + } else { + *phys = address & 0xFFFFFFFF; + *prot = PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *size = TARGET_PAGE_SIZE; + return true; + } +} + +/* qemu seems to only want to know about TARGET_PAGE_SIZE pages */ +static void find_qemu_subpage(vaddr *addr, hwaddr *phys, uint64_t page_size) +{ + vaddr page_start = *addr & ~((vaddr)(page_size - 1)); + vaddr offset = ((*addr - page_start) / TARGET_PAGE_SIZE) * TARGET_PAGE_SIZE; + *addr = page_start + offset; + *phys += offset; +} + +static hwaddr hexagon_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr) +{ + CPUHexagonState *env = cpu_env(cs); + hwaddr phys_addr; + int prot; + uint64_t page_size = 0; + int32_t excp = 0; + int mmu_idx = MMU_KERNEL_IDX; + + if (get_physical_address(env, &phys_addr, &prot, &page_size, &excp, + addr, 0, mmu_idx)) { + find_qemu_subpage(&addr, &phys_addr, page_size); + return phys_addr; + } + + return -1; +} + + +#define INVALID_BADVA 0xbadabada + +static void set_badva_regs(CPUHexagonState *env, uint32_t VA, int slot, + MMUAccessType access_type) +{ + env->t_sreg[HEX_SREG_BADVA] = VA; + + if (access_type == MMU_INST_FETCH || slot == 0) { + env->t_sreg[HEX_SREG_BADVA0] = VA; + env->t_sreg[HEX_SREG_BADVA1] = INVALID_BADVA; + SET_SSR_FIELD(env, SSR_V0, 1); + SET_SSR_FIELD(env, SSR_V1, 0); + SET_SSR_FIELD(env, SSR_BVS, 0); + } else if (slot == 1) { + env->t_sreg[HEX_SREG_BADVA0] = INVALID_BADVA; + env->t_sreg[HEX_SREG_BADVA1] = VA; + SET_SSR_FIELD(env, SSR_V0, 0); + SET_SSR_FIELD(env, SSR_V1, 1); + SET_SSR_FIELD(env, SSR_BVS, 1); + } else { + g_assert_not_reached(); + } +} + +static void raise_tlbmiss_exception(CPUState *cs, uint32_t VA, int slot, + MMUAccessType access_type) +{ + CPUHexagonState *env = cpu_env(cs); + + set_badva_regs(env, VA, slot, access_type); + + switch (access_type) { + case MMU_INST_FETCH: + cs->exception_index = HEX_EVENT_TLB_MISS_X; + if ((VA & ~TARGET_PAGE_MASK) == 0) { + env->cause_code = HEX_CAUSE_TLBMISSX_CAUSE_NEXTPAGE; + } else { + env->cause_code = HEX_CAUSE_TLBMISSX_CAUSE_NORMAL; + } + break; + case MMU_DATA_LOAD: + cs->exception_index = HEX_EVENT_TLB_MISS_RW; + env->cause_code = HEX_CAUSE_TLBMISSRW_CAUSE_READ; + break; + case MMU_DATA_STORE: + cs->exception_index = HEX_EVENT_TLB_MISS_RW; + env->cause_code = HEX_CAUSE_TLBMISSRW_CAUSE_WRITE; + break; + } +} + +static void raise_perm_exception(CPUState *cs, uint32_t VA, int slot, + MMUAccessType access_type, int32_t excp) +{ + CPUHexagonState *env = cpu_env(cs); + + set_badva_regs(env, VA, slot, access_type); + cs->exception_index = excp; +} + +static const char *access_type_names[] = { "MMU_DATA_LOAD ", "MMU_DATA_STORE", + "MMU_INST_FETCH" }; + +static const char *mmu_idx_names[] = { "MMU_USER_IDX", "MMU_GUEST_IDX", + "MMU_KERNEL_IDX" }; + +static bool hexagon_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, bool probe, + uintptr_t retaddr) +{ + CPUHexagonState *env = cpu_env(cs); + int slot = 0; + hwaddr phys; + int prot = 0; + uint64_t page_size = 0; + int32_t excp = 0; + bool ret = 0; + + qemu_log_mask( + CPU_LOG_MMU, + "%s: tid = 0x%" PRIx32 ", pc = 0x%08" PRIx32 + ", vaddr = 0x%08" VADDR_PRIx ", size = %d, %s,\tprobe = %d, %s\n", + __func__, env->threadId, env->gpr[HEX_REG_PC], address, size, + access_type_names[access_type], probe, mmu_idx_names[mmu_idx]); + ret = get_physical_address(env, &phys, &prot, &page_size, &excp, address, + access_type, mmu_idx); + if (ret) { + if (!excp) { + find_qemu_subpage(&address, &phys, page_size); + tlb_set_page(cs, address, phys, prot, mmu_idx, TARGET_PAGE_SIZE); + return ret; + } + if (probe) { + return false; + } + raise_perm_exception(cs, address, slot, access_type, excp); + do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC], + retaddr); + } + if (probe) { + return false; + } + raise_tlbmiss_exception(cs, address, slot, access_type); + do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC], retaddr); +} + +#include "hw/core/sysemu-cpu-ops.h" + +static const struct SysemuCPUOps hexagon_sysemu_ops = { + .has_work = hexagon_cpu_has_work, + .get_phys_addr_debug = hexagon_cpu_get_phys_addr_debug, +}; + +static bool hexagon_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + CPUHexagonState *env = cpu_env(cs); + if (interrupt_request & CPU_INTERRUPT_TLB_UNLOCK) { + cs->halted = false; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TLB_UNLOCK); + return true; + } + if (interrupt_request & CPU_INTERRUPT_K0_UNLOCK) { + cs->halted = false; + cpu_reset_interrupt(cs, CPU_INTERRUPT_K0_UNLOCK); + return true; + } + if (interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_SWI)) { + return hex_check_interrupts(env); + } + return false; +} + +static vaddr hexagon_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return result; +} + +#endif + static const TCGCPUOps hexagon_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, @@ -344,6 +727,14 @@ .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, .restore_state_to_opc = hexagon_restore_state_to_opc, .mmu_index = hexagon_cpu_mmu_index, +#ifndef CONFIG_USER_ONLY + .cpu_exec_interrupt = hexagon_cpu_exec_interrupt, + .pointer_wrap = hexagon_pointer_wrap, + .cpu_exec_reset = cpu_reset, + .tlb_fill = hexagon_tlb_fill, + .cpu_exec_halt = hexagon_cpu_has_work, + .do_interrupt = hexagon_cpu_do_interrupt, +#endif /* !CONFIG_USER_ONLY */ }; static void hexagon_cpu_class_init(ObjectClass *c, const void *data) @@ -369,9 +760,34 @@ cc->gdb_stop_before_watchpoint = true; cc->gdb_core_xml_file = "hexagon-core.xml"; cc->disas_set_info = hexagon_cpu_disas_set_info; +#ifndef CONFIG_USER_ONLY + cc->sysemu_ops = &hexagon_sysemu_ops; + dc->vmsd = &vmstate_hexagon_cpu; +#endif +#ifdef CONFIG_TCG cc->tcg_ops = &hexagon_tcg_ops; +#endif } +#ifndef CONFIG_USER_ONLY +uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg) +{ + if (reg <= HEX_GREG_G3) { + return env->greg[reg]; + } + switch (reg) { + case HEX_GREG_GPCYCLELO: + return hexagon_get_sys_pcycle_count_low(env); + case HEX_GREG_GPCYCLEHI: + return hexagon_get_sys_pcycle_count_high(env); + default: + qemu_log_mask(LOG_UNIMP, "reading greg %" PRId32 + " not yet supported.\n", reg); + return 0; + } +} +#endif + static void hexagon_cpu_class_base_init(ObjectClass *c, const void *data) { HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c);
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 7c09c97..7694fd9 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h
@@ -20,6 +20,16 @@ #include "fpu/softfloat-types.h" +#ifndef CONFIG_USER_ONLY +#define NUM_GREGS 32 +#define GREG_WRITES_MAX 2 +#define NUM_SREGS 64 +#define SREG_WRITES_MAX 2 +#endif + +typedef struct HexagonTLBState HexagonTLBState; +typedef struct HexagonGlobalRegState HexagonGlobalRegState; + #include "cpu-qom.h" #include "exec/cpu-common.h" #include "exec/target_long.h" @@ -28,9 +38,7 @@ #include "hw/core/registerfields.h" #include "qemu/bitmap.h" -#ifndef CONFIG_USER_ONLY -#error "Hexagon does not support system emulation" -#endif +#include "target/hexagon/reg_fields.h" #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -40,10 +48,47 @@ #define REG_WRITES_MAX 32 #define PRED_WRITES_MAX 5 /* 4 insns + endloop */ #define VSTORES_MAX 2 +#define MAX_TLB_ENTRIES 1024 +#define THREADS_MAX 8 #define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU +#ifndef CONFIG_USER_ONLY +#define CPU_INTERRUPT_SWI CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_K0_UNLOCK CPU_INTERRUPT_TGT_INT_1 +#define CPU_INTERRUPT_TLB_UNLOCK CPU_INTERRUPT_TGT_INT_2 -#define MMU_USER_IDX 0 +#define HEX_CPU_MODE_USER 1 +#define HEX_CPU_MODE_GUEST 2 +#define HEX_CPU_MODE_MONITOR 3 + +#define HEX_EXE_MODE_OFF 1 +#define HEX_EXE_MODE_RUN 2 +#define HEX_EXE_MODE_WAIT 3 +#define HEX_EXE_MODE_DEBUG 4 +#endif + +#define MMU_USER_IDX 0 +#ifndef CONFIG_USER_ONLY +#define MMU_GUEST_IDX 1 +#define MMU_KERNEL_IDX 2 + +typedef enum { + HEX_LOCK_UNLOCKED = 0, + HEX_LOCK_WAITING = 1, + HEX_LOCK_OWNER = 2, + HEX_LOCK_QUEUED = 3 +} hex_lock_state_t; +#endif + + +#define HEXAGON_CPU_IRQ_0 0 +#define HEXAGON_CPU_IRQ_1 1 +#define HEXAGON_CPU_IRQ_2 2 +#define HEXAGON_CPU_IRQ_3 3 +#define HEXAGON_CPU_IRQ_4 4 +#define HEXAGON_CPU_IRQ_5 5 +#define HEXAGON_CPU_IRQ_6 6 +#define HEXAGON_CPU_IRQ_7 7 typedef struct { target_ulong va; @@ -77,12 +122,30 @@ typedef struct CPUArchState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; + uint32_t cause_code; /* For comparing with LLDB on target - see adjust_stack_ptrs function */ target_ulong last_pc_dumped; target_ulong stack_start; uint8_t slot_cancelled; + +#ifndef CONFIG_USER_ONLY + /* Some system registers are per thread and some are global. */ + uint32_t t_sreg[NUM_SREGS]; + + uint32_t greg[NUM_GREGS]; + uint32_t wait_next_pc; + + /* This alias of CPUState.cpu_index is used by imported sources: */ + uint32_t threadId; + hex_lock_state_t tlb_lock_state; + hex_lock_state_t k0_lock_state; + uint32_t tlb_lock_count; + uint32_t k0_lock_count; + uint64_t t_cycle_count; +#endif + uint32_t next_PC; target_ulong new_value_usr; MemLog mem_log_stores[STORES_MAX]; @@ -130,16 +193,34 @@ bool lldb_compat; target_ulong lldb_stack_adjust; bool short_circuit; +#ifndef CONFIG_USER_ONLY + HexagonTLBState *tlb; + uint32_t boot_addr; + HexagonGlobalRegState *globalregs; + uint32_t htid; +#endif }; #include "cpu_bits.h" FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) +FIELD(TB_FLAGS, MMU_INDEX, 1, 3) +FIELD(TB_FLAGS, PCYCLE_ENABLED, 4, 1) G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, uint32_t exception, uintptr_t pc); +#ifndef CONFIG_USER_ONLY +/* + * @return true if the @a thread_env hardware thread is + * not stopped. + */ +bool hexagon_thread_is_enabled(CPUHexagonState *thread_env); +uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg); +void hexagon_cpu_soft_reset(CPUHexagonState *env); +#endif + typedef HexagonCPU ArchCPU; void hexagon_translate_init(void);
diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index aaac6b9..164e74c 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h
@@ -25,20 +25,77 @@ #define PCALIGN_MASK (PCALIGN - 1) enum hex_event { - HEX_EVENT_NONE = -1, - HEX_EVENT_TRAP0 = 0x008, + HEX_EVENT_NONE = -1, + HEX_EVENT_RESET = 0x0, + HEX_EVENT_IMPRECISE = 0x1, + HEX_EVENT_PRECISE = 0x2, + HEX_EVENT_TLB_MISS_X = 0x4, + HEX_EVENT_TLB_MISS_RW = 0x6, + HEX_EVENT_TRAP0 = 0x8, + HEX_EVENT_TRAP1 = 0x9, + HEX_EVENT_FPTRAP = 0xb, + HEX_EVENT_DEBUG = 0xc, + HEX_EVENT_INT0 = 0x10, + HEX_EVENT_INT1 = 0x11, + HEX_EVENT_INT2 = 0x12, + HEX_EVENT_INT3 = 0x13, + HEX_EVENT_INT4 = 0x14, + HEX_EVENT_INT5 = 0x15, + HEX_EVENT_INT6 = 0x16, + HEX_EVENT_INT7 = 0x17, + HEX_EVENT_INT8 = 0x18, + HEX_EVENT_INT9 = 0x19, + HEX_EVENT_INTA = 0x1a, + HEX_EVENT_INTB = 0x1b, + HEX_EVENT_INTC = 0x1c, + HEX_EVENT_INTD = 0x1d, + HEX_EVENT_INTE = 0x1e, + HEX_EVENT_INTF = 0x1f, }; enum hex_cause { HEX_CAUSE_NONE = -1, - HEX_CAUSE_TRAP0 = 0x172, - HEX_CAUSE_FETCH_NO_UPAGE = 0x012, - HEX_CAUSE_INVALID_PACKET = 0x015, - HEX_CAUSE_INVALID_OPCODE = 0x015, + HEX_CAUSE_RESET = 0x000, + HEX_CAUSE_BIU_PRECISE = 0x001, + HEX_CAUSE_UNSUPPORTED_HVX_64B = 0x002, /* QEMU-specific */ + HEX_CAUSE_DOUBLE_EXCEPT = 0x003, + HEX_CAUSE_TRAP0 = 0x008, + HEX_CAUSE_TRAP1 = 0x009, + HEX_CAUSE_FETCH_NO_XPAGE = 0x011, + HEX_CAUSE_FETCH_NO_UPAGE = 0x012, + HEX_CAUSE_INVALID_PACKET = 0x015, + HEX_CAUSE_INVALID_OPCODE = 0x015, /* alias: same cause as INVALID_PACKET */ + HEX_CAUSE_NO_COPROC_ENABLE = 0x016, + HEX_CAUSE_NO_COPROC2_ENABLE = 0x018, + HEX_CAUSE_PRIV_USER_NO_GINSN = 0x01a, + HEX_CAUSE_PRIV_USER_NO_SINSN = 0x01b, HEX_CAUSE_REG_WRITE_CONFLICT = 0x01d, - HEX_CAUSE_PC_NOT_ALIGNED = 0x01e, - HEX_CAUSE_PRIV_NO_UREAD = 0x024, - HEX_CAUSE_PRIV_NO_UWRITE = 0x025, + HEX_CAUSE_PC_NOT_ALIGNED = 0x01e, + HEX_CAUSE_MISALIGNED_LOAD = 0x020, + HEX_CAUSE_MISALIGNED_STORE = 0x021, + HEX_CAUSE_PRIV_NO_READ = 0x022, + HEX_CAUSE_PRIV_NO_WRITE = 0x023, + HEX_CAUSE_PRIV_NO_UREAD = 0x024, + HEX_CAUSE_PRIV_NO_UWRITE = 0x025, + HEX_CAUSE_COPROC_LDST = 0x026, + HEX_CAUSE_STACK_LIMIT = 0x027, + HEX_CAUSE_VWCTRL_WINDOW_MISS = 0x029, + HEX_CAUSE_IMPRECISE_NMI = 0x043, + HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH = 0x044, + HEX_CAUSE_TLBMISSX_CAUSE_NORMAL = 0x060, + HEX_CAUSE_TLBMISSX_CAUSE_NEXTPAGE = 0x061, + HEX_CAUSE_TLBMISSRW_CAUSE_READ = 0x070, + HEX_CAUSE_TLBMISSRW_CAUSE_WRITE = 0x071, + HEX_CAUSE_DEBUG_SINGLESTEP = 0x80, + HEX_CAUSE_FPTRAP_CAUSE_BADFLOAT = 0x0bf, + HEX_CAUSE_INT0 = 0x0c0, + HEX_CAUSE_INT1 = 0x0c1, + HEX_CAUSE_INT2 = 0x0c2, + HEX_CAUSE_INT3 = 0x0c3, + HEX_CAUSE_INT4 = 0x0c4, + HEX_CAUSE_INT5 = 0x0c5, + HEX_CAUSE_INT6 = 0x0c6, + HEX_CAUSE_INT7 = 0x0c7, }; #define PACKET_WORDS_MAX 4
diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c new file mode 100644 index 0000000..64c5746 --- /dev/null +++ b/target/hexagon/cpu_helper.c
@@ -0,0 +1,399 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_helper.h" +#include "system/cpus.h" +#include "hw/core/boards.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hex_interrupts.h" +#include "hex_mmu.h" +#include "system/runstate.h" +#include "exec/cpu-interrupt.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ldst.h" +#include "exec/cputlb.h" +#include "qemu/log.h" +#include "tcg/tcg-op.h" +#include "internal.h" +#include "macros.h" +#include "sys_macros.h" +#include "arch.h" + + +uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index) +{ + g_assert_not_reached(); +} + +uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env) +{ + uint64_t total = 0; + CPUState *cs; + + g_assert(bql_locked()); + CPU_FOREACH(cs) { + CPUHexagonState *thread_env = cpu_env(cs); + total += thread_env->t_cycle_count; + } + return total; +} + +uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env) +{ + return (uint32_t)(hexagon_get_sys_pcycle_count(env) >> 32); +} + +uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env) +{ + return (uint32_t)(hexagon_get_sys_pcycle_count(env)); +} + +void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t val) +{ + uint64_t old; + + g_assert(bql_locked()); + old = hexagon_get_sys_pcycle_count(env); + old = deposit64(old, 32, 32, val); + hexagon_set_sys_pcycle_count(env, old); +} + +void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t val) +{ + uint64_t old; + + g_assert(bql_locked()); + old = hexagon_get_sys_pcycle_count(env); + old = deposit64(old, 0, 32, val); + hexagon_set_sys_pcycle_count(env, old); +} + +void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t val) +{ + CPUState *cs; + uint64_t total; + int num_threads; + int64_t delta, per_thread, remainder; + + g_assert(bql_locked()); + total = hexagon_get_sys_pcycle_count(env); + + /* Count active threads */ + num_threads = 0; + CPU_FOREACH(cs) { + num_threads++; + } + g_assert(num_threads > 0); + + /* + * Distribute the delta evenly across all threads. + * Any remainder goes to the calling thread. + */ + delta = (int64_t)(val - total); + per_thread = delta / num_threads; + remainder = delta - per_thread * num_threads; + + CPU_FOREACH(cs) { + CPUHexagonState *thread_env = cpu_env(cs); + thread_env->t_cycle_count += per_thread; + } + env->t_cycle_count += remainder; +} + +static void hexagon_resume_thread(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + clear_wait_mode(env); + /* + * The wait instruction keeps the PC pointing to itself + * so that it has an opportunity to check for interrupts. + * + * When we come out of wait mode, adjust the PC to the + * next executable instruction. + */ + env->gpr[HEX_REG_PC] = env->wait_next_pc; + cs = env_cpu(env); + ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index); + cs->halted = false; + cs->exception_index = HEX_EVENT_NONE; + qemu_cpu_kick(cs); +} + +void hexagon_resume_threads(CPUHexagonState *current_env, uint32_t mask) +{ + CPUState *cs; + CPUHexagonState *env; + + g_assert(bql_locked()); + CPU_FOREACH(cs) { + env = cpu_env(cs); + g_assert(env->threadId < THREADS_MAX); + if ((mask & (0x1 << env->threadId))) { + if (get_exe_mode(env) == HEX_EXE_MODE_WAIT) { + hexagon_resume_thread(env); + } + } + } +} + +void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old) +{ + bool old_EX, old_UM, old_GM, old_IE; + bool new_EX, new_UM, new_GM, new_IE; + uint8_t old_asid, new_asid; + + g_assert(bql_locked()); + + old_EX = GET_SSR_FIELD(SSR_EX, old); + old_UM = GET_SSR_FIELD(SSR_UM, old); + old_GM = GET_SSR_FIELD(SSR_GM, old); + old_IE = GET_SSR_FIELD(SSR_IE, old); + new_EX = GET_SSR_FIELD(SSR_EX, new); + new_UM = GET_SSR_FIELD(SSR_UM, new); + new_GM = GET_SSR_FIELD(SSR_GM, new); + new_IE = GET_SSR_FIELD(SSR_IE, new); + + if ((old_EX != new_EX) || + (old_UM != new_UM) || + (old_GM != new_GM)) { + hex_mmu_mode_change(env); + } + + old_asid = GET_SSR_FIELD(SSR_ASID, old); + new_asid = GET_SSR_FIELD(SSR_ASID, new); + if (new_asid != old_asid) { + CPUState *cs = env_cpu(env); + tlb_flush(cs); + } + + /* See if the interrupts have been enabled or we have exited EX mode */ + if ((new_IE && !old_IE) || + (!new_EX && old_EX)) { + hex_interrupt_update(env); + } +} + +void clear_wait_mode(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_wait_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (cpu->globalregs) { + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_wait_mask = GET_FIELD(MODECTL_W, modectl); + thread_wait_mask &= ~(0x1 << env->threadId); + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_W, thread_wait_mask); + } +} + +void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause) +{ + uint32_t old, new; + + g_assert(bql_locked()); + + old = env->t_sreg[HEX_SREG_SSR]; + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, 1); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause); + new = env->t_sreg[HEX_SREG_SSR]; + + hexagon_modify_ssr(env, new, old); +} + + +int get_exe_mode(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_enabled_mask, thread_wait_mask; + uint32_t isdbst, debugmode; + bool E_bit, W_bit, D_bit; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + modectl = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId) : 0; + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + E_bit = thread_enabled_mask & (0x1 << env->threadId); + thread_wait_mask = GET_FIELD(MODECTL_W, modectl); + W_bit = thread_wait_mask & (0x1 << env->threadId); + isdbst = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_ISDBST, + env->threadId) : 0; + debugmode = GET_FIELD(ISDBST_DEBUGMODE, isdbst); + D_bit = debugmode & (0x1 << env->threadId); + + if (!D_bit && !W_bit && !E_bit) { + return HEX_EXE_MODE_OFF; + } + if (!D_bit && !W_bit && E_bit) { + return HEX_EXE_MODE_RUN; + } + if (!D_bit && W_bit && E_bit) { + return HEX_EXE_MODE_WAIT; + } + if (D_bit && !W_bit && E_bit) { + return HEX_EXE_MODE_DEBUG; + } + g_assert_not_reached(); +} + +static uint32_t set_enable_mask(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_enabled_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (!cpu->globalregs) { + return 0; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + thread_enabled_mask |= 0x1 << env->threadId; + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask); + return thread_enabled_mask; +} + +static uint32_t clear_enable_mask(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_enabled_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (!cpu->globalregs) { + return 0; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + thread_enabled_mask &= ~(0x1 << env->threadId); + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask); + return thread_enabled_mask; +} +static void do_start_thread(CPUState *cs, run_on_cpu_data tbd) +{ + CPUHexagonState *env; + + BQL_LOCK_GUARD(); + + env = cpu_env(cs); + + hexagon_cpu_soft_reset(env); + + set_enable_mask(env); + + cs->halted = 0; + cs->exception_index = HEX_EVENT_NONE; + cpu_resume(cs); +} + +void hexagon_start_threads(CPUHexagonState *current_env, uint32_t mask) +{ + CPUState *cs; + CPU_FOREACH(cs) { + CPUHexagonState *env = cpu_env(cs); + if (!(mask & (0x1 << env->threadId))) { + continue; + } + + if (current_env->threadId != env->threadId) { + async_safe_run_on_cpu(cs, do_start_thread, RUN_ON_CPU_NULL); + } + } +} + +/* + * When we have all threads stopped, the return + * value to the shell is register 2 from thread 0. + */ +static uint32_t get_thread0_r2(void) +{ + CPUState *cs; + CPU_FOREACH(cs) { + CPUHexagonState *thread = cpu_env(cs); + if (thread->threadId == 0) { + return thread->gpr[2]; + } + } + g_assert_not_reached(); +} + +void hexagon_stop_thread(CPUHexagonState *env) +{ + uint32_t thread_enabled_mask; + CPUState *cs; + + BQL_LOCK_GUARD(); + + thread_enabled_mask = clear_enable_mask(env); + cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + if (!thread_enabled_mask) { + /* All threads are stopped, request shutdown */ + qemu_system_shutdown_request_with_code( + SHUTDOWN_CAUSE_GUEST_SHUTDOWN, get_thread0_r2()); + } +} + +static int sys_in_monitor_mode_ssr(uint32_t ssr) +{ + if ((GET_SSR_FIELD(SSR_EX, ssr) != 0) || + ((GET_SSR_FIELD(SSR_EX, ssr) == 0) && + (GET_SSR_FIELD(SSR_UM, ssr) == 0))) { + return 1; + } + return 0; +} + +static int sys_in_guest_mode_ssr(uint32_t ssr) +{ + if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) && + (GET_SSR_FIELD(SSR_UM, ssr) != 0) && + (GET_SSR_FIELD(SSR_GM, ssr) != 0)) { + return 1; + } + return 0; +} + +static int sys_in_user_mode_ssr(uint32_t ssr) +{ + if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) && + (GET_SSR_FIELD(SSR_UM, ssr) != 0) && + (GET_SSR_FIELD(SSR_GM, ssr) == 0)) { + return 1; + } + return 0; +} + +int get_cpu_mode(CPUHexagonState *env) +{ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + + if (sys_in_monitor_mode_ssr(ssr)) { + return HEX_CPU_MODE_MONITOR; + } else if (sys_in_guest_mode_ssr(ssr)) { + return HEX_CPU_MODE_GUEST; + } else if (sys_in_user_mode_ssr(ssr)) { + return HEX_CPU_MODE_USER; + } + return HEX_CPU_MODE_MONITOR; +}
diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h new file mode 100644 index 0000000..d176750 --- /dev/null +++ b/target/hexagon/cpu_helper.h
@@ -0,0 +1,26 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_CPU_HELPER_H +#define HEXAGON_CPU_HELPER_H + +uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index); +void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old); +int get_cpu_mode(CPUHexagonState *env); +int get_exe_mode(CPUHexagonState *env); +void clear_wait_mode(CPUHexagonState *env); +void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause); +void hexagon_start_threads(CPUHexagonState *env, uint32_t mask); +void hexagon_stop_thread(CPUHexagonState *env); +void hexagon_resume_threads(CPUHexagonState *env, uint32_t mask); +uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env); +uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env); +uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env); +void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t val); +void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t val); +void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t val); + +#endif
diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 1595451..6eddcca 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c
@@ -185,6 +185,8 @@ if ((GET_ATTRIB(opcode, A_JUMP)) || (GET_ATTRIB(opcode, A_CALL)) || (opcode == J2_trap0) || + (opcode == J2_trap1) || + (opcode == J2_rte) || (opcode == J2_pause)) { /* Exception to A_JUMP attribute */ if (opcode == J4_hintjumpr) { @@ -363,6 +365,18 @@ break; } } + /* + * And at the very very very end, move any RTE's, since they update + * user/supervisor mode. + */ +#if !defined(CONFIG_USER_ONLY) + for (i = 0; i < last_insn; i++) { + if (packet->insn[i].opcode == J2_rte) { + decode_send_insn_to(packet, i, last_insn); + break; + } + } +#endif } static void
diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index fdefd5b..44bb22e 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py
@@ -22,7 +22,6 @@ import string import hex_common - ## ## Generate the code to analyze the instruction ## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} @@ -42,6 +41,13 @@ def gen_analyze_func(f, tag, regs, imms): f.write(f"static void analyze_{tag}(DisasContext *ctx)\n") f.write("{\n") + if hex_common.tag_ignore(tag): + f.write("}\n\n") + return + + if hex_common.is_sysemu_tag(tag): + f.write("#ifndef CONFIG_USER_ONLY\n") + f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") if (hex_common.is_hvx_insn(tag)): if hex_common.has_hvx_helper(tag): @@ -58,7 +64,8 @@ def gen_analyze_func(f, tag, regs, imms): for regno, register in enumerate(regs): reg_type, reg_id = register reg = hex_common.get_register(tag, reg_type, reg_id) - reg.decl_reg_num(f, regno) + if reg.is_read() or reg.is_written(): + reg.decl_reg_num(f, regno) ## Analyze the register reads for regno, register in enumerate(regs): @@ -78,6 +85,9 @@ def gen_analyze_func(f, tag, regs, imms): f.write(" mark_implicit_writes(ctx);\n") + if hex_common.is_sysemu_tag(tag): + f.write("#endif /* !CONFIG_USER_ONLY */\n") + f.write("}\n\n")
diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index 6206104..1629ebc 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py
@@ -104,30 +104,24 @@ def main(): tagimms = hex_common.get_tagimms() with open(args.out, "w") as f: - for tag in hex_common.tags: - ## Skip the priv instructions - if "A_PRIV" in hex_common.attribdict[tag]: - continue - ## Skip the guest instructions - if "A_GUEST" in hex_common.attribdict[tag]: - continue - ## Skip the floating point instructions - if "A_FPOP" in hex_common.attribdict[tag]: - continue - ## Skip the diag instructions - if tag == "Y6_diag": - continue - if tag == "Y6_diag0": - continue - if tag == "Y6_diag1": + for tag in hex_common.get_user_tags(): + if hex_common.tag_ignore(tag): continue if hex_common.skip_qemu_helper(tag): continue if hex_common.is_idef_parser_enabled(tag): continue - gen_helper_function(f, tag, tagregs, tagimms) + f.write("#if !defined(CONFIG_USER_ONLY)\n") + for tag in hex_common.get_sys_tags(): + if hex_common.skip_qemu_helper(tag): + continue + if hex_common.is_idef_parser_enabled(tag): + continue + gen_helper_function(f, tag, tagregs, tagimms) + f.write("#endif\n") + if __name__ == "__main__": main()
diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index 77f8e0a..59c8bdd 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py
@@ -59,19 +59,8 @@ def main(): tagimms = hex_common.get_tagimms() with open(args.out, "w") as f: - for tag in hex_common.tags: - ## Skip the priv instructions - if "A_PRIV" in hex_common.attribdict[tag]: - continue - ## Skip the guest instructions - if "A_GUEST" in hex_common.attribdict[tag]: - continue - ## Skip the diag instructions - if tag == "Y6_diag": - continue - if tag == "Y6_diag0": - continue - if tag == "Y6_diag1": + for tag in hex_common.get_user_tags(): + if hex_common.tag_ignore(tag): continue if hex_common.skip_qemu_helper(tag): @@ -81,6 +70,18 @@ def main(): gen_helper_prototype(f, tag, tagregs, tagimms) + f.write("#if !defined(CONFIG_USER_ONLY)\n") + for tag in hex_common.get_sys_tags(): + if hex_common.tag_ignore(tag): + continue + if hex_common.skip_qemu_helper(tag): + continue + if hex_common.is_idef_parser_enabled(tag): + continue + + gen_helper_prototype(f, tag, tagregs, tagimms) + f.write("#endif\n") + if __name__ == "__main__": main()
diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py index 2f6e826..32bce9b 100644 --- a/target/hexagon/gen_idef_parser_funcs.py +++ b/target/hexagon/gen_idef_parser_funcs.py
@@ -60,6 +60,8 @@ def main(): f.write('#include "macros.h.inc"\n\n') for tag in hex_common.tags: + if hex_common.tag_ignore(tag): + continue ## Skip the priv instructions if "A_PRIV" in hex_common.attribdict[tag]: continue
diff --git a/target/hexagon/gen_op_attribs.py b/target/hexagon/gen_op_attribs.py index bbbb02d..94dd1f8 100755 --- a/target/hexagon/gen_op_attribs.py +++ b/target/hexagon/gen_op_attribs.py
@@ -38,7 +38,7 @@ def main(): ## Generate all the attributes associated with each instruction ## with open(args.out, "w") as f: - for tag in hex_common.tags: + for tag in hex_common.get_all_tags(): f.write( f"OP_ATTRIB({tag},ATTRIBS(" f'{",".join(sorted(hex_common.attribdict[tag]))}))\n'
diff --git a/target/hexagon/gen_opcodes_def.py b/target/hexagon/gen_opcodes_def.py index 94a19ff..17ba3f9 100755 --- a/target/hexagon/gen_opcodes_def.py +++ b/target/hexagon/gen_opcodes_def.py
@@ -37,7 +37,10 @@ def main(): ## Generate a list of all the opcodes ## with open(args.out, "w") as f: - for tag in hex_common.tags: + for tag in hex_common.get_user_tags(): + f.write(f"OPCODE({tag}),\n") + + for tag in hex_common.get_sys_tags(): f.write(f"OPCODE({tag}),\n")
diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index 0159e5c..a28af0c 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h
@@ -488,6 +488,7 @@ /* dczeroa clears the 32 byte cache line at the address given */ #define fGEN_TCG_Y2_dczeroa(SHORTCODE) SHORTCODE +#define fGEN_TCG_Y2_dczeroa_nt(SHORTCODE) SHORTCODE /* In linux-user mode, these are not modelled, suppress compiler warning */ #define fGEN_TCG_Y2_dcinva(SHORTCODE) \ @@ -1112,6 +1113,9 @@ RdV, tcg_constant_tl(0)); \ } while (0) +#define fGEN_TCG_Y2_break(SHORTCODE) +#define fGEN_TCG_J2_unpause(SHORTCODE) + #define fGEN_TCG_J2_pause(SHORTCODE) \ do { \ uiV = uiV; \ @@ -1321,6 +1325,11 @@ RsV = RsV; \ uiV = uiV; \ } while (0) +#define fGEN_TCG_Y2_dcfetchbo_nt(SHORTCODE) \ + do { \ + RsV = RsV; \ + uiV = uiV; \ + } while (0) #define fGEN_TCG_L2_loadw_aq(SHORTCODE) SHORTCODE #define fGEN_TCG_L4_loadd_aq(SHORTCODE) SHORTCODE @@ -1340,13 +1349,6 @@ #define fGEN_TCG_S2_storew_rl_st_vi(SHORTCODE) SHORTCODE #define fGEN_TCG_S4_stored_rl_st_vi(SHORTCODE) SHORTCODE -#define fGEN_TCG_J2_trap0(SHORTCODE) \ - do { \ - uiV = uiV; \ - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->pkt.pc); \ - TCGv excp = tcg_constant_tl(HEX_EVENT_TRAP0); \ - gen_helper_raise_exception(tcg_env, excp); \ - } while (0) #endif #define fGEN_TCG_A2_nop(SHORTCODE) do { } while (0)
diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index e7f90a0..6d5d99c 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py
@@ -21,7 +21,7 @@ import re import string import hex_common - +from textwrap import dedent ## ## Generate the TCG code to call the helper @@ -49,6 +49,18 @@ def gen_tcg_func(f, tag, regs, imms): f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") + if "A_PRIV" in hex_common.attribdict[tag]: + f.write(dedent("""\ +#ifdef CONFIG_USER_ONLY + hex_gen_exception_end_tb(ctx, HEX_CAUSE_PRIV_USER_NO_SINSN); +#else +""")) + if "A_GUEST" in hex_common.attribdict[tag]: + f.write(dedent("""\ +#ifdef CONFIG_USER_ONLY + hex_gen_exception_end_tb(ctx, HEX_CAUSE_PRIV_USER_NO_GINSN); +#else +""")) if hex_common.need_ea(tag): f.write(" TCGv EA G_GNUC_UNUSED = tcg_temp_new();\n") @@ -100,6 +112,11 @@ def gen_tcg_func(f, tag, regs, imms): if reg.is_written(): reg.gen_write(f, tag) + if ( + "A_PRIV" in hex_common.attribdict[tag] + or "A_GUEST" in hex_common.attribdict[tag] + ): + f.write("#endif /* CONFIG_USER_ONLY */\n") f.write("}\n\n") @@ -124,18 +141,10 @@ def main(): f.write('#include "idef-generated-emitter.h.inc"\n\n') for tag in hex_common.tags: - ## Skip the priv instructions - if "A_PRIV" in hex_common.attribdict[tag]: - continue - ## Skip the guest instructions - if "A_GUEST" in hex_common.attribdict[tag]: - continue - ## Skip the diag instructions - if tag == "Y6_diag": - continue - if tag == "Y6_diag0": - continue - if tag == "Y6_diag1": + if hex_common.tag_ignore(tag): + f.write(f"static void generate_{tag}" + f"(DisasContext *ctx)\n") + f.write("{\n}\n\n") continue gen_def_tcg_func(f, tag, tagregs, tagimms)
diff --git a/target/hexagon/gen_tcg_sys.h b/target/hexagon/gen_tcg_sys.h new file mode 100644 index 0000000..af50322 --- /dev/null +++ b/target/hexagon/gen_tcg_sys.h
@@ -0,0 +1,139 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_GEN_TCG_SYS_H +#define HEXAGON_GEN_TCG_SYS_H + +/* System mode instructions */ +#define fGEN_TCG_Y2_swi(SHORTCODE) \ + gen_helper_swi(tcg_env, RsV) + +#define fGEN_TCG_Y2_cswi(SHORTCODE) \ + gen_helper_cswi(tcg_env, RsV) + +#define fGEN_TCG_Y2_ciad(SHORTCODE) \ + gen_helper_ciad(tcg_env, RsV) + +#define fGEN_TCG_Y4_siad(SHORTCODE) \ + gen_helper_siad(tcg_env, RsV) + +#define fGEN_TCG_Y2_iassignw(SHORTCODE) \ + gen_helper_iassignw(tcg_env, RsV) + +#define fGEN_TCG_Y2_iassignr(SHORTCODE) \ + gen_helper_iassignr(RdV, tcg_env, RsV) + +#define fGEN_TCG_Y2_getimask(SHORTCODE) \ + gen_helper_getimask(RdV, tcg_env, RsV) + +#define fGEN_TCG_Y2_setimask(SHORTCODE) \ + gen_helper_setimask(tcg_env, PtV, RsV) + +#define fGEN_TCG_Y2_setprio(SHORTCODE) \ + gen_helper_setprio(tcg_env, PtV, RsV) + +#define fGEN_TCG_Y2_crswap0(SHORTCODE) \ + do { \ + TCGv_i32 tmp = tcg_temp_new_i32(); \ + tcg_gen_mov_tl(tmp, RxV); \ + tcg_gen_mov_tl(RxV, hex_t_sreg[HEX_SREG_SGP0]); \ + if (ctx->need_commit) { \ + tcg_gen_mov_tl(ctx->t_sreg_new_value[HEX_SREG_SGP0], tmp); \ + } else { \ + tcg_gen_mov_tl(hex_t_sreg[HEX_SREG_SGP0], tmp); \ + } \ + } while (0) + +#define fGEN_TCG_Y4_crswap1(SHORTCODE) \ + do { \ + TCGv_i32 tmp = tcg_temp_new_i32(); \ + tcg_gen_mov_tl(tmp, RxV); \ + tcg_gen_mov_tl(RxV, hex_t_sreg[HEX_SREG_SGP1]); \ + if (ctx->need_commit) { \ + tcg_gen_mov_tl(ctx->t_sreg_new_value[HEX_SREG_SGP1], tmp); \ + } else { \ + tcg_gen_mov_tl(hex_t_sreg[HEX_SREG_SGP1], tmp); \ + } \ + } while (0) + +#define fGEN_TCG_Y4_crswap10(SHORTCODE) \ + do { \ + TCGv_i64 tmp = tcg_temp_new_i64(); \ + tcg_gen_mov_i64(tmp, RxxV); \ + tcg_gen_concat_i32_i64(RxxV, \ + hex_t_sreg[HEX_SREG_SGP0], \ + hex_t_sreg[HEX_SREG_SGP1]); \ + if (ctx->need_commit) { \ + tcg_gen_extrl_i64_i32( \ + ctx->t_sreg_new_value[HEX_SREG_SGP0], tmp); \ + tcg_gen_extrh_i64_i32( \ + ctx->t_sreg_new_value[HEX_SREG_SGP1], tmp); \ + } else { \ + tcg_gen_extrl_i64_i32(hex_t_sreg[HEX_SREG_SGP0], tmp); \ + tcg_gen_extrh_i64_i32(hex_t_sreg[HEX_SREG_SGP1], tmp); \ + } \ + } while (0) + +#define fGEN_TCG_Y2_wait(SHORTCODE) \ + do { \ + RsV = RsV; \ + gen_helper_wait(tcg_env, tcg_constant_tl(ctx->pkt.pc)); \ + } while (0) + +#define fGEN_TCG_Y2_resume(SHORTCODE) \ + gen_helper_resume(tcg_env, RsV) + +#define fGEN_TCG_Y2_start(SHORTCODE) \ + gen_helper_start(tcg_env, RsV) + +#define fGEN_TCG_Y2_stop(SHORTCODE) \ + do { \ + RsV = RsV; \ + gen_helper_stop(tcg_env); \ + } while (0) + +#define fGEN_TCG_Y2_tfrscrr(SHORTCODE) \ + tcg_gen_mov_tl(RdV, SsV) + +#define fGEN_TCG_Y2_tfrsrcr(SHORTCODE) \ + tcg_gen_mov_tl(SdV, RsV) + +#define fGEN_TCG_Y4_tfrscpp(SHORTCODE) \ + tcg_gen_mov_i64(RddV, SssV) + +#define fGEN_TCG_Y4_tfrspcp(SHORTCODE) \ + tcg_gen_mov_i64(SddV, RssV) + +#define fGEN_TCG_G4_tfrgcrr(SHORTCODE) \ + tcg_gen_mov_tl(RdV, GsV) + +#define fGEN_TCG_G4_tfrgrcr(SHORTCODE) \ + tcg_gen_mov_tl(GdV, RsV) + +#define fGEN_TCG_G4_tfrgcpp(SHORTCODE) \ + tcg_gen_mov_i64(RddV, GssV) + +#define fGEN_TCG_G4_tfrgpcp(SHORTCODE) \ + tcg_gen_mov_i64(GddV, RssV) + + +/* + * rte (return from exception) + * Clear the EX bit in SSR + * Jump to ELR + */ +#define fGEN_TCG_J2_rte(SHORTCODE) \ + do { \ + TCGv_i32 new_ssr = tcg_temp_new_i32(); \ + tcg_gen_deposit_tl(new_ssr, hex_t_sreg[HEX_SREG_SSR], \ + tcg_constant_tl(0), \ + reg_field_info[SSR_EX].offset, \ + reg_field_info[SSR_EX].width); \ + gen_log_sreg_write(ctx, HEX_SREG_SSR, new_ssr); \ + gen_jumpr(ctx, hex_t_sreg[HEX_SREG_ELR]); \ + } while (0) + +#endif
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 5d5adac..1ebc747 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c
@@ -23,6 +23,7 @@ #include "exec/helper-gen.h" #include "insn.h" #include "opcodes.h" +#include "sys_macros.h" #include "translate.h" #define QEMU_GENERATE /* Used internally by macros.h */ #include "macros.h" @@ -30,6 +31,10 @@ #undef QEMU_GENERATE #include "gen_tcg.h" #include "gen_tcg_hvx.h" +#ifndef CONFIG_USER_ONLY +#include "gen_tcg_sys.h" +#endif + #include "genptr.h" TCGv gen_read_reg(TCGv result, int num) @@ -120,6 +125,156 @@ } } +#ifndef CONFIG_USER_ONLY +G_GNUC_UNUSED +static bool greg_writable(int rnum, bool pair) +{ + if (pair) { + if (rnum < HEX_GREG_G3) { + return true; + } + qemu_log_mask(LOG_UNIMP, + "Warning: ignoring write to guest register pair G%d:%d\n", + rnum + 1, rnum); + } else { + if (rnum <= HEX_GREG_G3) { + return true; + } + qemu_log_mask(LOG_UNIMP, + "Warning: ignoring write to guest register G%d\n", rnum); + } + return false; +} + +G_GNUC_UNUSED +static void check_greg_impl(int rnum, bool pair) +{ + if (pair && (!greg_implemented(rnum) || !greg_implemented(rnum + 1))) { + qemu_log_mask(LOG_UNIMP, + "Warning: guest register pair G%d:%d is unimplemented or " + "reserved. Read will yield 0.\n", + rnum + 1, rnum); + } else if (!pair && !greg_implemented(rnum)) { + qemu_log_mask(LOG_UNIMP, + "Warning: guest register G%d is unimplemented or reserved." + " Read will yield 0.\n", rnum); + } +} + +G_GNUC_UNUSED +static inline void gen_log_greg_write(DisasContext *ctx, int rnum, TCGv_i32 val) +{ + tcg_gen_mov_i32(ctx->greg_new_value[rnum], val); +} + +G_GNUC_UNUSED +static void gen_log_greg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) +{ + TCGv_i32 val32 = tcg_temp_new_i32(); + + /* Low word */ + tcg_gen_extrl_i64_i32(val32, val); + gen_log_greg_write(ctx, rnum, val32); + + /* High word */ + tcg_gen_extrh_i64_i32(val32, val); + gen_log_greg_write(ctx, rnum + 1, val32); +} + +static const uint32_t sreg_immut_masks[NUM_SREGS] = { + [HEX_SREG_STID] = 0xff00ff00, + [HEX_SREG_ELR] = 0x00000003, + [HEX_SREG_SSR] = 0x00008000, + [HEX_SREG_CCR] = 0x10e0ff24, + [HEX_SREG_HTID] = IMMUTABLE, + [HEX_SREG_IMASK] = 0xffff0000, + [HEX_SREG_GEVB] = 0x000000ff, +}; + +G_GNUC_UNUSED +static void gen_log_sreg_write(DisasContext *ctx, int rnum, TCGv_i32 val) +{ + const uint32_t reg_mask = sreg_immut_masks[rnum]; + + if (reg_mask != IMMUTABLE) { + if (rnum < HEX_SREG_GLB_START) { + gen_masked_reg_write(val, hex_t_sreg[rnum], reg_mask); + if (ctx->need_commit || rnum == HEX_SREG_SSR) { + tcg_gen_mov_i32(ctx->t_sreg_new_value[rnum], val); + } else { + tcg_gen_mov_i32(hex_t_sreg[rnum], val); + } + } else { + gen_helper_sreg_write_masked(tcg_env, tcg_constant_i32(rnum), val); + } + } +} + +G_GNUC_UNUSED +static void gen_log_sreg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) +{ + TCGv_i32 val32 = tcg_temp_new_i32(); + + /* Low word */ + tcg_gen_extrl_i64_i32(val32, val); + gen_log_sreg_write(ctx, rnum, val32); + + /* High word */ + tcg_gen_extrh_i64_i32(val32, val); + gen_log_sreg_write(ctx, rnum + 1, val32); +} + +G_GNUC_UNUSED +static void gen_read_sreg(TCGv_i32 dst, int reg_num) +{ + if (reg_num >= HEX_SREG_GLB_START || reg_num == HEX_SREG_BADVA) { + gen_helper_sreg_read(dst, tcg_env, tcg_constant_i32(reg_num)); + } else { + tcg_gen_mov_i32(dst, hex_t_sreg[reg_num]); + } +} + +G_GNUC_UNUSED +static void gen_read_sreg_pair(TCGv_i64 dst, int reg_num) +{ + if (reg_num < HEX_SREG_GLB_START) { + if (reg_num + 1 == HEX_SREG_BADVA) { + TCGv_i32 badva = tcg_temp_new_i32(); + gen_helper_sreg_read(badva, tcg_env, + tcg_constant_i32(HEX_SREG_BADVA)); + tcg_gen_concat_i32_i64(dst, hex_t_sreg[reg_num], badva); + } else { + tcg_gen_concat_i32_i64(dst, hex_t_sreg[reg_num], + hex_t_sreg[reg_num + 1]); + } + } else { + gen_helper_sreg_read_pair(dst, tcg_env, tcg_constant_i32(reg_num)); + } +} + +G_GNUC_UNUSED +static void gen_read_greg(TCGv_i32 dst, int reg_num) +{ + if (reg_num <= HEX_GREG_G3) { + tcg_gen_mov_i32(dst, hex_greg[reg_num]); + } else { + gen_helper_greg_read(dst, tcg_env, tcg_constant_i32(reg_num)); + } +} + +G_GNUC_UNUSED +static void gen_read_greg_pair(TCGv_i64 dst, int reg_num) +{ + if (reg_num == HEX_GREG_G0 || reg_num == HEX_GREG_G2) { + tcg_gen_concat_i32_i64(dst, hex_greg[reg_num], + hex_greg[reg_num + 1]); + } else { + gen_helper_greg_read_pair(dst, tcg_env, tcg_constant_i32(reg_num)); + } +} +#endif + + void gen_pred_write(DisasContext *ctx, int pnum, TCGv val) { TCGv pred = get_result_pred(ctx, pnum); @@ -738,26 +893,30 @@ tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ); } -#ifndef CONFIG_HEXAGON_IDEF_PARSER /* Stack overflow check */ -static void gen_framecheck(TCGv EA, int framesize) +void gen_framecheck(DisasContext *ctx, TCGv_i32 addr, TCGv_i32 ea) { - /* Not modelled in linux-user mode */ - /* Placeholder for system mode */ #ifndef CONFIG_USER_ONLY - g_assert_not_reached(); + TCGLabel *ok = gen_new_label(); + tcg_gen_brcond_i32(TCG_COND_GEU, addr, hex_gpr[HEX_REG_FRAMELIMIT], ok); + gen_helper_raise_stack_overflow(tcg_env, + tcg_constant_i32(ctx->insn->slot), ea); + gen_set_label(ok); #endif } +#ifndef CONFIG_HEXAGON_IDEF_PARSER static void gen_allocframe(DisasContext *ctx, TCGv r29, int framesize) { TCGv r30 = get_result_gpr(ctx, HEX_REG_FP); + TCGv_i32 new_r29 = tcg_temp_new_i32(); TCGv_i64 frame; tcg_gen_addi_tl(r30, r29, -8); frame = gen_frame_scramble(); gen_store8(tcg_env, r30, frame, ctx->insn->slot); - gen_framecheck(r30, framesize); - tcg_gen_subi_tl(r29, r30, framesize); + tcg_gen_subi_tl(new_r29, r30, framesize); + gen_framecheck(ctx, new_r29, hex_gpr[HEX_REG_PC]); + tcg_gen_mov_tl(r29, new_r29); } static void gen_deallocframe(DisasContext *ctx, TCGv_i64 r31_30, TCGv r30)
diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index 6445682..033e561 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h
@@ -18,7 +18,7 @@ #include "internal.h" #include "helper_protos_generated.h.inc" -DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_RETURN, noreturn, env, i32) +DEF_HELPER_FLAGS_3(raise_exception, TCG_CALL_NO_RETURN, noreturn, env, i32, i32) DEF_HELPER_2(commit_store, void, env, int) DEF_HELPER_3(gather_store, void, env, i32, int) DEF_HELPER_1(commit_hvx_stores, void, env) @@ -107,3 +107,28 @@ DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) DEF_HELPER_2(probe_hvx_stores, void, env, int) DEF_HELPER_2(probe_pkt_scalar_hvx_stores, void, env, int) + +#if !defined(CONFIG_USER_ONLY) +DEF_HELPER_3(raise_stack_overflow, void, env, i32, i32) +DEF_HELPER_2(swi, void, env, i32) +DEF_HELPER_2(cswi, void, env, i32) +DEF_HELPER_2(ciad, void, env, i32) +DEF_HELPER_2(siad, void, env, i32) +DEF_HELPER_2(iassignw, void, env, i32) +DEF_HELPER_2(iassignr, i32, env, i32) +DEF_HELPER_2(getimask, i32, env, i32) +DEF_HELPER_3(setimask, void, env, i32, i32) +DEF_HELPER_2(sreg_read, i32, env, i32) +DEF_HELPER_2(sreg_read_pair, i64, env, i32) +DEF_HELPER_2(greg_read, i32, env, i32) +DEF_HELPER_2(greg_read_pair, i64, env, i32) +DEF_HELPER_3(sreg_write_masked, void, env, i32, i32) +DEF_HELPER_3(setprio, void, env, i32, i32) +DEF_HELPER_2(start, void, env, i32) +DEF_HELPER_1(stop, void, env) +DEF_HELPER_2(wait, void, env, i32) +DEF_HELPER_2(resume, void, env, i32) +DEF_HELPER_1(resched, void, env) +DEF_HELPER_3(modify_ssr, void, env, i32, i32) +DEF_HELPER_1(pending_interrupt, void, env) +#endif
diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index e37d5a5..d91a653 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py
@@ -33,6 +33,41 @@ overrides = {} # tags with helper overrides idef_parser_enabled = {} # tags enabled for idef-parser + +def is_sysemu_tag(tag): + return bool(attribdict[tag] & {"A_PRIV", "A_GUEST"}) + + +def tag_ignore(tag): + tag_skips = ( + "Y6_diag", + "Y6_diag0", + "Y6_diag1", + ) + attr_skips = { + "A_FAKEINSN", + "A_MAPPING", + "A_CONDMAPPING", + } + return tag in tag_skips or attribdict[tag] & attr_skips + + +def get_sys_tags(): + return sorted( + tag for tag in frozenset(tags) if is_sysemu_tag(tag) + ) + + +def get_user_tags(): + return sorted( + tag for tag in frozenset(tags) if not is_sysemu_tag(tag) + ) + + +def get_all_tags(): + return get_user_tags() + get_sys_tags() + + # We should do this as a hash for performance, # but to keep order let's keep it as a list. def uniquify(seq): @@ -93,8 +128,11 @@ def calculate_attribs(): add_qemu_macro_attrib("fTRAP", "A_IMPLICIT_READS_PC") add_qemu_macro_attrib("fSET_OVERFLOW", "A_IMPLICIT_WRITES_USR") add_qemu_macro_attrib("fSET_LPCFG", "A_IMPLICIT_WRITES_USR") + add_qemu_macro_attrib("fCLEAR_RTE_EX", "A_IMPLICIT_WRITES_SSR") add_qemu_macro_attrib("fLOAD", "A_SCALAR_LOAD") add_qemu_macro_attrib("fSTORE", "A_SCALAR_STORE") + add_qemu_macro_attrib("fSET_K0_LOCK", "A_IMPLICIT_READS_PC") + add_qemu_macro_attrib("fSET_TLB_LOCK", "A_IMPLICIT_READS_PC") add_qemu_macro_attrib('fLSBNEW0', 'A_IMPLICIT_READS_P0') add_qemu_macro_attrib('fLSBNEW0NOT', 'A_IMPLICIT_READS_P0') add_qemu_macro_attrib('fREAD_P0', 'A_IMPLICIT_READS_P0') @@ -215,7 +253,9 @@ def need_env(tag): "A_LOAD" in attribdict[tag] or "A_CVI_GATHER" in attribdict[tag] or "A_CVI_SCATTER" in attribdict[tag] or - "A_IMPLICIT_WRITES_USR" in attribdict[tag]) + "A_IMPLICIT_WRITES_USR" in attribdict[tag] or + "A_PRIV" in attribdict[tag] or + "J2_trap" in tag) def need_slot(tag): @@ -224,6 +264,9 @@ def need_slot(tag): and "A_CVI_GATHER" not in attribdict[tag] and ("A_STORE" in attribdict[tag] or "A_LOAD" in attribdict[tag]) + and tag != "L4_loadw_phys" + and tag != "L6_memcpy" + and tag != "Y6_dmlink" ): return 1 else: @@ -247,7 +290,9 @@ def need_next_PC(tag): def need_pkt_has_multi_cof(tag): - return "A_COF" in attribdict[tag] + if attribdict[tag] & {"A_JUMP", "A_CALL"}: + return tag != "J4_hintjumpr" + return False def need_pkt_need_commit(tag): @@ -367,12 +412,16 @@ def helper_proto_type(self): return "s32" def helper_arg_type(self): return "int32_t" + def is_pair(self): + return False class Pair(Scalar): def helper_proto_type(self): return "s64" def helper_arg_type(self): return "int64_t" + def is_pair(self): + return True class Hvx: def is_scalar_reg(self): @@ -1008,6 +1057,125 @@ def analyze_write(self, f, tag, regno): ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper); """)) +class GuestRegister(Register): + pass + +class GuestDest(GuestRegister, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_greg_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_greg_write(ctx, {self.reg_num}); + """)) + +class GuestSource(GuestRegister, Single, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + gen_read_greg({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + +class GuestPairDest(GuestRegister, Pair, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_greg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_greg_write_pair(ctx, {self.reg_num}); + """)) + +class GuestPairSource(GuestRegister, Pair, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + gen_read_greg_pair({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + +class SystemDest(Register, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_sreg_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_sreg_write(ctx, {self.reg_num}); + """)) + +class SystemSource(Register, Single, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + gen_read_sreg({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + +class SystemPairDest(Register, Pair, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_sreg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_sreg_write_pair(ctx, {self.reg_num}); + """)) + +class SystemPairSource(Register, Pair, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + gen_read_sreg_pair({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + def init_registers(): regs = { GprDest("R", "d"), @@ -1054,6 +1222,16 @@ def init_registers(): QRegSource("Q", "u"), QRegSource("Q", "v"), QRegReadWrite("Q", "x"), + + # system regs + GuestDest("G", "d"), + GuestSource("G", "s"), + GuestPairDest("G", "dd"), + GuestPairSource("G", "ss"), + SystemDest("S", "d"), + SystemSource("S", "s"), + SystemPairDest("S", "dd"), + SystemPairSource("S", "ss"), } for reg in regs: registers[f"{reg.regtype}{reg.regid}"] = reg @@ -1197,6 +1375,7 @@ def parse_common_args(desc): parser.add_argument("semantics", help="semantics file") parser.add_argument("overrides", help="overrides file") parser.add_argument("overrides_vec", help="vector overrides file") + parser.add_argument("overrides_sys", help="system overrides file") parser.add_argument("out", help="output file") parser.add_argument("--idef-parser", help="file of instructions translated by idef-parser") @@ -1204,6 +1383,7 @@ def parse_common_args(desc): read_semantics_file(args.semantics) read_overrides_file(args.overrides) read_overrides_file(args.overrides_vec) + read_overrides_file(args.overrides_sys) if args.idef_parser: read_idef_parser_enabled_file(args.idef_parser) calculate_attribs()
diff --git a/target/hexagon/hex_interrupts.c b/target/hexagon/hex_interrupts.c new file mode 100644 index 0000000..3534481 --- /dev/null +++ b/target/hexagon/hex_interrupts.c
@@ -0,0 +1,371 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "cpu_helper.h" +#include "exec/cpu-interrupt.h" +#include "hex_interrupts.h" +#include "macros.h" +#include "sys_macros.h" +#include "system/cpus.h" +#include "hw/hexagon/hexagon_globalreg.h" + +static bool hex_is_qualified_for_int(CPUHexagonState *env, int int_num); + +static bool get_syscfg_gie(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t syscfg = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId); + return GET_SYSCFG_FIELD(SYSCFG_GIE, syscfg); +} + +static bool get_ssr_ex(CPUHexagonState *env) +{ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + return GET_SSR_FIELD(SSR_EX, ssr); +} + +static bool get_ssr_ie(CPUHexagonState *env) +{ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + return GET_SSR_FIELD(SSR_IE, ssr); +} + +/* Do these together so we only have to call hexagon_modify_ssr once */ +static void set_ssr_ex_cause(CPUHexagonState *env, int ex, uint32_t cause) +{ + uint32_t old, new; + + old = env->t_sreg[HEX_SREG_SSR]; + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, ex); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause); + new = env->t_sreg[HEX_SREG_SSR]; + hexagon_modify_ssr(env, new, old); +} + +static bool get_iad_bit(CPUHexagonState *env, int int_num) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t iad = GET_FIELD(IPENDAD_IAD, ipendad); + return extract32(iad, int_num, 1); +} + +static void set_iad_bit(CPUHexagonState *env, int int_num, int val) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t iad = GET_FIELD(IPENDAD_IAD, ipendad); + iad = deposit32(iad, int_num, 1, val); + fSET_FIELD(ipendad, IPENDAD_IAD, iad); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static uint32_t get_ipend(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + return GET_FIELD(IPENDAD_IPEND, ipendad); +} + +static inline bool get_ipend_bit(CPUHexagonState *env, int int_num) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + return extract32(ipend, int_num, 1); +} + +static void clear_ipend(CPUHexagonState *env, uint32_t mask) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend &= ~mask; + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static void set_ipend(CPUHexagonState *env, uint32_t mask) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend |= mask; + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static void set_ipend_bit(CPUHexagonState *env, int int_num, int val) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend = deposit32(ipend, int_num, 1, val); + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static bool get_imask_bit(CPUHexagonState *env, int int_num) +{ + uint32_t imask = env->t_sreg[HEX_SREG_IMASK]; + return extract32(imask, int_num, 1); +} + +static uint32_t get_prio(CPUHexagonState *env) +{ + uint32_t stid = env->t_sreg[HEX_SREG_STID]; + return extract32(stid, reg_field_info[STID_PRIO].offset, + reg_field_info[STID_PRIO].width); +} + +static void set_elr(CPUHexagonState *env, uint32_t val) +{ + env->t_sreg[HEX_SREG_ELR] = val; +} + +static bool get_schedcfgen(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t schedcfg = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG, + env->threadId); + return extract32(schedcfg, reg_field_info[SCHEDCFG_EN].offset, + reg_field_info[SCHEDCFG_EN].width); +} + +static bool is_lowest_prio(CPUHexagonState *env, int int_num) +{ + uint32_t my_prio = get_prio(env); + CPUState *cs; + + CPU_FOREACH(cs) { + CPUHexagonState *hex_env = cpu_env(cs); + if (!hex_is_qualified_for_int(hex_env, int_num)) { + continue; + } + + /* Note that lower values indicate *higher* priority */ + if (my_prio < get_prio(hex_env)) { + return false; + } + } + return true; +} + +static bool hex_is_qualified_for_int(CPUHexagonState *env, int int_num) +{ + bool syscfg_gie = get_syscfg_gie(env); + bool iad = get_iad_bit(env, int_num); + bool ssr_ie = get_ssr_ie(env); + bool ssr_ex = get_ssr_ex(env); + bool imask = get_imask_bit(env, int_num); + + return syscfg_gie && !iad && ssr_ie && !ssr_ex && !imask; +} + +static void clear_pending_locks(CPUHexagonState *env) +{ + g_assert(bql_locked()); + if (env->k0_lock_state == HEX_LOCK_WAITING) { + env->k0_lock_state = HEX_LOCK_UNLOCKED; + } + if (env->tlb_lock_state == HEX_LOCK_WAITING) { + env->tlb_lock_state = HEX_LOCK_UNLOCKED; + } +} + +static bool should_not_exec(CPUHexagonState *env) +{ + return (get_exe_mode(env) == HEX_EXE_MODE_WAIT); +} + +static void restore_state(CPUHexagonState *env, bool int_accepted) +{ + CPUState *cs = env_cpu(env); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_SWI); + if (!int_accepted && should_not_exec(env)) { + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + } +} + +static void hex_accept_int(CPUHexagonState *env, int int_num) +{ + CPUState *cs = env_cpu(env); + HexagonCPU *cpu = env_archcpu(env); + uint32_t evb = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB, + env->threadId); + const int exe_mode = get_exe_mode(env); + const bool in_wait_mode = exe_mode == HEX_EXE_MODE_WAIT; + + set_ipend_bit(env, int_num, 0); + set_iad_bit(env, int_num, 1); + set_ssr_ex_cause(env, 1, HEX_CAUSE_INT0 | int_num); + cs->exception_index = HEX_EVENT_INT0 + int_num; + env->cause_code = HEX_EVENT_INT0 + int_num; + clear_pending_locks(env); + if (in_wait_mode) { + qemu_log_mask(CPU_LOG_INT, + "%s: thread " TARGET_FMT_ld " resuming, exiting WAIT mode\n", + __func__, env->threadId); + set_elr(env, env->wait_next_pc); + clear_wait_mode(env); + cs->halted = false; + } else if (env->k0_lock_state == HEX_LOCK_WAITING) { + g_assert_not_reached(); + } else { + set_elr(env, env->gpr[HEX_REG_PC]); + } + env->gpr[HEX_REG_PC] = evb | (cs->exception_index << 2); + if (get_ipend(env) == 0) { + restore_state(env, true); + } +} + + +bool hex_check_interrupts(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + bool int_handled = false; + bool ssr_ex = get_ssr_ex(env); + int max_ints = 32; + bool schedcfgen; + + /* Early exit if nothing pending */ + if (get_ipend(env) == 0) { + restore_state(env, false); + return false; + } + + BQL_LOCK_GUARD(); + /* Only check priorities when schedcfgen is set */ + schedcfgen = get_schedcfgen(env); + for (int i = 0; i < max_ints; i++) { + if (!get_iad_bit(env, i) && get_ipend_bit(env, i)) { + bool syscfg_gie, iad, ssr_ie, imask; + + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] " + "pc = 0x" TARGET_FMT_lx + " found int %d\n", + __func__, env->threadId, + env->gpr[HEX_REG_PC], i); + if (hex_is_qualified_for_int(env, i) && + (!schedcfgen || is_lowest_prio(env, i))) { + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] int %d handled_\n", + __func__, env->threadId, i); + hex_accept_int(env, i); + int_handled = true; + break; + } + syscfg_gie = get_syscfg_gie(env); + iad = get_iad_bit(env, i); + ssr_ie = get_ssr_ie(env); + imask = get_imask_bit(env, i); + + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] " + "int %d not handled, qualified: %d, " + "schedcfg_en: %d, low prio %d\n", + __func__, env->threadId, i, + hex_is_qualified_for_int(env, i), schedcfgen, + is_lowest_prio(env, i)); + + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] " + "int %d not handled, GIE %d, iad %d, " + "SSR:IE %d, SSR:EX: %d, imask bit %d\n", + __func__, env->threadId, i, syscfg_gie, iad, ssr_ie, + ssr_ex, imask); + } + } + + /* + * If we didn't handle the interrupt and it wasn't + * because we were in EX state, then we won't be able + * to execute the interrupt on this CPU unless something + * changes in the CPU state. Clear the interrupt_request bits + * while preserving the IPEND bits, and we can re-assert the + * interrupt_request bit(s) when we execute one of those instructions. + */ + if (!int_handled && !ssr_ex) { + restore_state(env, int_handled); + } else if (int_handled) { + assert(!cs->halted); + } + + return int_handled; +} + +void hex_clear_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type) +{ + if (mask == 0) { + return; + } + + /* + * Notify all CPUs that the interrupt has happened + */ + BQL_LOCK_GUARD(); + clear_ipend(env, mask); + hex_interrupt_update(env); +} + +void hex_raise_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type) +{ + g_assert(bql_locked()); + if (mask == 0) { + return; + } + + /* + * Notify all CPUs that the interrupt has happened + */ + set_ipend(env, mask); + hex_interrupt_update(env); +} + +void hex_interrupt_update(CPUHexagonState *env) +{ + CPUState *cs; + + g_assert(bql_locked()); + if (get_ipend(env) != 0) { + CPU_FOREACH(cs) { + CPUHexagonState *hex_env = cpu_env(cs); + const int exe_mode = get_exe_mode(hex_env); + if (exe_mode != HEX_EXE_MODE_OFF) { + cpu_interrupt(cs, CPU_INTERRUPT_SWI); + cpu_resume(cs); + } + } + } +}
diff --git a/target/hexagon/hex_interrupts.h b/target/hexagon/hex_interrupts.h new file mode 100644 index 0000000..6b6f540 --- /dev/null +++ b/target/hexagon/hex_interrupts.h
@@ -0,0 +1,15 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEX_INTERRUPTS_H +#define HEX_INTERRUPTS_H + +bool hex_check_interrupts(CPUHexagonState *env); +void hex_clear_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type); +void hex_raise_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type); +void hex_interrupt_update(CPUHexagonState *env); + +#endif
diff --git a/target/hexagon/hex_mmu.c b/target/hexagon/hex_mmu.c new file mode 100644 index 0000000..81d64a6 --- /dev/null +++ b/target/hexagon/hex_mmu.c
@@ -0,0 +1,270 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/qemu-print.h" +#include "cpu.h" +#include "system/cpus.h" +#include "internal.h" +#include "exec/cpu-interrupt.h" +#include "cpu_helper.h" +#include "exec/cputlb.h" +#include "hex_mmu.h" +#include "macros.h" +#include "sys_macros.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/hexagon/hexagon_globalreg.h" + +static inline void hex_log_tlbw(uint32_t index, uint64_t entry) +{ + qemu_log_mask(CPU_LOG_MMU, + "tlbw[%03" PRIu32 "]: 0x%016" PRIx64 "\n", + index, entry); +} + +void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value) +{ + uint32_t myidx = fTLB_NONPOW2WRAP(fTLB_IDXMASK(index)); + HexagonTLBState *tlb = env_archcpu(env)->tlb; + uint64_t old_entry = hexagon_tlb_read(tlb, myidx); + + bool old_entry_valid = extract64(old_entry, 63, 1); + if (old_entry_valid && hexagon_cpu_mmu_enabled(env)) { + CPUState *cs = env_cpu(env); + tlb_flush(cs); + } + hexagon_tlb_write(tlb, myidx, value); + hex_log_tlbw(myidx, value); +} + +void hex_mmu_on(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned on!\n"); + tlb_flush(cs); +} + +void hex_mmu_off(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned off!\n"); + tlb_flush(cs); +} + +void hex_mmu_mode_change(CPUHexagonState *env) +{ + qemu_log_mask(CPU_LOG_MMU, "Hexagon mode change!\n"); + CPUState *cs = env_cpu(env); + tlb_flush(cs); +} + +bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA, + MMUAccessType access_type, hwaddr *PA, int *prot, + uint64_t *size, int32_t *excp, int mmu_idx) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr); + int cause_code = 0; + + bool found = hexagon_tlb_find_match(cpu->tlb, asid, VA, access_type, + PA, prot, size, excp, &cause_code, + mmu_idx); + if (cause_code) { + env->cause_code = cause_code; + } + return found; +} + +/* Called from tlbp instruction */ +uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA) +{ + HexagonCPU *cpu = env_archcpu(env); + uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr); + int cause_code = 0; + + uint32_t result = hexagon_tlb_lookup(cpu->tlb, asid, VA, &cause_code); + if (cause_code) { + env->cause_code = cause_code; + } + return result; +} + +/* + * Return codes: + * 0 or positive index of match + * -1 multiple matches + * -2 no match + */ +int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry, uint64_t index) +{ + HexagonCPU *cpu = env_archcpu(env); + return hexagon_tlb_check_overlap(cpu->tlb, entry, index); +} + +void dump_mmu(Monitor *mon, CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + hexagon_tlb_dump(mon, cpu->tlb); +} + +static inline void print_thread(const char *str, CPUState *cs) +{ + g_assert(bql_locked()); + CPUHexagonState *thread = cpu_env(cs); + bool is_stopped = cpu_is_stopped(cs); + int exe_mode = get_exe_mode(thread); + hex_lock_state_t lock_state = thread->tlb_lock_state; + qemu_log_mask(CPU_LOG_MMU, + "%s: threadId = %" PRIu32 ": %s," + " exe_mode = %s, tlb_lock_state = %s\n", + str, + thread->threadId, + is_stopped ? "stopped" : "running", + exe_mode == HEX_EXE_MODE_OFF ? "off" : + exe_mode == HEX_EXE_MODE_RUN ? "run" : + exe_mode == HEX_EXE_MODE_WAIT ? "wait" : + exe_mode == HEX_EXE_MODE_DEBUG ? "debug" : + "unknown", + lock_state == HEX_LOCK_UNLOCKED ? "unlocked" : + lock_state == HEX_LOCK_WAITING ? "waiting" : + lock_state == HEX_LOCK_OWNER ? "owner" : + "unknown"); +} + +static inline void print_thread_states(const char *str) +{ + CPUState *cs; + CPU_FOREACH(cs) { + print_thread(str, cs); + } +} + +void hex_tlb_lock(CPUHexagonState *env) +{ + qemu_log_mask(CPU_LOG_MMU, "hex_tlb_lock: " TARGET_FMT_ld "\n", + env->threadId); + BQL_LOCK_GUARD(); + g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1)); + + HexagonCPU *cpu = env_archcpu(env); + uint32_t syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg); + if (tlb_lock) { + if (env->tlb_lock_state == HEX_LOCK_QUEUED) { + env->next_PC += 4; + env->tlb_lock_count++; + env->tlb_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1); + return; + } + if (env->tlb_lock_state == HEX_LOCK_OWNER) { + qemu_log_mask(CPU_LOG_MMU | LOG_GUEST_ERROR, + "Double tlblock at PC: 0x%" PRIx32 + ", thread may hang\n", + env->next_PC); + env->next_PC += 4; + CPUState *cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + return; + } + env->tlb_lock_state = HEX_LOCK_WAITING; + CPUState *cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + } else { + env->next_PC += 4; + env->tlb_lock_count++; + env->tlb_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1); + } + + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_lock:\n"); + print_thread_states("\tThread"); + } +} + +void hex_tlb_unlock(CPUHexagonState *env) +{ + BQL_LOCK_GUARD(); + g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1)); + + /* Nothing to do if the TLB isn't locked by this thread */ + HexagonCPU *cpu = env_archcpu(env); + uint32_t syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg); + if ((tlb_lock == 0) || + (env->tlb_lock_state != HEX_LOCK_OWNER)) { + qemu_log_mask(LOG_GUEST_ERROR, + "thread %" PRIu32 " attempted to tlbunlock" + " without having the lock, tlb_lock state = %u\n", + env->threadId, (unsigned)env->tlb_lock_state); + g_assert(env->tlb_lock_state != HEX_LOCK_WAITING); + return; + } + + env->tlb_lock_count--; + env->tlb_lock_state = HEX_LOCK_UNLOCKED; + SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 0); + + /* Look for a thread to unlock */ + unsigned int this_threadId = env->threadId; + CPUHexagonState *unlock_thread = NULL; + CPUState *cs; + CPU_FOREACH(cs) { + CPUHexagonState *thread = cpu_env(cs); + + /* + * The hardware implements round-robin fairness, so we look for threads + * starting at env->threadId + 1 and incrementing modulo the number of + * threads. + * + * To implement this, we check if thread is a earlier in the modulo + * sequence than unlock_thread. + * if unlock thread is higher than this thread + * thread must be between this thread and unlock_thread + * else + * thread higher than this thread is ahead of unlock_thread + * thread must be lower then unlock thread + */ + if (thread->tlb_lock_state == HEX_LOCK_WAITING) { + if (!unlock_thread) { + unlock_thread = thread; + } else if (unlock_thread->threadId > this_threadId) { + if (this_threadId < thread->threadId && + thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } else { + if (thread->threadId > this_threadId) { + unlock_thread = thread; + } + if (thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } + } + } + if (unlock_thread) { + cs = env_cpu(unlock_thread); + print_thread("\tWaiting thread found", cs); + unlock_thread->tlb_lock_state = HEX_LOCK_QUEUED; + SET_SYSCFG_FIELD(unlock_thread, SYSCFG_TLBLOCK, 1); + cpu_interrupt(cs, CPU_INTERRUPT_TLB_UNLOCK); + } + + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_unlock:\n"); + print_thread_states("\tThread"); + } + +}
diff --git a/target/hexagon/hex_mmu.h b/target/hexagon/hex_mmu.h new file mode 100644 index 0000000..4f556c7 --- /dev/null +++ b/target/hexagon/hex_mmu.h
@@ -0,0 +1,26 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_MMU_H +#define HEXAGON_MMU_H + +#include "cpu.h" +#include "monitor/monitor.h" + +extern void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value); +extern uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA); +extern void hex_mmu_on(CPUHexagonState *env); +extern void hex_mmu_off(CPUHexagonState *env); +extern void hex_mmu_mode_change(CPUHexagonState *env); +extern bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA, + MMUAccessType access_type, hwaddr *PA, int *prot, + uint64_t *size, int32_t *excp, int mmu_idx); +extern int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry, + uint64_t index); +extern void hex_tlb_lock(CPUHexagonState *env); +extern void hex_tlb_unlock(CPUHexagonState *env); +void dump_mmu(Monitor *mon, CPUHexagonState *env); +#endif
diff --git a/target/hexagon/hex_regs.h b/target/hexagon/hex_regs.h index bddfc28..214b7b8 100644 --- a/target/hexagon/hex_regs.h +++ b/target/hexagon/hex_regs.h
@@ -81,4 +81,121 @@ HEX_REG_UTIMERHI = 63, }; +#ifndef CONFIG_USER_ONLY + +#define HEX_GREG_VALUES \ + DECL_HEX_GREG(G0, 0) \ + DECL_HEX_GREG(GELR, 0) \ + DECL_HEX_GREG(G1, 1) \ + DECL_HEX_GREG(GSR, 1) \ + DECL_HEX_GREG(G2, 2) \ + DECL_HEX_GREG(GOSP, 2) \ + DECL_HEX_GREG(G3, 3) \ + DECL_HEX_GREG(GBADVA, 3) \ + DECL_HEX_GREG(GCYCLE_1T, 10) \ + DECL_HEX_GREG(GCYCLE_2T, 11) \ + DECL_HEX_GREG(GCYCLE_3T, 12) \ + DECL_HEX_GREG(GCYCLE_4T, 13) \ + DECL_HEX_GREG(GCYCLE_5T, 14) \ + DECL_HEX_GREG(GCYCLE_6T, 15) \ + DECL_HEX_GREG(GPMUCNT4, 16) \ + DECL_HEX_GREG(GPMUCNT5, 17) \ + DECL_HEX_GREG(GPMUCNT6, 18) \ + DECL_HEX_GREG(GPMUCNT7, 19) \ + DECL_HEX_GREG(GPCYCLELO, 24) \ + DECL_HEX_GREG(GPCYCLEHI, 25) \ + DECL_HEX_GREG(GPMUCNT0, 26) \ + DECL_HEX_GREG(GPMUCNT1, 27) \ + DECL_HEX_GREG(GPMUCNT2, 28) \ + DECL_HEX_GREG(GPMUCNT3, 29) \ + DECL_HEX_GREG_DONE + +#define DECL_HEX_GREG_DONE +#define DECL_HEX_GREG(name, val) HEX_GREG_ ##name = val, +enum hex_greg { + HEX_GREG_VALUES +}; +#undef DECL_HEX_GREG +#undef DECL_HEX_GREG_DONE + +#define DECL_HEX_GREG_DONE 0 +#define DECL_HEX_GREG(_, val) (1 << val) | +static inline bool greg_implemented(enum hex_greg greg) +{ +#if NUM_GREGS > 32 +#error "NUM_GREGS too large for greg_implemented(): update `impl_bitmap`" +#endif + static int32_t impl_bitmap = HEX_GREG_VALUES; + return impl_bitmap & (1 << greg); +} +#undef DECL_HEX_GREG +#undef DECL_HEX_GREG_DONE + +#endif /* CONFIG_USER_ONLY */ + +#ifndef CONFIG_USER_ONLY +enum { + HEX_SREG_SGP0 = 0, + HEX_SREG_SGP1 = 1, + HEX_SREG_STID = 2, + HEX_SREG_ELR = 3, + HEX_SREG_BADVA0 = 4, + HEX_SREG_BADVA1 = 5, + HEX_SREG_SSR = 6, + HEX_SREG_CCR = 7, + HEX_SREG_HTID = 8, + HEX_SREG_BADVA = 9, + HEX_SREG_IMASK = 10, + HEX_SREG_GEVB = 11, + HEX_SREG_GLB_START = 16, + HEX_SREG_EVB = 16, + HEX_SREG_MODECTL = 17, + HEX_SREG_SYSCFG = 18, + HEX_SREG_IPENDAD = 20, + HEX_SREG_VID = 21, + HEX_SREG_VID1 = 22, + HEX_SREG_BESTWAIT = 23, + HEX_SREG_IEL = 24, + HEX_SREG_SCHEDCFG = 25, + HEX_SREG_IAHL = 26, + HEX_SREG_CFGBASE = 27, + HEX_SREG_DIAG = 28, + HEX_SREG_REV = 29, + HEX_SREG_PCYCLELO = 30, + HEX_SREG_PCYCLEHI = 31, + HEX_SREG_ISDBST = 32, + HEX_SREG_ISDBCFG0 = 33, + HEX_SREG_ISDBCFG1 = 34, + HEX_SREG_LIVELOCK = 35, + HEX_SREG_BRKPTPC0 = 36, + HEX_SREG_BRKPTCFG0 = 37, + HEX_SREG_BRKPTPC1 = 38, + HEX_SREG_BRKPTCFG1 = 39, + HEX_SREG_ISDBMBXIN = 40, + HEX_SREG_ISDBMBXOUT = 41, + HEX_SREG_ISDBEN = 42, + HEX_SREG_ISDBGPR = 43, + HEX_SREG_PMUCNT4 = 44, + HEX_SREG_PMUCNT5 = 45, + HEX_SREG_PMUCNT6 = 46, + HEX_SREG_PMUCNT7 = 47, + HEX_SREG_PMUCNT0 = 48, + HEX_SREG_PMUCNT1 = 49, + HEX_SREG_PMUCNT2 = 50, + HEX_SREG_PMUCNT3 = 51, + HEX_SREG_PMUEVTCFG = 52, + HEX_SREG_PMUSTID0 = 53, + HEX_SREG_PMUEVTCFG1 = 54, + HEX_SREG_PMUSTID1 = 55, + HEX_SREG_TIMERLO = 56, + HEX_SREG_TIMERHI = 57, + HEX_SREG_PMUCFG = 58, + HEX_SREG_S59 = 59, + HEX_SREG_S60 = 60, + HEX_SREG_S61 = 61, + HEX_SREG_S62 = 62, + HEX_SREG_S63 = 63, +}; +#endif /* !CONFIG_USER_ONLY */ + #endif
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c new file mode 100644 index 0000000..43c373e --- /dev/null +++ b/target/hexagon/hexswi.c
@@ -0,0 +1,271 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_helper.h" +#include "exec/helper-proto.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "arch.h" +#include "internal.h" +#include "macros.h" +#include "sys_macros.h" +#include "accel/tcg/cpu-loop.h" +#include "tcg/tcg-op.h" +#include "hex_mmu.h" +#include "hexswi.h" +#include "hw/hexagon/hexagon_globalreg.h" + +#ifdef CONFIG_USER_ONLY +#error "This file is only used in system emulation" +#endif + +static void set_addresses(CPUHexagonState *env, uint32_t pc_offset, + uint32_t exception_index) + +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t evb = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB, + env->threadId) : + cpu->boot_addr; + env->t_sreg[HEX_SREG_ELR] = env->gpr[HEX_REG_PC] + pc_offset; + env->gpr[HEX_REG_PC] = evb | (exception_index << 2); +} + +static const char *event_name[] = { + [HEX_EVENT_RESET] = "HEX_EVENT_RESET", + [HEX_EVENT_IMPRECISE] = "HEX_EVENT_IMPRECISE", + [HEX_EVENT_PRECISE] = "HEX_EVENT_PRECISE", + [HEX_EVENT_TLB_MISS_X] = "HEX_EVENT_TLB_MISS_X", + [HEX_EVENT_TLB_MISS_RW] = "HEX_EVENT_TLB_MISS_RW", + [HEX_EVENT_TRAP0] = "HEX_EVENT_TRAP0", + [HEX_EVENT_TRAP1] = "HEX_EVENT_TRAP1", + [HEX_EVENT_FPTRAP] = "HEX_EVENT_FPTRAP", + [HEX_EVENT_DEBUG] = "HEX_EVENT_DEBUG", + [HEX_EVENT_INT0] = "HEX_EVENT_INT0", + [HEX_EVENT_INT1] = "HEX_EVENT_INT1", + [HEX_EVENT_INT2] = "HEX_EVENT_INT2", + [HEX_EVENT_INT3] = "HEX_EVENT_INT3", + [HEX_EVENT_INT4] = "HEX_EVENT_INT4", + [HEX_EVENT_INT5] = "HEX_EVENT_INT5", + [HEX_EVENT_INT6] = "HEX_EVENT_INT6", + [HEX_EVENT_INT7] = "HEX_EVENT_INT7", + [HEX_EVENT_INT8] = "HEX_EVENT_INT8", + [HEX_EVENT_INT9] = "HEX_EVENT_INT9", + [HEX_EVENT_INTA] = "HEX_EVENT_INTA", + [HEX_EVENT_INTB] = "HEX_EVENT_INTB", + [HEX_EVENT_INTC] = "HEX_EVENT_INTC", + [HEX_EVENT_INTD] = "HEX_EVENT_INTD", + [HEX_EVENT_INTE] = "HEX_EVENT_INTE", + [HEX_EVENT_INTF] = "HEX_EVENT_INTF" +}; + +void hexagon_cpu_do_interrupt(CPUState *cs) + +{ + CPUHexagonState *env = cpu_env(cs); + uint32_t ssr; + + BQL_LOCK_GUARD(); + + qemu_log_mask(CPU_LOG_INT, + "\t%s: event 0x%02x:%s, cause 0x%" PRIx32 "(%" PRIu32 ")\n", + __func__, (unsigned)cs->exception_index, + event_name[cs->exception_index], env->cause_code, + env->cause_code); + + env->llsc_addr = ~0; + + ssr = env->t_sreg[HEX_SREG_SSR]; + if (GET_SSR_FIELD(SSR_EX, ssr) == 1) { + HexagonCPU *cpu = env_archcpu(env); + if (cpu->globalregs) { + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_DIAG, + env->cause_code, env->threadId); + } + env->cause_code = HEX_CAUSE_DOUBLE_EXCEPT; + cs->exception_index = HEX_EVENT_PRECISE; + } + + switch (cs->exception_index) { + case HEX_EVENT_TRAP0: + if (env->cause_code == 0) { + qemu_log_mask(LOG_UNIMP, + "trap0 is unhandled, no semihosting available\n"); + } + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 4, cs->exception_index); + break; + + case HEX_EVENT_TRAP1: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 4, cs->exception_index); + break; + + case HEX_EVENT_TLB_MISS_X: + switch (env->cause_code) { + case HEX_CAUSE_TLBMISSX_CAUSE_NORMAL: + case HEX_CAUSE_TLBMISSX_CAUSE_NEXTPAGE: + qemu_log_mask(CPU_LOG_MMU, + "TLB miss EX exception (0x%02" PRIx32 ") caught: " + "Cause code (0x%" PRIx32 ") " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + env->t_sreg[HEX_SREG_BADVA]); + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + default: + cpu_abort(cs, + "1:Hexagon exception %" PRId32 "/0x%02" PRIx32 ": " + "Unknown cause code %" PRIu32 "/0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + (uint32_t)cs->exception_index, + env->cause_code, + env->cause_code); + break; + } + break; + + case HEX_EVENT_TLB_MISS_RW: + switch (env->cause_code) { + case HEX_CAUSE_TLBMISSRW_CAUSE_READ: + case HEX_CAUSE_TLBMISSRW_CAUSE_WRITE: + qemu_log_mask(CPU_LOG_MMU, + "TLB miss RW exception (0x%02" PRIx32 ") caught: " + "Cause code (0x%" PRIx32 ") " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + env->t_sreg[HEX_SREG_BADVA]); + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + /* env->sreg[HEX_SREG_BADVA] is set when the exception is raised */ + break; + + default: + cpu_abort(cs, + "2:Hexagon exception %" PRId32 "/0x%02" PRIx32 ": " + "Unknown cause code %" PRIu32 "/0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + (uint32_t)cs->exception_index, + env->cause_code, + env->cause_code); + break; + } + break; + + case HEX_EVENT_FPTRAP: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + case HEX_EVENT_DEBUG: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + qemu_log_mask(LOG_UNIMP, "single-step exception is not handled\n"); + break; + + case HEX_EVENT_PRECISE: + switch (env->cause_code) { + case HEX_CAUSE_FETCH_NO_XPAGE: + case HEX_CAUSE_FETCH_NO_UPAGE: + case HEX_CAUSE_PRIV_NO_READ: + case HEX_CAUSE_PRIV_NO_UREAD: + case HEX_CAUSE_PRIV_NO_WRITE: + case HEX_CAUSE_PRIV_NO_UWRITE: + case HEX_CAUSE_MISALIGNED_LOAD: + case HEX_CAUSE_MISALIGNED_STORE: + case HEX_CAUSE_PC_NOT_ALIGNED: + qemu_log_mask(CPU_LOG_MMU, + "MMU permission exception (0x%02" PRIx32 ") caught: " + "Cause code (0x%" PRIx32 ") " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + env->t_sreg[HEX_SREG_BADVA]); + + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + /* env->sreg[HEX_SREG_BADVA] is set when the exception is raised */ + break; + + case HEX_CAUSE_DOUBLE_EXCEPT: + case HEX_CAUSE_PRIV_USER_NO_SINSN: + case HEX_CAUSE_PRIV_USER_NO_GINSN: + case HEX_CAUSE_INVALID_OPCODE: + case HEX_CAUSE_NO_COPROC_ENABLE: + case HEX_CAUSE_NO_COPROC2_ENABLE: + case HEX_CAUSE_UNSUPPORTED_HVX_64B: + case HEX_CAUSE_REG_WRITE_CONFLICT: + case HEX_CAUSE_VWCTRL_WINDOW_MISS: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + case HEX_CAUSE_COPROC_LDST: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + case HEX_CAUSE_STACK_LIMIT: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + default: + cpu_abort(cs, + "3:Hexagon exception %" PRId32 "/0x%02" PRIx32 ": " + "Unknown cause code %" PRIu32 "/0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + (uint32_t)cs->exception_index, + env->cause_code, + env->cause_code); + break; + } + break; + + case HEX_EVENT_IMPRECISE: + qemu_log_mask(LOG_UNIMP, + "Imprecise exception: this case is not yet handled"); + break; + + default: + qemu_log_mask(LOG_UNIMP, + "Hexagon Unsupported exception 0x%02x/0x%" PRIx32 "\n", + (unsigned)cs->exception_index, env->cause_code); + break; + } + + cs->exception_index = HEX_EVENT_NONE; +} + +void register_trap_exception(CPUHexagonState *env, int traptype, int imm, + uint32_t PC) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = (traptype == 0) ? HEX_EVENT_TRAP0 : HEX_EVENT_TRAP1; + ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index); + + env->cause_code = imm; + env->gpr[HEX_REG_PC] = PC; + cpu_loop_exit(cs); +}
diff --git a/target/hexagon/hexswi.h b/target/hexagon/hexswi.h new file mode 100644 index 0000000..48c1ae6 --- /dev/null +++ b/target/hexagon/hexswi.h
@@ -0,0 +1,17 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXSWI_H +#define HEXSWI_H + + +#include "cpu.h" + +void hexagon_cpu_do_interrupt(CPUState *cpu); +void register_trap_exception(CPUHexagonState *env, int type, int imm, + uint32_t PC); + +#endif /* HEXSWI_H */
diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y index c6f17c6..3cffafa 100644 --- a/target/hexagon/idef-parser/idef-parser.y +++ b/target/hexagon/idef-parser/idef-parser.y
@@ -404,6 +404,9 @@ ; frame_check : FCHK '(' rvalue ',' rvalue ')' ';' + { + gen_framecheck(c, &@1, &$3, &$5); + } ; cancel_statement : LOAD_CANCEL @@ -869,7 +872,7 @@ context.header_str = g_string_new(NULL); context.ternary = g_array_new(FALSE, TRUE, sizeof(Ternary)); /* Read input file */ - FILE *input_file = fopen(argv[ARG_INDEX_IDEFS], "r"); + FILE *input_file = fopen(argv[ARG_INDEX_IDEFS], "rb"); fseek(input_file, 0L, SEEK_END); long input_size = ftell(input_file); context.input_buffer = (char *) calloc(input_size + 1, sizeof(char));
diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index 70bfa64..b942d9e 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c
@@ -1731,6 +1731,15 @@ OUT(c, locp, "}\n"); } +void gen_framecheck(Context *c, YYLTYPE *locp, HexValue *addr, HexValue *ea) +{ + HexValue addr_m = rvalue_materialize(c, locp, addr); + HexValue ea_m = rvalue_materialize(c, locp, ea); + addr_m = gen_rvalue_truncate(c, locp, &addr_m); + ea_m = gen_rvalue_truncate(c, locp, &ea_m); + OUT(c, locp, "gen_framecheck(ctx, ", &addr_m, ", ", &ea_m, ");\n"); +} + void gen_load(Context *c, YYLTYPE *locp, HexValue *width, HexSignedness signedness, HexValue *ea, HexValue *dst) {
diff --git a/target/hexagon/idef-parser/parser-helpers.h b/target/hexagon/idef-parser/parser-helpers.h index 2087d53..d3dfcec 100644 --- a/target/hexagon/idef-parser/parser-helpers.h +++ b/target/hexagon/idef-parser/parser-helpers.h
@@ -295,6 +295,8 @@ void gen_load_cancel(Context *c, YYLTYPE *locp); +void gen_framecheck(Context *c, YYLTYPE *locp, HexValue *addr, HexValue *ea); + void gen_load(Context *c, YYLTYPE *locp, HexValue *size, HexSignedness signedness, HexValue *ea, HexValue *dst);
diff --git a/target/hexagon/imported/encode_pp.def b/target/hexagon/imported/encode_pp.def index 0cd30a5..1c64495 100644 --- a/target/hexagon/imported/encode_pp.def +++ b/target/hexagon/imported/encode_pp.def
@@ -382,15 +382,19 @@ DEF_ENC32(L4_return_tnew_pnt, ICLASS_LD" 011 0 000 sssss PP0010vv ---ddddd") DEF_ENC32(L4_return_fnew_pnt, ICLASS_LD" 011 0 000 sssss PP1010vv ---ddddd") -DEF_ENC32(L2_loadw_locked,ICLASS_LD" 001 0 000 sssss PP000--- 000ddddd") +/** Load Acquire Store Release Encoding **/ - +DEF_ENC32(L4_loadw_phys, ICLASS_LD" 001 0 000 sssss PP1ttttt -00ddddd") +DEF_ENC32(L2_loadw_locked, ICLASS_LD" 001 0 000 sssss PP000--- 000ddddd") +DEF_ENC32(L4_loadd_locked, ICLASS_LD" 001 0 000 sssss PP010--- 000ddddd") DEF_ENC32(L2_loadw_aq, ICLASS_LD" 001 0 000 sssss PP001--- 000ddddd") DEF_ENC32(L4_loadd_aq, ICLASS_LD" 001 0 000 sssss PP011--- 000ddddd") -DEF_ENC32(R6_release_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0011dd") -DEF_ENC32(R6_release_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1011dd") + +DEF_ENC32(S2_storew_locked, ICLASS_ST" 000 01 01sssss PP-ttttt ----00dd") +DEF_ENC32(S4_stored_locked, ICLASS_ST" 000 01 11sssss PP0ttttt ----00dd") + DEF_ENC32(S2_storew_rl_at_vi, ICLASS_ST" 000 01 01sssss PP-ttttt --0010dd") DEF_ENC32(S2_storew_rl_st_vi, ICLASS_ST" 000 01 01sssss PP-ttttt --1010dd") @@ -398,17 +402,15 @@ DEF_ENC32(S4_stored_rl_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0010dd") DEF_ENC32(S4_stored_rl_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1010dd") -DEF_ENC32(L4_loadd_locked,ICLASS_LD" 001 0 000 sssss PP010--- 000ddddd") -DEF_EXT_SPACE(EXTRACTW, ICLASS_LD" 001 0 000 iiiii PP0iiiii -01iiiii") +DEF_ENC32(R6_release_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0011dd") +DEF_ENC32(R6_release_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1011dd") + +DEF_EXT_SPACE(EXTRACTW, ICLASS_LD" 001 0 000 iiiii PP0iiiii 001iiiii") DEF_ENC32(Y2_dcfetchbo, ICLASS_LD" 010 0 000 sssss PP0--iii iiiiiiii") - - - - /*******************************/ /* */ /* */ @@ -488,13 +490,17 @@ /* x bus/cache */ /* x store/cache */ DEF_ENC32(S2_allocframe, ICLASS_ST" 000 01 00xxxxx PP000iii iiiiiiii") -DEF_ENC32(S2_storew_locked,ICLASS_ST" 000 01 01sssss PP-ttttt ----00dd") -DEF_ENC32(S4_stored_locked,ICLASS_ST" 000 01 11sssss PP0ttttt ----00dd") +DEF_ENC32(Y5_l2locka, ICLASS_ST" 000 01 11sssss PP1----- ------dd") DEF_ENC32(Y2_dczeroa, ICLASS_ST" 000 01 10sssss PP0----- --------") DEF_ENC32(Y2_barrier, ICLASS_ST" 100 00 00----- PP------ 000-----") DEF_ENC32(Y2_syncht, ICLASS_ST" 100 00 10----- PP------ --------") +DEF_ENC32(Y2_l2kill, ICLASS_ST" 100 00 01----- PP-000-- --------") +DEF_ENC32(Y5_l2gunlock, ICLASS_ST" 100 00 01----- PP-010-- --------") +DEF_ENC32(Y5_l2gclean, ICLASS_ST" 100 00 01----- PP-100-- --------") +DEF_ENC32(Y5_l2gcleaninv, ICLASS_ST" 100 00 01----- PP-110-- --------") +DEF_ENC32(Y2_l2cleaninvidx,ICLASS_ST" 100 00 11sssss PP------ --------") @@ -502,9 +508,28 @@ DEF_ENC32(Y2_dcinva, ICLASS_ST" 000 00 01sssss PP------ --------") DEF_ENC32(Y2_dccleaninva, ICLASS_ST" 000 00 10sssss PP------ --------") -DEF_ENC32(Y4_l2fetch, ICLASS_ST" 011 00 00sssss PP-ttttt 000-----") +/* Super */ +DEF_ENC32(Y2_dckill, ICLASS_ST" 001 00 00----- PP------ --------") +DEF_ENC32(Y2_dccleanidx, ICLASS_ST" 001 00 01sssss PP------ --------") +DEF_ENC32(Y2_dcinvidx, ICLASS_ST" 001 00 10sssss PP------ --------") +DEF_ENC32(Y2_dccleaninvidx,ICLASS_ST" 001 00 11sssss PP------ --------") + +DEF_ENC32(Y2_dctagw ,ICLASS_ST" 010 00 00sssss PP-ttttt --------") +DEF_ENC32(Y2_dctagr ,ICLASS_ST" 010 00 01sssss PP------ ---ddddd") + +DEF_ENC32(Y4_l2tagw ,ICLASS_ST" 010 00 10sssss PP0ttttt --------") +DEF_ENC32(Y4_l2tagr ,ICLASS_ST" 010 00 11sssss PP------ ---ddddd") + +DEF_ENC32(Y4_l2fetch, ICLASS_ST" 011 00 00sssss PP-ttttt 000-----") +DEF_ENC32(Y5_l2cleanidx, ICLASS_ST" 011 00 01sssss PP------ --------") +DEF_ENC32(Y5_l2invidx, ICLASS_ST" 011 00 10sssss PP------ --------") +DEF_ENC32(Y5_l2unlocka, ICLASS_ST" 011 00 11sssss PP------ --------") DEF_ENC32(Y5_l2fetch, ICLASS_ST" 011 01 00sssss PP-ttttt --------") +DEF_ENC32(Y6_l2gcleanpa, ICLASS_ST" 011 01 01----- PP-ttttt --------") +DEF_ENC32(Y6_l2gcleaninvpa,ICLASS_ST" 011 01 10----- PP-ttttt --------") + + /*******************************/ /* */ /* */ @@ -547,13 +572,23 @@ DEF_FIELDROW_DESC32(ICLASS_J" 0100 -------- PP------ --------","[#4] (#u8) ") DEF_ENC32(J2_trap0, ICLASS_J" 0100 00------ PP-iiiii ---iii--") -DEF_ENC32(J2_pause, ICLASS_J" 0100 01------ PP-iiiii ---iii--") +DEF_ENC32(J2_trap1, ICLASS_J" 0100 10-xxxxx PP-iiiii ---iii--") +DEF_ENC32(J2_pause, ICLASS_J" 0100 01----ii PP-iiiii ---iii--") + +DEF_FIELDROW_DESC32(ICLASS_J" 0101 -------- PP------ --------","[#5] Rd=(Rs) ") +DEF_ENC32(Y2_icdatar, ICLASS_J" 0101 101sssss PP------ ---ddddd") +DEF_ENC32(Y2_ictagr, ICLASS_J" 0101 111sssss PP------ ---ddddd") +DEF_ENC32(Y2_ictagw, ICLASS_J" 0101 110sssss PP0ttttt --------") +DEF_ENC32(Y2_icdataw, ICLASS_J" 0101 110sssss PP1ttttt --------") DEF_FIELDROW_DESC32(ICLASS_J" 0110 -------- PP------ --------","[#6] icop(Rs) ") DEF_ENC32(Y2_icinva, ICLASS_J" 0110 110sssss PP000--- --------") +DEF_ENC32(Y2_icinvidx, ICLASS_J" 0110 110sssss PP001--- --------") +DEF_ENC32(Y2_ickill, ICLASS_J" 0110 110----- PP010--- --------") DEF_FIELDROW_DESC32(ICLASS_J" 0111 -------- PP------ --------","[#7] () ") DEF_ENC32(Y2_isync, ICLASS_J" 0111 11000000 PP0---00 00000010") +DEF_ENC32(J2_rte, ICLASS_J" 0111 111----- PP00---- 000-----") /* JUMP */ DEF_FIELDROW_DESC32(ICLASS_J" 100- -------- PP------ --------","[#8,9] PC=(#r22)") @@ -738,12 +773,19 @@ DEF_FIELDROW_DESC32( ICLASS_CR" 0010 -------- PP------ --------","[#2] Cd=Rs ") DEF_ENC32(A2_tfrrcr, ICLASS_CR" 0010 001sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgrcr, ICLASS_CR" 0010 000sssss PP------ ---ddddd") +DEF_ENC32(Y4_trace, ICLASS_CR" 0010 010sssss PP------ 000-----") +DEF_ENC32(Y6_diag, ICLASS_CR" 0010 010sssss PP------ 001-----") +DEF_ENC32(Y6_diag0, ICLASS_CR" 0010 010sssss PP-ttttt 010-----") +DEF_ENC32(Y6_diag1, ICLASS_CR" 0010 010sssss PP-ttttt 011-----") DEF_FIELDROW_DESC32( ICLASS_CR" 0011 -------- PP------ --------","[#3] Cdd=Rss ") DEF_ENC32(A4_tfrpcp, ICLASS_CR" 0011 001sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgpcp, ICLASS_CR" 0011 000sssss PP------ ---ddddd") DEF_FIELDROW_DESC32( ICLASS_CR" 1000 -------- PP------ --------","[#8] Rdd=Css ") DEF_ENC32(A4_tfrcpp, ICLASS_CR" 1000 000sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgcpp, ICLASS_CR" 1000 001sssss PP------ ---ddddd") DEF_FIELDROW_DESC32( ICLASS_CR" 1001 -------- PP------ --------","[#9] (#r8,#U10)") DEF_ENC32(J2_ploop1si, ICLASS_CR" 1001 101IIIII PP-iiiii IIIii-II") @@ -754,6 +796,7 @@ DEF_FIELDROW_DESC32( ICLASS_CR" 1010 -------- PP------ --------","[#10] Rd=Cs ") DEF_ENC32(A2_tfrcrr, ICLASS_CR" 1010 000sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgcrr, ICLASS_CR" 1010 001sssss PP------ ---ddddd") DEF_ENC32(C4_addipc, ICLASS_CR" 1010 01001001 PP-iiiii i--ddddd") @@ -781,6 +824,64 @@ +/* Supervisor CR ops */ +/* Interrupts */ +DEF_FIELDROW_DESC32( ICLASS_CR" 0100 -------- PP------ --------","[#4] (Rs,Pt)") +DEF_ENC32(Y2_swi, ICLASS_CR" 0100 000sssss PP------ 000-----") +DEF_ENC32(Y2_cswi, ICLASS_CR" 0100 000sssss PP------ 001-----") +DEF_ENC32(Y2_iassignw, ICLASS_CR" 0100 000sssss PP------ 010-----") +DEF_ENC32(Y2_ciad, ICLASS_CR" 0100 000sssss PP------ 011-----") +DEF_ENC32(Y2_setimask, ICLASS_CR" 0100 100sssss PP----tt 000-----") +DEF_ENC32(Y2_setprio, ICLASS_CR" 0100 100sssss PP----tt 001-----") +DEF_ENC32(Y4_siad, ICLASS_CR" 0100 100sssss PP------ 011-----") + +DEF_ENC32(Y2_wait, ICLASS_CR" 0100 010sssss PP------ 000-----") +DEF_ENC32(Y2_resume, ICLASS_CR" 0100 010sssss PP------ 001-----") +DEF_ENC32(Y2_stop, ICLASS_CR" 0100 011sssss PP------ 000-----") +DEF_ENC32(Y2_start, ICLASS_CR" 0100 011sssss PP------ 001-----") +DEF_ENC32(Y4_nmi, ICLASS_CR" 0100 011sssss PP------ 010-----") + +DEF_FIELDROW_DESC32( ICLASS_CR" 0101 -------- PP------ --------","[#5] Rx ") +DEF_ENC32(Y2_crswap0, ICLASS_CR" 0101 000xxxxx PP------ --------") +DEF_ENC32(Y4_crswap1, ICLASS_CR" 0101 001xxxxx PP------ --------") + +DEF_FIELDROW_DESC32( ICLASS_CR" 0110 -------- PP------ --------","[#6] Rd=(Rs)") +DEF_ENC32(Y2_getimask, ICLASS_CR" 0110 000sssss PP------ ---ddddd") +DEF_ENC32(Y2_iassignr, ICLASS_CR" 0110 011sssss PP------ ---ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 0111 -------- PP------ --------","[#7] cr=Rs ") +DEF_ENC32(Y2_tfrsrcr, ICLASS_CR" 0111 00-sssss PP------ -ddddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1100 -------- PP------ --------","[#12] ") +DEF_ENC32(Y2_break, ICLASS_CR" 1100 001----- PP------ 000-----") +DEF_ENC32(Y2_tlblock, ICLASS_CR" 1100 001----- PP------ 001-----") +DEF_ENC32(Y2_tlbunlock,ICLASS_CR" 1100 001----- PP------ 010-----") +DEF_ENC32(Y2_k0lock, ICLASS_CR" 1100 001----- PP------ 011-----") +DEF_ENC32(Y2_k0unlock, ICLASS_CR" 1100 001----- PP------ 100-----") +DEF_ENC32(Y2_tlbp, ICLASS_CR" 1100 100sssss PP------ ---ddddd") +DEF_ENC32(Y5_tlboc, ICLASS_CR" 1100 111sssss PP------ ---ddddd") +DEF_ENC32(Y5_tlbasidi, ICLASS_CR" 1100 101sssss PP------ --------") +DEF_ENC32(Y2_tlbr, ICLASS_CR" 1100 010sssss PP------ ---ddddd") +DEF_ENC32(Y2_tlbw, ICLASS_CR" 1100 000sssss PP0ttttt --------") +DEF_ENC32(Y5_ctlbw, ICLASS_CR" 1100 110sssss PP0ttttt ---ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1101 -------- PP------ --------","[#13] Rxx ") +DEF_ENC32(Y4_crswap10, ICLASS_CR" 1101 10-xxxxx PP------ ---00000") +DEF_ENC32(Y4_tfrspcp, ICLASS_CR" 1101 00-sssss PP------ -ddddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1110 -------- PP------ --------","[#14] Rd=cr ") +DEF_ENC32(Y2_tfrscrr, ICLASS_CR" 1110 1sssssss PP------ ---ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1111 -------- PP------ --------","[#15] Rdd=Sss ") +DEF_ENC32(Y4_tfrscpp, ICLASS_CR" 1111 0sssssss PP------ ---ddddd") + + + + + + + + /*******************************/ /* */ /* */
diff --git a/target/hexagon/imported/ldst.idef b/target/hexagon/imported/ldst.idef index 5319817..4e1e5d5 100644 --- a/target/hexagon/imported/ldst.idef +++ b/target/hexagon/imported/ldst.idef
@@ -203,6 +203,9 @@ Q6INSN(L4_loadd_locked,"Rdd32=memd_locked(Rs32)", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_LOAD,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK), "Load double with lock", { fEA_REG(RsV); fLOAD_LOCKED(1,8,u,EA,RddV) }) +Q6INSN(L4_loadw_phys,"Rd32=memw_phys(Rs32,Rt32)", ATTRIBS(A_REGWRSIZE_4B,A_PRIV,A_RESTRICT_SLOT0ONLY,A_NOTE_PRIV,A_MEMSIZE_4B,A_LOAD,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Load word from physical address", +{ fLOAD_PHYS(1,4,u,RsV,RtV,RdV); }) + Q6INSN(S4_stored_locked,"memd_locked(Rs32,Pd4)=Rtt32", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_LATEPRED,A_NOTE_LATEPRED), "Store word with lock", { fEA_REG(RsV); fSTORE_LOCKED(1,8,EA,RttV,PdV) })
diff --git a/target/hexagon/imported/macros.def b/target/hexagon/imported/macros.def old mode 100755 new mode 100644 index 4bbcfdd..13eb13c --- a/target/hexagon/imported/macros.def +++ b/target/hexagon/imported/macros.def
@@ -354,6 +354,12 @@ ) DEF_MACRO( + fREAD_SSR, /* read SSR register */ + (READ_RREG(REG_SSR)), /* behavior */ + () +) + +DEF_MACRO( fWRITE_LR, /* write lr */ WRITE_RREG(REG_LR,A), /* behavior */ (A_IMPLICIT_WRITES_LR) @@ -372,12 +378,36 @@ ) DEF_MACRO( + fWRITE_GOSP, /* write gosp */ + WRITE_RREG(REG_GOSP,A), /* behavior */ + (A_IMPLICIT_WRITES_GOSP) +) + +DEF_MACRO( fREAD_SP, /* read stack pointer */ (READ_RREG(REG_SP)), /* behavior */ () ) DEF_MACRO( + fREAD_GOSP, /* read guest other stack pointer */ + (READ_RREG(REG_GOSP)), /* behavior */ + () +) + +DEF_MACRO( + fREAD_GELR, /* read guest other stack pointer */ + (READ_RREG(REG_GELR)), /* behavior */ + () +) + +DEF_MACRO( + fREAD_GEVB, /* read guest other stack pointer */ + (READ_RREG(REG_GEVB)), /* behavior */ + () +) + +DEF_MACRO( fREAD_CSREG, /* read CS register */ (READ_RREG(REG_CSA+N)), /* behavior */ () @@ -570,6 +600,11 @@ WRITE_PREG(3,VAL), /* behavior */ (A_IMPLICIT_WRITES_P3) ) +DEF_MACRO( + fWRITE_P3_LATE, /* write Predicate 0 */ + {WRITE_PREG(3,VAL); fHIDE(MARK_LATE_PRED_WRITE(3))} , /* behavior */ + (A_IMPLICIT_WRITES_P3,A_RESTRICT_LATEPRED) +) DEF_MACRO( fPART1, /* write Predicate 0 */ @@ -660,6 +695,7 @@ ((size8s_t)((size2s_t)(A))), /* optional attributes */ ) + DEF_MACRO( fCAST2_8u, /* macro name */ ((size8u_t)((size2u_t)(A))), @@ -1532,18 +1568,137 @@ /* OS interface and stop/wait */ /********************************************/ +DEF_MACRO(RUNNABLE_THREADS_MAX, + /* */, + () +) + +DEF_MACRO(THREAD_IS_ON, + ((PROC->arch_proc_options->thread_enable_mask>>TNUM) & 0x1), + () +) + +DEF_MACRO(THREAD_EN_MASK, + ((PROC->arch_proc_options->thread_enable_mask)), + () +) + + + +DEF_MACRO(READ_IMASK, + /* */, + () +) +DEF_MACRO(WRITE_IMASK, + /* */, + (A_IMPLICIT_WRITES_IMASK_ANYTHREAD) +) + + +DEF_MACRO(WRITE_PRIO, + /* */, + (A_IMPLICIT_WRITES_STID_PRIO_ANYTHREAD) +) + + +DEF_MACRO(DO_IASSIGNW, + /* */, + (A_IMPLICIT_WRITES_IMASK_ANYTHREAD) +) + + + + +DEF_MACRO(fDO_NMI, + /* */, +) + +DEF_MACRO(fDO_TRACE, + /* */, +) + +DEF_MACRO(DO_IASSIGNR, + /* */, + () +) + +DEF_MACRO(DO_SWI, + /* */, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(DO_CSWI, + LOG_GLOBAL_REG_WRITE(REG_IPEND,GLOBAL_REG_READ(REG_IPEND) & ~((REG) & GLOBAL_REG_READ(REG_IEL)));, + () +) + +DEF_MACRO(DO_CIAD, + sys_ciad(thread,VAL); LOG_GLOBAL_REG_WRITE(REG_IAD,GLOBAL_REG_READ(REG_IAD) & ~(VAL));, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(DO_SIAD, + sys_siad(thread,VAL); LOG_GLOBAL_REG_WRITE(REG_IAD,GLOBAL_REG_READ(REG_IAD) | (VAL));, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(fBREAK, + /* */, + () +) + DEF_MACRO(fPAUSE, {sys_pause(thread, insn->slot, IMM);}, () ) + DEF_MACRO(fTRAP, - warn("Trap NPC=%x ",fREAD_NPC()); - warn("Trap exception, PCYCLE=%lld TYPE=%d NPC=%x IMM=0x%x",thread->processor_ptr->pstats[pcycles],TRAPTYPE,fREAD_NPC(),IMM); - register_trap_exception(thread,fREAD_NPC(),TRAPTYPE,IMM);, + /* */, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(fCLEAR_RTE_EX, + /* */, + () +) + +DEF_MACRO(fTLB_LOCK_AVAILABLE, + (fREAD_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_TLBLOCK) == 0), () ) +DEF_MACRO(fK0_LOCK_AVAILABLE, + (fREAD_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_K0LOCK) == 0), + () +) + +DEF_MACRO(fSET_TLB_LOCK, + { + if (fTLB_LOCK_AVAILABLE()) { + fLOG_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_TLBLOCK,1); + } else { + sys_waiting_for_tlb_lock(thread); + } + }, + () +) + +DEF_MACRO(fSET_K0_LOCK, + /* */, + () +) + +DEF_MACRO(fCLEAR_TLB_LOCK, + /* */, + () +) + +DEF_MACRO(fCLEAR_K0_LOCK, + /* */, + () +) + DEF_MACRO(fALIGN_REG_FIELD_VALUE, ((VAL)<<reg_field_info[FIELD].offset), /* */ @@ -1554,6 +1709,24 @@ /* */ ) +DEF_MACRO(fLOG_REG_FIELD, + LOG_MASKED_REG_WRITE(thread,REG_##REG, + fALIGN_REG_FIELD_VALUE(FIELD,VAL), + fGET_REG_FIELD_MASK(FIELD)), + () +) + +DEF_MACRO(fWRITE_GLOBAL_REG_FIELD, + /* */, +) + +DEF_MACRO(fLOG_GLOBAL_REG_FIELD, + LOG_MASKED_GLOBAL_REG_WRITE(REG_##REG, + fALIGN_REG_FIELD_VALUE(FIELD,VAL), + fGET_REG_FIELD_MASK(FIELD)), + () +) + DEF_MACRO(fREAD_REG_FIELD, fEXTRACTU_BITS(thread->Regs[REG_##REG], reg_field_info[FIELD].width, @@ -1561,6 +1734,11 @@ /* ATTRIBS */ ) +DEF_MACRO(fREAD_GLOBAL_REG_FIELD, + /* */, + /* ATTRIBS */ +) + DEF_MACRO(fGET_FIELD, fEXTRACTU_BITS(VAL, reg_field_info[FIELD].width, @@ -1576,6 +1754,174 @@ /* ATTRIBS */ ) +DEF_MACRO(fSET_RUN_MODE_NOW, + /* */, +) + +DEF_MACRO(fIN_DEBUG_MODE, + (thread->debug_mode || (fREAD_GLOBAL_REG_FIELD(ISDBST,ISDBST_DEBUGMODE) & 1<<TNUM)), + () +) +DEF_MACRO(fIN_DEBUG_MODE_NO_ISDB, + (thread->debug_mode), + () +) + + +DEF_MACRO(fIN_DEBUG_MODE_WARN, + { + if (fREAD_GLOBAL_REG_FIELD(ISDBST,ISDBST_DEBUGMODE) & 1<<TNUM) + warn("In ISDB debug mode, but TB told me to step normally"); + }, + () +) + +DEF_MACRO(fCLEAR_RUN_MODE, + {fLOG_GLOBAL_REG_FIELD(MODECTL,MODECTL_E, + fREAD_GLOBAL_REG_FIELD(MODECTL,MODECTL_E) & ~(1<<(TNUM)))}, + /* NOTHING */ +) + +DEF_MACRO(fCLEAR_RUN_MODE_NOW, + /* */, + /* NOTHING */ +) + +DEF_MACRO(fGET_RUN_MODE, + /* */, +) + +DEF_MACRO(fSET_WAIT_MODE, + {fLOG_GLOBAL_REG_FIELD(MODECTL,MODECTL_W, + fREAD_GLOBAL_REG_FIELD(MODECTL,MODECTL_W) | 1<<(TNUM))}, + /* NOTHING */ +) + +DEF_MACRO(fCLEAR_WAIT_MODE, + /* */, +) + +DEF_MACRO(fGET_WAIT_MODE, + /* */, +) + + +DEF_MACRO(fRESET_THREAD, + register_reset_interrupt(T,NUM), +) + +DEF_MACRO(fREAD_CURRENT_EVB, + (GLOBAL_REG_READ(REG_EVB)), + /* nothing */ +) + +DEF_MACRO(fREAD_ELR, + READ_RREG(REG_ELR), + () +) + +DEF_MACRO(fPOW2_HELP_ROUNDUP, + ((VAL) | ((VAL) >> 1) | ((VAL) >> 2) | ((VAL) >> 4) | ((VAL) >> 8) | ((VAL) >> 16)), + () +) + +DEF_MACRO(fPOW2_ROUNDUP, + fPOW2_HELP_ROUNDUP((VAL)-1)+1, + () +) + +DEF_MACRO(fTLB_IDXMASK, + /* */, + () +) + +DEF_MACRO(fTLB_NONPOW2WRAP, + /* */, + /* ATTRIBS */ +) + +DEF_MACRO(fTLBW, + /* */, + /* ATTRIBS */ +) + +DEF_MACRO(fTLB_ENTRY_OVERLAP, + fHIDE( (sys_check_overlap(thread,VALUE)!=-2) ), + /* ATTRIBS */ +) + +DEF_MACRO(fTLB_ENTRY_OVERLAP_IDX, + fHIDE(sys_check_overlap(thread,VALUE)), + /* ATTRIBS */ +) + + +DEF_MACRO(fTLBR, + TLB_REG_READ(fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX))), + /* ATTRIBS */ +) + +DEF_MACRO(fTLBP, + tlb_lookup(thread,((TLBHI)>>12),((TLBHI)<<12),1), + /* attribs */ +) + + + +DEF_MACRO(READ_SGP0, + READ_RREG(REG_SGP), + () +) + +DEF_MACRO(READ_SGP1, + READ_RREG(REG_SGP+1), + () +) + +DEF_MACRO(READ_SGP10, + READ_RREG_PAIR(REG_SGP), + () +) + +DEF_MACRO(READ_UGP, + READ_RREG(REG_UGP), +) + +DEF_MACRO(WRITE_SGP0, + WRITE_RREG(REG_SGP,VAL), + (A_IMPLICIT_WRITES_SGP0) +) + +DEF_MACRO(WRITE_SGP1, + WRITE_RREG(REG_SGP+1,VAL), + (A_IMPLICIT_WRITES_SGP1) +) + +DEF_MACRO(WRITE_SGP10, + WRITE_RREG_PAIR(REG_SGP,VAL), + (A_IMPLICIT_WRITES_SGP0,A_IMPLICIT_WRITES_SGP1) +) + +DEF_MACRO(WRITE_UGP, + WRITE_RREG(REG_UGP,VAL), +) + +DEF_MACRO(fSTART, + /* */, + () +) + +DEF_MACRO(fRESUME, + fLOG_GLOBAL_REG_FIELD(MODECTL,MODECTL_W, + fREAD_GLOBAL_REG_FIELD(MODECTL,MODECTL_W) & (~(REG))), + () +) + +DEF_MACRO(fGET_TNUM, + thread->threadId, + () +) + /********************************************/ /* Cache Management */ /********************************************/ @@ -1602,19 +1948,49 @@ ) +DEF_MACRO(fICFETCH, + , + () +) + DEF_MACRO(fDCFETCH, sys_dcfetch(thread, (REG), insn->slot), (A_MEMLIKE) ) DEF_MACRO(fICINVA, - { - arch_internal_flush(thread->processor_ptr, 0, 0xffffffff); - sys_icinva(thread, (REG),insn->slot); - }, + /* */, (A_ICINVA) ) +DEF_MACRO(fDCTAGR, + ({DST=sys_dctagr(thread, INDEX, insn->slot,DSTREGNO);})/* FIXME */, + () +) + +DEF_MACRO(fDCTAGW, + (sys_dctagw(thread, INDEX, PART2, insn->slot)), + () +) +DEF_MACRO(fICTAGR, + ({DST=sys_ictagr(thread, INDEX, insn->slot,REGNO);}), + () +) + +DEF_MACRO(fICDATAR, + ({DST=sys_icdatar(thread, INDEX, insn->slot);}), + () +) + +DEF_MACRO(fICTAGW, + (sys_ictagw(thread, INDEX, PART2, insn->slot)), + () +) +DEF_MACRO(fICDATAW, + ({ fHIDE(); }), + () +) + DEF_MACRO(fL2FETCH, sys_l2fetch(thread, ADDR,HEIGHT,WIDTH,STRIDE,FLAGS, insn->slot), (A_MEMLIKE,A_L2FETCH) @@ -1635,6 +2011,12 @@ (A_MEMLIKE) ) +DEF_MACRO(fDCINVA, + sys_dcinva(thread, (REG)), + (A_MEMLIKE) +) + + DEF_MACRO(fCHECKFORPRIV, {sys_check_privs(thread); if (EXCEPTION_DETECTED) return; }, () @@ -1645,6 +2027,16 @@ () ) +DEF_MACRO(fTAKEN_INTERRUPT_EDGECLEAR, + { proc->global_regs[REG_IPEND] &= ~(INT_NUMTOMASK(intnum) & proc->global_regs[REG_IEL]); }, + () +) + +DEF_MACRO(fSET_IAD, + /* */, + () +) + DEF_MACRO(fBRANCH_SPECULATE_STALL, { sys_speculate_branch_stall(thread, insn->slot, JUMP_COND(JUMP_PRED_SET), @@ -1664,3 +2056,79 @@ , () ) + +DEF_MACRO(fIN_MONITOR_MODE, + sys_in_monitor_mode(thread), + () +) + +DEF_MACRO(fIN_USER_MODE, + sys_in_user_mode(thread), + () +) + +DEF_MACRO(fIN_GUEST_MODE, + sys_in_guest_mode(thread), + () +) + +DEF_MACRO(fGRE_ENABLED, + fREAD_REG_FIELD(CCR,CCR_GRE), + () +) + +DEF_MACRO(fGTE_ENABLED, + fREAD_REG_FIELD(CCR,CCR_GRE), + () +) + +DEF_MACRO(fTRAP1_VIRTINSN, + ((fIN_GUEST_MODE()) + && (fGRE_ENABLED()) + && ( ((IMM) == 1) + || ((IMM) == 3) + || ((IMM) == 4) + || ((IMM) == 6))), + () +) + +DEF_MACRO(fVIRTINSN_RTE, + do { + thread->trap1_info = TRAP1_VIRTINSN_RTE; + fLOG_REG_FIELD(SSR,SSR_SS,fREAD_REG_FIELD(GSR,GSR_SS)); + fLOG_REG_FIELD(CCR,CCR_GIE,fREAD_REG_FIELD(GSR,GSR_IE)); + fLOG_REG_FIELD(SSR,SSR_GM,!fREAD_REG_FIELD(GSR,GSR_UM)); + fBRANCH((fREAD_GELR() & -4),COF_TYPE_RTE); + fINTERNAL_CLEAR_SAMEPAGE(); + } while (0), + (A_IMPLICIT_WRITES_CCR,A_IMPLICIT_WRITES_SSR) +) + +DEF_MACRO(fVIRTINSN_SETIE, + do { + fLOG_REG_FIELD(CCR,CCR_GIE,(REG) & 1); + REG = fREAD_REG_FIELD(CCR,CCR_GIE); + thread->trap1_info = TRAP1_VIRTINSN_SETIE; + } while (0), + (A_IMPLICIT_WRITES_CCR) +) + +DEF_MACRO(fVIRTINSN_GETIE, + { + thread->trap1_info = TRAP1_VIRTINSN_GETIE; + REG = fREAD_REG_FIELD(CCR,CCR_GIE); + }, + () +) + +DEF_MACRO(fVIRTINSN_SPSWAP, + do { + if (fREAD_REG_FIELD(GSR,GSR_UM)) { + size4u_t TEMP = REG; + REG = fREAD_GOSP(); + fWRITE_GOSP(TEMP); + thread->trap1_info = TRAP1_VIRTINSN_SPSWAP; + } + } while (0), + (A_IMPLICIT_WRITES_GOSP) +)
diff --git a/target/hexagon/imported/system.idef b/target/hexagon/imported/system.idef index 7c6568e..df4527a 100644 --- a/target/hexagon/imported/system.idef +++ b/target/hexagon/imported/system.idef
@@ -25,31 +25,230 @@ /* User->OS interface */ /********************************************/ -Q6INSN(J2_trap0,"trap0(#u8)",ATTRIBS(A_COF), +Q6INSN(J2_trap0,"trap0(#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Trap to Operating System", fTRAP(0,uiV); ) -Q6INSN(J2_pause,"pause(#u8)",ATTRIBS(A_COF), +Q6INSN(J2_trap1,"trap1(Rx32,#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"Trap to Operating System", + /* + * Note: if RxV is not written, we get the same as the input. + * Since trap1 is SOLO, this means the register will effectively not be updated + */ + if (!fTRAP1_VIRTINSN(uiV)) { + fTRAP(1,uiV); + } else if (uiV == 1) { + fVIRTINSN_RTE(uiV,RxV); + } else if (uiV == 3) { + fVIRTINSN_SETIE(uiV,RxV); + } else if (uiV == 4) { + fVIRTINSN_GETIE(uiV,RxV); + } else if (uiV == 6) { + fVIRTINSN_SPSWAP(uiV,RxV); + }) + +Q6INSN(J2_pause,"pause(#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Enter low-power state for #u8 cycles",{fPAUSE(uiV);}) -Q6INSN(Y2_icinva,"icinva(Rs32)",ATTRIBS(A_ICOP,A_ICFLUSHOP),"Instruction Cache Invalidate Address",{fEA_REG(RsV); fICINVA(EA);}) - -Q6INSN(Y2_isync,"isync",ATTRIBS(),"Memory Synchronization",{fISYNC();}) -Q6INSN(Y2_barrier,"barrier",ATTRIBS(A_RESTRICT_SLOT0ONLY),"Memory Barrier",{fBARRIER();}) -Q6INSN(Y2_syncht,"syncht",ATTRIBS(A_RESTRICT_SLOT0ONLY),"Memory Synchronization",{fSYNCH();}) +Q6INSN(J2_rte, "rte", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NO_TIMING_LOG), +"Return from Exception", +{ +fHIDE(if((thread->timing_on) && (thread->status & EXEC_STATUS_REPLAY)) { return; }) +fHIDE(CALLBACK(thread->processor_ptr->options->rte_callback, + thread->system_ptr,thread->processor_ptr, + thread->threadId,0);) +fCLEAR_RTE_EX(); +fBRANCH(fREAD_ELR(),COF_TYPE_RTE);}) -Q6INSN(Y2_dcfetchbo,"dcfetch(Rs32+#u11:3)",ATTRIBS(A_RESTRICT_PREFERSLOT0,A_DCFETCH),"Data Cache Prefetch",{fEA_RI(RsV,uiV); fDCFETCH(EA);}) +/********************************************/ +/* Interrupt Management */ +/********************************************/ + +Q6INSN(Y2_swi,"swi(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Software Interrupt",{DO_SWI(RsV);}) +Q6INSN(Y2_cswi,"cswi(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Cancel Software Interrupt",{DO_CSWI(RsV);}) +Q6INSN(Y2_ciad,"ciad(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Re-enable interrupt in IAD",{DO_CIAD(RsV);}) +Q6INSN(Y4_siad,"siad(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Disable interrupt in IAD",{DO_SIAD(RsV);}) +Q6INSN(Y2_iassignr,"Rd32=iassignr(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Read interrupt to thread assignments",{DO_IASSIGNR(RsV,RdV);}) +Q6INSN(Y2_iassignw,"iassignw(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Write interrupt to thread assignments",{DO_IASSIGNW(RsV);}) -Q6INSN(Y2_dczeroa,"dczeroa(Rs32)",ATTRIBS(A_STORE,A_RESTRICT_SLOT0ONLY,A_DCZEROA),"Zero an aligned 32-byte cacheline",{fEA_REG(RsV); fDCZEROA(EA);}) -Q6INSN(Y2_dccleana,"dccleana(Rs32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_DCFLUSHOP),"Data Cache Clean Address",{fEA_REG(RsV); fDCCLEANA(EA);}) -Q6INSN(Y2_dccleaninva,"dccleaninva(Rs32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_DCFLUSHOP),"Data Cache Clean and Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) -Q6INSN(Y2_dcinva,"dcinva(Rs32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_DCFLUSHOP),"Data Cache Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) +Q6INSN(Y2_getimask,"Rd32=getimask(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Read imask register of another thread", +{RdV = READ_IMASK(RsV & thread->processor_ptr->thread_system_mask); }) + +Q6INSN(Y2_setimask,"setimask(Pt4,Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Change imask register of another thread", +{fPREDUSE_TIMING();WRITE_IMASK(PtV & thread->processor_ptr->thread_system_mask,RsV); }) -Q6INSN(Y4_l2fetch,"l2fetch(Rs32,Rt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY),"L2 Cache Prefetch", + +/********************************************/ +/* TLB management */ +/********************************************/ + +Q6INSN(Y2_tlbw,"tlbw(Rss32,Rt32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"Write TLB entry", {fTLBW(RtV,RssV);}) + +Q6INSN(Y5_ctlbw,"Rd32=ctlbw(Rss32,Rt32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"Conditional Write TLB entry", +{ + if (fTLB_ENTRY_OVERLAP( (1LL<<63) | RssV )) { + RdV=fTLB_ENTRY_OVERLAP_IDX( (1LL<<63) | RssV); + } else { + fTLBW(RtV,RssV); + RdV=0x80000000; + } +}) + +Q6INSN(Y5_tlboc,"Rd32=tlboc(Rss32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"TLB overlap check", +{ + if (fTLB_ENTRY_OVERLAP( (1LL<<63) | RssV )) { + RdV=fTLB_ENTRY_OVERLAP_IDX( (1LL<<63) | RssV); + } else { + RdV=0x80000000; + } +}) + +Q6INSN(Y2_tlbr,"Rdd32=tlbr(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Read TLB entry", +{RddV = fTLBR(RsV);}) + +Q6INSN(Y2_tlbp,"Rd32=tlbp(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Probe TLB", {RdV=fTLBP(RsV);}) + +Q6INSN(Y5_tlbasidi,"tlbinvasid(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Invalidate ASID", +{ + fHIDE(int i;) + fHIDE(unsigned int NUM_TLB_ENTRIES = NUM_TLB_REGS(thread->processor_ptr);) + for (i = 0; i < NUM_TLB_ENTRIES; i++) { + if ((fGET_FIELD(fTLBR(i),PTE_G) == 0) && + (fGET_FIELD(fTLBR(i),PTE_ASID) == fEXTRACTU_RANGE(RsV,26,20))) { + fTLBW(i,fTLBR(i) & ~(1ULL << 63)); + } + } +}) + +Q6INSN(Y2_tlblock,"tlblock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG), "Lock TLB", +{fSET_TLB_LOCK();}) + +Q6INSN(Y2_tlbunlock,"tlbunlock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Unlock TLB", +{fCLEAR_TLB_LOCK();}) + +Q6INSN(Y2_k0lock,"k0lock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG), "Lock K0", +{fSET_K0_LOCK();}) + +Q6INSN(Y2_k0unlock,"k0unlock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Unlock K0", +{fCLEAR_K0_LOCK();}) + +/********************************************/ +/* Supervisor Reg Management */ +/********************************************/ + +Q6INSN(Y2_crswap0,"crswap(Rx32,sgp0)",ATTRIBS(A_PRIV,A_NOTE_PRIV), "Swap system general pointer 0 with GPR", +{fHIDE(size4s_t tmp;) tmp = RxV; RxV = READ_SGP0(); WRITE_SGP0(tmp);}) +Q6INSN(Y4_crswap1,"crswap(Rx32,sgp1)",ATTRIBS(A_PRIV,A_NOTE_PRIV), "Swap system general pointer 1 with GPR", +{fHIDE(size4s_t tmp;) tmp = RxV; RxV = READ_SGP1(); WRITE_SGP1(tmp);}) + +Q6INSN(Y4_crswap10,"crswap(Rxx32,sgp1:0)",ATTRIBS(A_PRIV,A_NOTE_PRIV), "Swap system general purpose 0/1 with GPR Pair", +{fHIDE(size8s_t tmp;) tmp = RxxV; RxxV=READ_SGP10(); WRITE_SGP10(tmp);}) + +Q6INSN(Y2_tfrscrr,"Rd32=Ss128",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer Supervisor Reg to GPR", {RdV=SsV;}) +Q6INSN(Y2_tfrsrcr,"Sd128=Rs32",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer GPR to Supervisor Reg", {SdV=RsV;}) +Q6INSN(Y4_tfrscpp,"Rdd32=Sss128",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer Supervisor Reg to GPR", {RddV=SssV;}) +Q6INSN(Y4_tfrspcp,"Sdd128=Rss32",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer GPR to Supervisor Reg", {SddV=RssV;}) + +Q6INSN(G4_tfrgcrr,"Rd32=Gs32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer Guest Reg to GPR", {RdV=GsV;}) +Q6INSN(G4_tfrgrcr,"Gd32=Rs32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer GPR to Guest Reg", {GdV=RsV;}) +Q6INSN(G4_tfrgcpp,"Rdd32=Gss32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer Guest Reg to GPR", {RddV=GssV;}) +Q6INSN(G4_tfrgpcp,"Gdd32=Rss32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer GPR to Guest Reg", {GddV=RssV;}) + + + +Q6INSN(Y2_setprio,"setprio(Pt4,Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Change TID Prio of another thread", +{fPREDUSE_TIMING();WRITE_PRIO(PtV & thread->processor_ptr->thread_system_mask,RsV); }) + + + + +/********************************************/ +/* Power Management / Thread on/off */ +/********************************************/ +Q6INSN(Y6_diag,"diag(Rs32)",ATTRIBS(),"Send value to Diag trace module",{ +}) +Q6INSN(Y6_diag0,"diag0(Rss32,Rtt32)",ATTRIBS(),"Send values of two register to DIAG Trace. Set X=0",{ +}) +Q6INSN(Y6_diag1,"diag1(Rss32,Rtt32)",ATTRIBS(),"Send values of two register to DIAG Trace. Set X=1",{ +}) + + +Q6INSN(Y4_trace,"trace(Rs32)",ATTRIBS(A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Send value to ETM trace",{ + fDO_TRACE(RsV); +}) + +Q6INSN(Y2_stop,"stop(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Stop thread(s)",{ + fHIDE(RsV=RsV;) + if (!fIN_DEBUG_MODE_NO_ISDB(fGET_TNUM())) fCLEAR_RUN_MODE(fGET_TNUM()); +}) + +Q6INSN(Y4_nmi,"nmi(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG),"Raise NMI on thread(s)",{ + fDO_NMI(RsV); +}) + +Q6INSN(Y2_start,"start(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Start thread(s)",fSTART(RsV);) + +Q6INSN(Y2_wait,"wait(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG),"Make thread(s) wait",{ + fHIDE(RsV=RsV;) + if (!fIN_DEBUG_MODE(fGET_TNUM())) fSET_WAIT_MODE(fGET_TNUM()); + fIN_DEBUG_MODE_WARN(fGET_TNUM()); +}) + +Q6INSN(Y2_resume,"resume(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Make thread(s) stop waiting",fRESUME(RsV);) + +Q6INSN(Y2_break,"brkpt",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Breakpoint",{fBREAK();}) + + +/********************************************/ +/* Cache Management */ +/********************************************/ + +Q6INSN(Y2_ictagr,"Rd32=ictagr(Rs32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Tag Read",{fICTAGR(RsV,RdV,RdN);}) +Q6INSN(Y2_ictagw,"ictagw(Rs32,Rt32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Tag Write",{fICTAGW(RsV,RtV);}) +Q6INSN(Y2_icdataw,"icdataw(Rs32,Rt32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Data Write",{fICDATAW(RsV,RtV);}) +Q6INSN(Y2_icdatar,"Rd32=icdatar(Rs32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Data Read",{fICDATAR(RsV, RdV);}) +Q6INSN(Y2_icinva,"icinva(Rs32)",ATTRIBS(A_ICOP,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYADDRESS,A_ICFLUSHOP),"Instruction Cache Invalidate Address",{fEA_REG(RsV); fICINVA(EA);}) +Q6INSN(Y2_icinvidx,"icinvidx(Rs32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICFLUSHOP),"Instruction Cache Invalidate Index",{fICINVIDX(RsV);}) +Q6INSN(Y2_ickill,"ickill",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_ICFLUSHOP),"Instruction Cache Invalidate",{fICKILL();}) + +Q6INSN(Y2_isync,"isync",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Memory Synchronization",{fISYNC();}) +Q6INSN(Y2_barrier,"barrier",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK),"Memory Barrier",{fBARRIER();}) +Q6INSN(Y2_syncht,"syncht",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET),"Memory Synchronization",{fSYNCH();}) + + +Q6INSN(Y2_dcfetchbo,"dcfetch(Rs32+#u11:3)",ATTRIBS(A_RESTRICT_PREFERSLOT0,A_DCFETCH,A_RESTRICT_NOSLOT1_STORE),"Data Cache Prefetch",{fEA_RI(RsV,uiV); fDCFETCH(EA);}) +Q6INSN(Y2_dckill,"dckill",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_DCFLUSHOP),"Data Cache Invalidate",{fDCKILL();}) + + +Q6INSN(Y2_dczeroa,"dczeroa(Rs32)",ATTRIBS(A_STORE,A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCZEROA),"Zero an aligned 32-byte cacheline",{fEA_REG(RsV); fDCZEROA(EA);}) +Q6INSN(Y2_dccleana,"dccleana(Rs32)",ATTRIBS(A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCFLUSHOP),"Data Cache Clean Address",{fEA_REG(RsV); fDCCLEANA(EA);}) +Q6INSN(Y2_dccleanidx,"dccleanidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCFLUSHOP),"Data Cache Clean Index",{fDCCLEANIDX(RsV);}) +Q6INSN(Y2_dccleaninva,"dccleaninva(Rs32)",ATTRIBS(A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCFLUSHOP),"Data Cache Clean and Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) +Q6INSN(Y2_dccleaninvidx,"dccleaninvidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCFLUSHOP),"Data Cache Clean and Invalidate Index",{fDCCLEANINVIDX(RsV);}) +Q6INSN(Y2_dcinva,"dcinva(Rs32)",ATTRIBS(A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCFLUSHOP),"Data Cache Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) +Q6INSN(Y2_dcinvidx,"dcinvidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCFLUSHOP),"Data Cache Invalidate Index",{fDCINVIDX(RsV);}) +Q6INSN(Y2_dctagr,"Rd32=dctagr(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCTAGOP),"Data Cache Tag Read",{fDCTAGR(RsV,RdV,RdN);}) +Q6INSN(Y2_dctagw,"dctagw(Rs32,Rt32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_SLOT0ONLY,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_DCTAGOP),"Data Cache Tag Write",{fDCTAGW(RsV,RtV);}) + + +Q6INSN(Y2_l2kill,"l2kill",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Cache Invalidate",{fL2KILL();}) +Q6INSN(Y4_l2tagw,"l2tagw(Rs32,Rt32)",ATTRIBS(A_PRIV,A_NOTE_BADTAG_UNDEF,A_NOTE_PRIV,A_RESTRICT_SLOT0ONLY,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_L2TAGOP),"L2 Cache Tag Write",{fL2TAGW(RsV,RtV);}) +Q6INSN(Y4_l2tagr,"Rd32=l2tagr(Rs32)",ATTRIBS(A_PRIV,A_NOTE_BADTAG_UNDEF,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2TAGOP),"L2 Cache Tag Read",{fL2TAGR(RsV,RdV,RdN);}) + +Q6INSN(Y2_l2cleaninvidx,"l2cleaninvidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2FLUSHOP),"L2 Cache Clean and Invalidate Index",{fL2CLEANINVIDX(RsV); }) +Q6INSN(Y5_l2cleanidx,"l2cleanidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2FLUSHOP),"L2 Cache Clean by Index",{fL2CLEANIDX(RsV); }) +Q6INSN(Y5_l2invidx,"l2invidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2FLUSHOP),"L2 Cache Invalidate by Index",{fL2INVIDX(RsV); }) + + + +Q6INSN(Y4_l2fetch,"l2fetch(Rs32,Rt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK),"L2 Cache Prefetch", { fL2FETCH(RsV, (RtV&0xff), /*height*/ ((RtV>>8)&0xff), /*width*/ @@ -59,10 +258,29 @@ -Q6INSN(Y5_l2fetch,"l2fetch(Rs32,Rtt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY),"L2 Cache Prefetch", +Q6INSN(Y5_l2fetch,"l2fetch(Rs32,Rtt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK),"L2 Cache Prefetch", { fL2FETCH(RsV, fGETUHALF(0,RttV), /*height*/ fGETUHALF(1,RttV), /*width*/ fGETUHALF(2,RttV), /*stride*/ fGETUHALF(3,RttV)); /*flags*/ }) + +Q6INSN(Y5_l2locka,"Pd4=l2locka(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_CACHEOP,A_COPBYADDRESS,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_LATEPRED,A_NOTE_LATEPRED), +"Lock L2 cache line by address", { fEA_REG(RsV); fL2LOCKA(EA,PdV,PdN); fHIDE(MARK_LATE_PRED_WRITE(PdN)) }) + + +Q6INSN(Y5_l2unlocka,"l2unlocka(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_CACHEOP,A_COPBYADDRESS,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK), "UnLock L2 cache line by address", { fEA_REG(RsV); fL2UNLOCKA(EA); }) + + + +Q6INSN(Y5_l2gunlock,"l2gunlock",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Unlock",{fL2UNLOCK();}) + +Q6INSN(Y5_l2gclean,"l2gclean",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean",{fL2CLEAN();}) + +Q6INSN(Y5_l2gcleaninv,"l2gcleaninv",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean and Invalidate",{fL2CLEANINV();}) + +Q6INSN(Y6_l2gcleanpa,"l2gclean(Rtt32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean by PA Range",{fL2CLEANPA(RttV);}) + +Q6INSN(Y6_l2gcleaninvpa,"l2gcleaninv(Rtt32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean and Invalidate by PA Range",{fL2CLEANINVPA(RttV);}) +
diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h index 5fc837a..00b37ae 100644 --- a/target/hexagon/internal.h +++ b/target/hexagon/internal.h
@@ -30,5 +30,27 @@ void hexagon_debug(CPUHexagonState *env); extern const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS]; +#ifndef CONFIG_USER_ONLY +extern const char * const hexagon_sregnames[]; +extern const char * const hexagon_gregnames[]; +#endif + +void G_NORETURN do_raise_exception(CPUHexagonState *env, + uint32_t exception, + uint32_t PC, + uintptr_t retaddr); + +#define hexagon_cpu_mmu_enabled(env) ({ \ + HexagonCPU *cpu = env_archcpu(env); \ + cpu->globalregs ? \ + GET_SYSCFG_FIELD(SYSCFG_MMUEN, \ + hexagon_globalreg_read(cpu->globalregs, \ + HEX_SREG_SYSCFG, (env)->threadId)) : \ + 0; \ +}) + +#ifndef CONFIG_USER_ONLY +extern const VMStateDescription vmstate_hexagon_cpu; +#endif #endif
diff --git a/target/hexagon/machine.c b/target/hexagon/machine.c new file mode 100644 index 0000000..2dd9546 --- /dev/null +++ b/target/hexagon/machine.c
@@ -0,0 +1,32 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "migration/vmstate.h" +#include "cpu.h" + +const VMStateDescription vmstate_hexagon_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(env.gpr, HexagonCPU, TOTAL_PER_THREAD_REGS), + VMSTATE_UINT32_ARRAY(env.pred, HexagonCPU, NUM_PREGS), + VMSTATE_UINT32_ARRAY(env.t_sreg, HexagonCPU, NUM_SREGS), + VMSTATE_UINT32_ARRAY(env.greg, HexagonCPU, NUM_GREGS), + VMSTATE_UINT32(env.next_PC, HexagonCPU), + VMSTATE_UINT32(env.tlb_lock_state, HexagonCPU), + VMSTATE_UINT32(env.k0_lock_state, HexagonCPU), + VMSTATE_UINT32(env.tlb_lock_count, HexagonCPU), + VMSTATE_UINT32(env.k0_lock_count, HexagonCPU), + VMSTATE_UINT32(env.threadId, HexagonCPU), + VMSTATE_UINT32(env.cause_code, HexagonCPU), + VMSTATE_UINT32(env.wait_next_pc, HexagonCPU), + VMSTATE_UINT64(env.t_cycle_count, HexagonCPU), + + VMSTATE_END_OF_LIST() + }, +};
diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index eebfe1e..21ab8ae 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h
@@ -538,9 +538,6 @@ #ifdef CONFIG_USER_ONLY #define fFRAMECHECK(ADDR, EA) do { } while (0) /* Not modelled in linux-user */ -#else -/* System mode not implemented yet */ -#define fFRAMECHECK(ADDR, EA) g_assert_not_reached(); #endif #ifdef QEMU_GENERATE @@ -631,8 +628,18 @@ #define fCONSTLL(A) A##LL #define fECHO(A) (A) -#define fTRAP(TRAPTYPE, IMM) helper_raise_exception(env, HEX_EXCP_TRAP0) +#ifdef CONFIG_USER_ONLY +#define fTRAP(TRAPTYPE, IMM) \ + do { \ + hexagon_raise_exception_err(env, HEX_EVENT_TRAP0, PC); \ + } while (0) +#endif + +#define fDO_TRACE(SREG) +#define fBREAK() +#define fUNPAUSE() #define fPAUSE(IMM) +#define fDCFETCH(REG) #define fALIGN_REG_FIELD_VALUE(FIELD, VAL) \ ((VAL) << reg_field_info[FIELD].offset) @@ -643,6 +650,16 @@ reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset) +#define fGET_FIELD(VAL, FIELD) \ + fEXTRACTU_BITS(VAL, \ + reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset) +#define fSET_FIELD(VAL, FIELD, NEWVAL) \ + fINSERT_BITS(VAL, \ + reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset, \ + (NEWVAL)) + #ifdef QEMU_GENERATE #define fDCZEROA(REG) \ do { \ @@ -654,5 +671,18 @@ #define fBRANCH_SPECULATE_STALL(DOTNEWVAL, JUMP_COND, SPEC_DIR, HINTBITNUM, \ STRBITNUM) /* Nothing */ +#ifdef CONFIG_USER_ONLY +/* + * This macro can only be true in guest mode. + * In user mode, the 4 VIRTINSN's can't be reached + */ +#define fTRAP1_VIRTINSN(IMM) (false) +#define fVIRTINSN_SPSWAP(IMM, REG) g_assert_not_reached() +#define fVIRTINSN_GETIE(IMM, REG) g_assert_not_reached() +#define fVIRTINSN_SETIE(IMM, REG) g_assert_not_reached() +#define fVIRTINSN_RTE(IMM, REG) g_assert_not_reached() +#endif + +#define fPREDUSE_TIMING() #endif
diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index d169cf7..bc7a292 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build
@@ -20,6 +20,7 @@ hex_common_py = 'hex_common.py' gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h' gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h' +gen_tcg_sys_h = meson.current_source_dir() / 'gen_tcg_sys.h' idef_parser_dir = meson.current_source_dir() / 'idef-parser' # @@ -234,6 +235,7 @@ command: [python, files('gen_trans_funcs.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(decodetree_trans_funcs_generated) +hexagon_softmmu_ss = ss.source_set() hexagon_ss.add(files( 'cpu.c', @@ -252,6 +254,14 @@ 'mmvec/system_ext_mmvec.c', )) +hexagon_softmmu_ss.add(files( + 'cpu_helper.c', + 'hex_mmu.c', + 'hex_interrupts.c', + 'hexswi.c', + 'machine.c', +)) + # # Step 4.5 # We use flex/bison based idef-parser to generate TCG code for a lot @@ -261,7 +271,8 @@ # idef-generated-enabled-instructions # idef_parser_enabled = get_option('hexagon_idef_parser') -if idef_parser_enabled and 'hexagon-linux-user' in target_dirs +if idef_parser_enabled and ('hexagon-linux-user' in target_dirs or + 'hexagon-softmmu' in target_dirs) idef_parser_input_generated = custom_target( 'idef_parser_input.h.inc', output: 'idef_parser_input.h.inc', @@ -337,12 +348,12 @@ # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg] - helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, '--idef-parser', idef_generated_list] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h, '--idef-parser', idef_generated_list] else # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated] - helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h] endif # @@ -356,7 +367,7 @@ 'helper_protos_generated.h.inc', output: 'helper_protos_generated.h.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_helper_protos.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(helper_protos_generated) @@ -365,7 +376,7 @@ 'helper_funcs_generated.c.inc', output: 'helper_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_helper_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(helper_funcs_generated) @@ -374,7 +385,7 @@ 'tcg_funcs_generated.c.inc', output: 'tcg_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_tcg_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(tcg_funcs_generated) @@ -383,9 +394,10 @@ 'analyze_funcs_generated.c.inc', output: 'analyze_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_analyze_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(analyze_funcs_generated) target_arch += {'hexagon': hexagon_ss} +target_system_arch += {'hexagon': hexagon_softmmu_ss}
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 0a94643..125952a 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c
@@ -20,36 +20,59 @@ #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/cpu-loop.h" #include "accel/tcg/probe.h" +#include "qemu/main-loop.h" +#include "cpu.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" -#include "cpu.h" +#include "exec/cpu-interrupt.h" #include "internal.h" #include "macros.h" +#include "sys_macros.h" #include "arch.h" #include "hex_arch_types.h" #include "fma_emu.h" #include "mmvec/mmvec.h" #include "mmvec/macros.h" #include "op_helper.h" +#include "cpu_helper.h" #include "translate.h" +#ifndef CONFIG_USER_ONLY +#include "hw/hexagon/hexagon_globalreg.h" +#include "hex_mmu.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hex_interrupts.h" +#include "hexswi.h" +#endif #define SF_BIAS 127 #define SF_MANTBITS 23 /* Exceptions processing helpers */ +G_NORETURN +void do_raise_exception(CPUHexagonState *env, uint32_t exception, + uint32_t PC, uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + qemu_log_mask(CPU_LOG_INT, "%s: 0x%08" PRIx32 ", @ %08" PRIx32 "\n", + __func__, exception, PC); + ASSERT_DIRECT_TO_GUEST_UNSET(env, exception); + + env->gpr[HEX_REG_PC] = PC; + cs->exception_index = exception; + cpu_loop_exit_restore(cs, retaddr); +} + G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, uint32_t exception, uintptr_t pc) { - CPUState *cs = env_cpu(env); - qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); - cs->exception_index = exception; - cpu_loop_exit_restore(cs, pc); + do_raise_exception(env, exception, pc, 0); } -G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) +G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp, + uint32_t PC) { - hexagon_raise_exception_err(env, excp, 0); + hexagon_raise_exception_err(env, excp, PC); } void log_store32(CPUHexagonState *env, target_ulong addr, @@ -1205,6 +1228,131 @@ return RxxV; } +#ifndef CONFIG_USER_ONLY +void HELPER(modify_ssr)(CPUHexagonState *env, uint32_t new, uint32_t old) +{ + BQL_LOCK_GUARD(); + hexagon_modify_ssr(env, new, old); +} + +static void hex_k0_lock(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + target_ulong syscfg; + + BQL_LOCK_GUARD(); + g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1)); + + syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + if (GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)) { + if (env->k0_lock_state == HEX_LOCK_QUEUED) { + env->next_PC += 4; + env->k0_lock_count++; + env->k0_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1); + return; + } + if (env->k0_lock_state == HEX_LOCK_OWNER) { + qemu_log_mask(LOG_GUEST_ERROR, + "Double k0lock at PC: 0x%" PRIx32 + ", thread may hang\n", + env->next_PC); + env->next_PC += 4; + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + cpu_loop_exit(cs); + return; + } + env->k0_lock_state = HEX_LOCK_WAITING; + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + cpu_loop_exit(cs); + } else { + env->next_PC += 4; + env->k0_lock_count++; + env->k0_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1); + } +} + +static void hex_k0_unlock(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + unsigned int this_threadId = env->threadId; + CPUHexagonState *unlock_thread = NULL; + CPUState *cs; + target_ulong syscfg; + + BQL_LOCK_GUARD(); + g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1)); + + /* Nothing to do if the k0 isn't locked by this thread */ + syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + if ((GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg) == 0) || + (env->k0_lock_state != HEX_LOCK_OWNER)) { + qemu_log_mask(LOG_GUEST_ERROR, + "thread %" PRIu32 " attempted to unlock k0 without" + " having the lock, k0_lock state = %u," + " syscfg:k0 = %" PRIu32 "\n", + env->threadId, (unsigned)env->k0_lock_state, + (uint32_t)GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)); + g_assert(env->k0_lock_state != HEX_LOCK_WAITING); + return; + } + + env->k0_lock_count--; + env->k0_lock_state = HEX_LOCK_UNLOCKED; + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 0); + + /* Look for a thread to unlock */ + CPU_FOREACH(cs) { + CPUHexagonState *thread = cpu_env(cs); + + /* + * The hardware implements round-robin fairness, so we look for threads + * starting at env->threadId + 1 and incrementing modulo the number of + * threads. + * + * To implement this, we check if thread is a earlier in the modulo + * sequence than unlock_thread. + * if unlock thread is higher than this thread + * thread must be between this thread and unlock_thread + * else + * thread higher than this thread is ahead of unlock_thread + * thread must be lower then unlock thread + */ + if (thread->k0_lock_state == HEX_LOCK_WAITING) { + if (!unlock_thread) { + unlock_thread = thread; + } else if (unlock_thread->threadId > this_threadId) { + if (this_threadId < thread->threadId && + thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } else { + if (thread->threadId > this_threadId) { + unlock_thread = thread; + } + if (thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } + } + } + if (unlock_thread) { + cs = env_cpu(unlock_thread); + unlock_thread->k0_lock_state = HEX_LOCK_QUEUED; + SET_SYSCFG_FIELD(unlock_thread, SYSCFG_K0LOCK, 1); + cpu_interrupt(cs, CPU_INTERRUPT_K0_UNLOCK); + } + +} +#endif + + /* Histogram instructions */ void HELPER(vhist)(CPUHexagonState *env) @@ -1370,6 +1518,420 @@ } } +#ifndef CONFIG_USER_ONLY +void HELPER(raise_stack_overflow)(CPUHexagonState *env, uint32_t slot, + uint32_t badva) +{ + /* + * Per section 7.3.1 of the V67 Programmer's Reference, + * stack limit exception isn't raised in monitor mode. + */ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + CPUState *cs; + + if (GET_SSR_FIELD(SSR_EX, ssr) || + !GET_SSR_FIELD(SSR_UM, ssr)) { + return; + } + + cs = env_cpu(env); + cs->exception_index = HEX_EVENT_PRECISE; + env->cause_code = HEX_CAUSE_STACK_LIMIT; + ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index); + + if (slot == 0) { + env->t_sreg[HEX_SREG_BADVA0] = badva; + SET_SSR_FIELD(env, SSR_V0, 1); + SET_SSR_FIELD(env, SSR_V1, 0); + SET_SSR_FIELD(env, SSR_BVS, 0); + } else if (slot == 1) { + env->t_sreg[HEX_SREG_BADVA1] = badva; + SET_SSR_FIELD(env, SSR_V0, 0); + SET_SSR_FIELD(env, SSR_V1, 1); + SET_SSR_FIELD(env, SSR_BVS, 1); + } else { + g_assert_not_reached(); + } + cpu_loop_exit_restore(cs, 0); +} + +void HELPER(ciad)(CPUHexagonState *env, uint32_t mask) +{ + g_assert_not_reached(); +} + +void HELPER(siad)(CPUHexagonState *env, uint32_t mask) +{ + uint32_t ipendad; + uint32_t iad; + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + cpu = env_archcpu(env); + ipendad = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId) : 0; + iad = fGET_FIELD(ipendad, IPENDAD_IAD); + fSET_FIELD(ipendad, IPENDAD_IAD, iad | mask); + if (cpu->globalregs) { + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); + } + hex_interrupt_update(env); +} + +void HELPER(swi)(CPUHexagonState *env, uint32_t mask) +{ + BQL_LOCK_GUARD(); + hex_raise_interrupts(env, mask, CPU_INTERRUPT_SWI); +} + +void HELPER(cswi)(CPUHexagonState *env, uint32_t mask) +{ + BQL_LOCK_GUARD(); + hex_clear_interrupts(env, mask, CPU_INTERRUPT_SWI); +} + +void HELPER(iassignw)(CPUHexagonState *env, uint32_t src) +{ + uint32_t modectl; + uint32_t thread_enabled_mask; + CPUState *cpu; + HexagonCPU *hex_cpu; + + BQL_LOCK_GUARD(); + hex_cpu = env_archcpu(env); + modectl = hex_cpu->globalregs ? + hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL, + env->threadId) : 0; + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + + CPU_FOREACH(cpu) { + CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env); + uint32_t thread_id_mask = 0x1 << thread_env->threadId; + if (thread_enabled_mask & thread_id_mask) { + uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK]; + uint32_t intbitpos = (src >> 16) & 0xF; + uint32_t val = (src >> thread_env->threadId) & 0x1; + imask = deposit32(imask, intbitpos, 1, val); + thread_env->t_sreg[HEX_SREG_IMASK] = imask; + + qemu_log_mask(CPU_LOG_INT, "%s: thread " TARGET_FMT_ld + ", new imask 0x%" PRIx32 "\n", __func__, + thread_env->threadId, imask); + } + } + hex_interrupt_update(env); +} + +uint32_t HELPER(iassignr)(CPUHexagonState *env, uint32_t src) +{ + uint32_t modectl; + uint32_t thread_enabled_mask; + uint32_t intbitpos; + uint32_t dest_reg; + CPUState *cpu; + HexagonCPU *hex_cpu; + + BQL_LOCK_GUARD(); + hex_cpu = env_archcpu(env); + modectl = hex_cpu->globalregs ? + hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL, + env->threadId) : 0; + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + /* src fields are in same position as modectl, but mean different things */ + intbitpos = GET_FIELD(MODECTL_W, src); + dest_reg = 0; + CPU_FOREACH(cpu) { + CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env); + uint32_t thread_id_mask = 0x1 << thread_env->threadId; + if (thread_enabled_mask & thread_id_mask) { + uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK]; + dest_reg |= ((imask >> intbitpos) & 0x1) << thread_env->threadId; + } + } + + return dest_reg; +} + +void HELPER(start)(CPUHexagonState *env, uint32_t imask) +{ + hexagon_start_threads(env, imask); +} + +void HELPER(stop)(CPUHexagonState *env) +{ + hexagon_stop_thread(env); +} + +static void set_wait_mode(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl; + uint32_t thread_wait_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (!cpu->globalregs) { + return; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_wait_mask = GET_FIELD(MODECTL_W, modectl); + thread_wait_mask |= 0x1 << env->threadId; + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_W, thread_wait_mask); +} + +static void hexagon_wait_thread(CPUHexagonState *env, uint32_t PC) +{ + CPUState *cs; + + g_assert(bql_locked()); + + if (qemu_loglevel_mask(LOG_GUEST_ERROR) && + (env->k0_lock_state != HEX_LOCK_UNLOCKED || + env->tlb_lock_state != HEX_LOCK_UNLOCKED)) { + qemu_log("WARNING: executing wait() with acquired lock" + "may lead to deadlock\n"); + } + g_assert(get_exe_mode(env) != HEX_EXE_MODE_WAIT); + + cs = env_cpu(env); + /* + * The addtion of cpu_has_work is borrowed from arm's wfi helper + * and is critical for our stability + */ + if ((cs->exception_index != HEX_EVENT_NONE) || + (cpu_has_work(cs))) { + qemu_log_mask(CPU_LOG_INT, + "%s: thread %" PRIu32 " skipping WAIT mode, have some work\n", + __func__, env->threadId); + return; + } + set_wait_mode(env); + env->wait_next_pc = PC + 4; + + cpu_interrupt(cs, CPU_INTERRUPT_HALT); +} + +static inline QEMU_ALWAYS_INLINE void resched(CPUHexagonState *env) +{ + uint32_t schedcfg; + uint32_t schedcfg_en; + int int_number; + CPUState *cs; + uint32_t lowest_th_prio = 0; /* 0 is highest prio */ + uint32_t bestwait_reg; + uint32_t best_prio; + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + qemu_log_mask(CPU_LOG_INT, "%s: check resched\n", __func__); + cpu = env_archcpu(env); + schedcfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG, + env->threadId) : 0; + schedcfg_en = GET_FIELD(SCHEDCFG_EN, schedcfg); + int_number = GET_FIELD(SCHEDCFG_INTNO, schedcfg); + + if (!schedcfg_en) { + return; + } + + CPU_FOREACH(cs) { + HexagonCPU *thread = HEXAGON_CPU(cs); + CPUHexagonState *thread_env = &(thread->env); + uint32_t th_prio = GET_FIELD( + STID_PRIO, thread_env->t_sreg[HEX_SREG_STID]); + if (!hexagon_thread_is_enabled(thread_env)) { + continue; + } + + lowest_th_prio = (lowest_th_prio > th_prio) + ? lowest_th_prio + : th_prio; + } + + bestwait_reg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_BESTWAIT, + env->threadId) : 0; + best_prio = GET_FIELD(BESTWAIT_PRIO, bestwait_reg); + + /* + * If the lowest priority thread is lower priority than the + * value in the BESTWAIT register, we must raise the reschedule + * interrupt on the lowest priority thread. + */ + if (lowest_th_prio > best_prio) { + qemu_log_mask(CPU_LOG_INT, + "%s: raising resched int %u," + " cur PC 0x%" PRIx32 "\n", + __func__, (unsigned)int_number, env->gpr[HEX_REG_PC]); + SET_SYSTEM_FIELD(env, HEX_SREG_BESTWAIT, BESTWAIT_PRIO, ~0); + hex_raise_interrupts(env, 1 << int_number, CPU_INTERRUPT_SWI); + } +} + +void HELPER(resched)(CPUHexagonState *env) +{ + resched(env); +} + +void HELPER(wait)(CPUHexagonState *env, uint32_t PC) +{ + BQL_LOCK_GUARD(); + + if (!fIN_DEBUG_MODE(env->threadId)) { + hexagon_wait_thread(env, PC); + } +} + +void HELPER(resume)(CPUHexagonState *env, uint32_t mask) +{ + BQL_LOCK_GUARD(); + hexagon_resume_threads(env, mask); +} + +uint32_t HELPER(getimask)(CPUHexagonState *env, uint32_t tid) +{ + CPUState *cs; + BQL_LOCK_GUARD(); + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + if (found_env->threadId == tid) { + uint32_t imask = found_env->t_sreg[HEX_SREG_IMASK]; + qemu_log_mask(CPU_LOG_INT, "%s: tid " TARGET_FMT_lx + " imask = 0x%" PRIx32 "\n", __func__, + env->threadId, + (uint32_t)GET_FIELD(IMASK_MASK, imask)); + return GET_FIELD(IMASK_MASK, imask); + } + } + return 0; +} + +void HELPER(setimask)(CPUHexagonState *env, uint32_t tid, uint32_t imask) +{ + CPUState *cs; + + BQL_LOCK_GUARD(); + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + + if (tid == found_env->threadId) { + SET_SYSTEM_FIELD(found_env, HEX_SREG_IMASK, IMASK_MASK, imask); + qemu_log_mask(CPU_LOG_INT, "%s: tid " TARGET_FMT_lx + " imask 0x%" PRIx32 "\n", + __func__, found_env->threadId, imask); + hex_interrupt_update(found_env); + return; + } + } + qemu_log_mask(LOG_GUEST_ERROR, + "setimask used with an invalid tid near PC: 0x%" + PRIx32 "\n", env->next_PC); +} + +void HELPER(sreg_write_masked)(CPUHexagonState *env, uint32_t reg, uint32_t val) +{ + BQL_LOCK_GUARD(); + if (reg < HEX_SREG_GLB_START) { + env->t_sreg[reg] = val; + } else { + HexagonCPU *cpu = env_archcpu(env); + if (cpu->globalregs) { + hexagon_globalreg_write_masked(cpu->globalregs, reg, val); + } + } +} + +static inline QEMU_ALWAYS_INLINE uint32_t sreg_read(CPUHexagonState *env, + uint32_t reg) +{ + HexagonCPU *cpu; + + g_assert(bql_locked()); + if (reg < HEX_SREG_GLB_START) { + return env->t_sreg[reg]; + } + cpu = env_archcpu(env); + return cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, reg, env->threadId) : 0; +} + +uint32_t HELPER(sreg_read)(CPUHexagonState *env, uint32_t reg) +{ + BQL_LOCK_GUARD(); + return sreg_read(env, reg); +} + +uint64_t HELPER(sreg_read_pair)(CPUHexagonState *env, uint32_t reg) +{ + BQL_LOCK_GUARD(); + + return deposit64((uint64_t) sreg_read(env, reg), 32, 32, + sreg_read(env, reg + 1)); +} + +uint32_t HELPER(greg_read)(CPUHexagonState *env, uint32_t reg) + +{ + return hexagon_greg_read(env, reg); +} + +uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg) + +{ + if (reg == HEX_GREG_G0 || reg == HEX_GREG_G2) { + return (uint64_t)(env->greg[reg]) | + (((uint64_t)(env->greg[reg + 1])) << 32); + } + switch (reg) { + case HEX_GREG_GPCYCLELO: + return hexagon_get_sys_pcycle_count(env); + default: + return (uint64_t)hexagon_greg_read(env, reg) | + ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32); + } +} + +/* + * setprio/resched - hardware-assisted scheduler helpers for managing + * the run queue and interrupt steering. + */ +void HELPER(setprio)(CPUHexagonState *env, uint32_t thread, uint32_t prio) +{ + CPUState *cs; + + BQL_LOCK_GUARD(); + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + if (thread == found_env->threadId) { + SET_SYSTEM_FIELD(found_env, HEX_SREG_STID, STID_PRIO, prio); + qemu_log_mask(CPU_LOG_INT, + "%s: tid %" PRIu32 " prio = 0x%" PRIx32 "\n", + __func__, found_env->threadId, prio); + resched(env); + return; + } + } + g_assert_not_reached(); +} + + +void HELPER(pending_interrupt)(CPUHexagonState *env) +{ + BQL_LOCK_GUARD(); + hex_interrupt_update(env); +} +#endif + + /* These macros can be referenced in the generated helper functions */ #define warn(...) /* Nothing */ #define fatal(...) g_assert_not_reached();
diff --git a/target/hexagon/reg_fields_def.h.inc b/target/hexagon/reg_fields_def.h.inc index f2a58d4..d2c706d 100644 --- a/target/hexagon/reg_fields_def.h.inc +++ b/target/hexagon/reg_fields_def.h.inc
@@ -39,3 +39,110 @@ DEF_REG_FIELD(USR_FPOVFE, 27, 1) DEF_REG_FIELD(USR_FPUNFE, 28, 1) DEF_REG_FIELD(USR_FPINPE, 29, 1) + +DEF_REG_FIELD(IPENDAD_IAD, 16, 16) +DEF_REG_FIELD(IPENDAD_IPEND, 0, 16) + +DEF_REG_FIELD(SCHEDCFG_EN, 8, 1) +DEF_REG_FIELD(SCHEDCFG_INTNO, 0, 4) +DEF_REG_FIELD(BESTWAIT_PRIO, 0, 9) + + +/* PTE (aka TLB entry) fields */ +DEF_REG_FIELD(PTE_PPD, 0, 24) +DEF_REG_FIELD(PTE_C, 24, 4) +DEF_REG_FIELD(PTE_U, 28, 1) +DEF_REG_FIELD(PTE_R, 29, 1) +DEF_REG_FIELD(PTE_W, 30, 1) +DEF_REG_FIELD(PTE_X, 31, 1) +DEF_REG_FIELD(PTE_VPN, 32, 20) +DEF_REG_FIELD(PTE_ASID, 52, 7) +DEF_REG_FIELD(PTE_ATR0, 59, 1) +DEF_REG_FIELD(PTE_ATR1, 60, 1) +DEF_REG_FIELD(PTE_PA35, 61, 1) +DEF_REG_FIELD(PTE_G, 62, 1) +DEF_REG_FIELD(PTE_V, 63, 1) + +/* SYSCFG fields */ +DEF_REG_FIELD(SYSCFG_MMUEN, 0, 1) +DEF_REG_FIELD(SYSCFG_ICEN, 1, 1) +DEF_REG_FIELD(SYSCFG_DCEN, 2, 1) +DEF_REG_FIELD(SYSCFG_ISDBTRUSTED, 3, 1) +DEF_REG_FIELD(SYSCFG_GIE, 4, 1) +DEF_REG_FIELD(SYSCFG_ISDBREADY, 5, 1) +DEF_REG_FIELD(SYSCFG_PCYCLEEN, 6, 1) +DEF_REG_FIELD(SYSCFG_V2X, 7, 1) +DEF_REG_FIELD(SYSCFG_IGNOREDABORT, 8, 1) +DEF_REG_FIELD(SYSCFG_PM, 9, 1) +DEF_REG_FIELD(SYSCFG_TLBLOCK, 11, 1) +DEF_REG_FIELD(SYSCFG_K0LOCK, 12, 1) +DEF_REG_FIELD(SYSCFG_BQ, 13, 1) +DEF_REG_FIELD(SYSCFG_PRIO, 14, 1) +DEF_REG_FIELD(SYSCFG_DMT, 15, 1) +DEF_REG_FIELD(SYSCFG_L2CFG, 16, 3) +DEF_REG_FIELD(SYSCFG_ITCM, 19, 1) +DEF_REG_FIELD(SYSCFG_L2NWA, 21, 1) +DEF_REG_FIELD(SYSCFG_L2NRA, 22, 1) +DEF_REG_FIELD(SYSCFG_L2WB, 23, 1) +DEF_REG_FIELD(SYSCFG_L2P, 24, 1) +DEF_REG_FIELD(SYSCFG_SLVCTL0, 25, 2) +DEF_REG_FIELD(SYSCFG_SLVCTL1, 27, 2) +DEF_REG_FIELD(SYSCFG_L2PARTSIZE, 29, 2) +DEF_REG_FIELD(SYSCFG_L2GCA, 31, 1) + +/* SSR fields */ +DEF_REG_FIELD(SSR_CAUSE, 0, 8) +DEF_REG_FIELD(SSR_ASID, 8, 7) +DEF_REG_FIELD(SSR_UM, 16, 1) +DEF_REG_FIELD(SSR_EX, 17, 1) +DEF_REG_FIELD(SSR_IE, 18, 1) +DEF_REG_FIELD(SSR_GM, 19, 1) +DEF_REG_FIELD(SSR_V0, 20, 1) +DEF_REG_FIELD(SSR_V1, 21, 1) +DEF_REG_FIELD(SSR_BVS, 22, 1) +DEF_REG_FIELD(SSR_CE, 23, 1) +DEF_REG_FIELD(SSR_PE, 24, 1) +DEF_REG_FIELD(SSR_BP, 25, 1) +DEF_REG_FIELD(SSR_XE2, 26, 1) +DEF_REG_FIELD(SSR_XA, 27, 3) +DEF_REG_FIELD(SSR_SS, 30, 1) +DEF_REG_FIELD(SSR_XE, 31, 1) + +/* misc registers */ +DEF_REG_FIELD(IMASK_MASK, 0, 16) + +DEF_REG_FIELD(STID_PRIO, 16, 8) +DEF_REG_FIELD(STID_STID, 0, 8) + +/* MODECTL fields */ +DEF_REG_FIELD(MODECTL_E, 0, 8) +DEF_REG_FIELD(MODECTL_W, 16, 8) + +DEF_REG_FIELD(CCR_L1ICP, 0, 2) +DEF_REG_FIELD(CCR_L1DCP, 3, 2) +DEF_REG_FIELD(CCR_L2CP, 6, 2) + +DEF_REG_FIELD(CCR_HFI, 16, 1) +DEF_REG_FIELD(CCR_HFD, 17, 1) +DEF_REG_FIELD(CCR_HFIL2, 18, 1) +DEF_REG_FIELD(CCR_HFDL2, 19, 1) +DEF_REG_FIELD(CCR_SFD, 20, 1) + +DEF_REG_FIELD(CCR_GIE, 24, 1) +DEF_REG_FIELD(CCR_GTE, 25, 1) +DEF_REG_FIELD(CCR_GEE, 26, 1) +DEF_REG_FIELD(CCR_GRE, 27, 1) +DEF_REG_FIELD(CCR_VV1, 29, 1) +DEF_REG_FIELD(CCR_VV2, 30, 1) +DEF_REG_FIELD(CCR_VV3, 31, 1) + +/* ISDB ST fields */ +DEF_REG_FIELD(ISDBST_WAITRUN, 24, 8) +DEF_REG_FIELD(ISDBST_ONOFF, 16, 8) +DEF_REG_FIELD(ISDBST_DEBUGMODE, 8, 8) +DEF_REG_FIELD(ISDBST_STUFFSTATUS, 5, 1) +DEF_REG_FIELD(ISDBST_CMDSTATUS, 4, 1) +DEF_REG_FIELD(ISDBST_PROCMODE, 3, 1) +DEF_REG_FIELD(ISDBST_MBXINSTATUS, 2, 1) +DEF_REG_FIELD(ISDBST_MBXOUTSTATUS, 1, 1) +DEF_REG_FIELD(ISDBST_READY, 0, 1)
diff --git a/target/hexagon/sys_macros.h b/target/hexagon/sys_macros.h new file mode 100644 index 0000000..a49c727 --- /dev/null +++ b/target/hexagon/sys_macros.h
@@ -0,0 +1,252 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_SYS_MACROS_H +#define HEXAGON_SYS_MACROS_H + +/* + * Macro definitions for Hexagon system mode + */ + +#ifndef CONFIG_USER_ONLY + +#include "system/physmem.h" + +#ifdef QEMU_GENERATE +#define GET_SSR_FIELD(RES, FIELD) \ + GET_FIELD(RES, FIELD, hex_t_sreg[HEX_SREG_SSR]) +#else + +#define GET_SSR_FIELD(FIELD, REGIN) \ + (uint32_t)GET_FIELD(FIELD, REGIN) +#define GET_SYSCFG_FIELD(FIELD, REGIN) \ + (uint32_t)GET_FIELD(FIELD, REGIN) +#define SET_SYSTEM_FIELD(ENV, REG, FIELD, VAL) \ + do { \ + HexagonCPU *_sf_cpu = env_archcpu(ENV); \ + uint32_t regval; \ + if ((REG) < HEX_SREG_GLB_START) { \ + regval = (ENV)->t_sreg[(REG)]; \ + } else { \ + regval = _sf_cpu->globalregs ? \ + hexagon_globalreg_read(_sf_cpu->globalregs, (REG), \ + (ENV)->threadId) : 0; \ + } \ + fINSERT_BITS(regval, reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset, (VAL)); \ + if ((REG) < HEX_SREG_GLB_START) { \ + (ENV)->t_sreg[(REG)] = regval; \ + } else if (_sf_cpu->globalregs) { \ + hexagon_globalreg_write(_sf_cpu->globalregs, (REG), regval, \ + (ENV)->threadId); \ + } \ + } while (0) +#define SET_SSR_FIELD(ENV, FIELD, VAL) \ + SET_SYSTEM_FIELD(ENV, HEX_SREG_SSR, FIELD, VAL) +#define SET_SYSCFG_FIELD(ENV, FIELD, VAL) \ + SET_SYSTEM_FIELD(ENV, HEX_SREG_SYSCFG, FIELD, VAL) + +#define CCR_FIELD_SET(ENV, FIELD) \ + (!!GET_FIELD(FIELD, (ENV)->t_sreg[HEX_SREG_CCR])) + +/* + * Direct-to-guest is not implemented yet, continuing would cause unexpected + * behavior, so we abort. + */ +#define ASSERT_DIRECT_TO_GUEST_UNSET(ENV, EXCP) \ + do { \ + switch (EXCP) { \ + case HEX_EVENT_TRAP0: \ + g_assert(!CCR_FIELD_SET(ENV, CCR_GTE)); \ + break; \ + case HEX_EVENT_IMPRECISE: \ + case HEX_EVENT_PRECISE: \ + case HEX_EVENT_FPTRAP: \ + g_assert(!CCR_FIELD_SET(ENV, CCR_GEE)); \ + break; \ + default: \ + if ((EXCP) >= HEX_EVENT_INT0) { \ + g_assert(!CCR_FIELD_SET(ENV, CCR_GIE)); \ + } \ + break; \ + } \ + } while (0) +#endif + +#define fREAD_ELR() (env->t_sreg[HEX_SREG_ELR]) + +#define fLOAD_PHYS(NUM, SIZE, SIGN, SRC1, SRC2, DST) { \ + const uintptr_t rs = ((unsigned long)(unsigned)(SRC1)) & 0x7ff; \ + const uintptr_t rt = ((unsigned long)(unsigned)(SRC2)) << 11; \ + const uintptr_t addr = rs + rt; \ + physical_memory_read(addr, &DST, sizeof(uint32_t)); \ +} + +#define fPOW2_HELP_ROUNDUP(VAL) \ + ((VAL) | \ + ((VAL) >> 1) | \ + ((VAL) >> 2) | \ + ((VAL) >> 4) | \ + ((VAL) >> 8) | \ + ((VAL) >> 16)) +#define fPOW2_ROUNDUP(VAL) (fPOW2_HELP_ROUNDUP((VAL) - 1) + 1) + +#define fTRAP(TRAPTYPE, IMM) \ + register_trap_exception(env, TRAPTYPE, IMM, PC) + +#ifdef QEMU_GENERATE +#define fFRAMECHECK(ADDR, EA) gen_framecheck(ctx, ADDR, EA) +#endif + +#define fVIRTINSN_SPSWAP(IMM, REG) +#define fVIRTINSN_GETIE(IMM, REG) { REG = 0xdeafbeef; } +#define fVIRTINSN_SETIE(IMM, REG) +#define fVIRTINSN_RTE(IMM, REG) +#define fGRE_ENABLED() \ + GET_FIELD(CCR_GRE, env->t_sreg[HEX_SREG_CCR]) +#define fTRAP1_VIRTINSN(IMM) \ + (fGRE_ENABLED() && \ + (((IMM) == 1) || ((IMM) == 3) || ((IMM) == 4) || ((IMM) == 6))) + +/* Not modeled in qemu */ + +#define MARK_LATE_PRED_WRITE(RNUM) +#define fICINVIDX(REG) +#define fICKILL() +#define fDCKILL() +#define fL2KILL() +#define fL2UNLOCK() +#define fL2CLEAN() +#define fL2CLEANINV() +#define fL2CLEANPA(REG) +#define fL2CLEANINVPA(REG) +#define fL2CLEANINVIDX(REG) +#define fL2CLEANIDX(REG) +#define fL2INVIDX(REG) +#define fL2TAGR(INDEX, DST, DSTREG) +#define fL2UNLOCKA(VA) ((void) VA) +#define fL2TAGW(INDEX, PART2) +#define fDCCLEANIDX(REG) +#define fDCCLEANINVIDX(REG) + +/* Always succeed: */ +#define fL2LOCKA(EA, PDV, PDN) ((void) EA, PDV = 0xFF) +#define fCLEAR_RTE_EX() \ + do { \ + uint32_t tmp = env->t_sreg[HEX_SREG_SSR]; \ + fINSERT_BITS(tmp, reg_field_info[SSR_EX].width, \ + reg_field_info[SSR_EX].offset, 0); \ + log_sreg_write(env, HEX_SREG_SSR, tmp, slot); \ + } while (0) + +#define fDCINVIDX(REG) +#define fDCINVA(REG) do { REG = REG; } while (0) /* Nothing to do in qemu */ + +#define fSET_TLB_LOCK() hex_tlb_lock(env); +#define fCLEAR_TLB_LOCK() hex_tlb_unlock(env); + +#define fSET_K0_LOCK() hex_k0_lock(env); +#define fCLEAR_K0_LOCK() hex_k0_unlock(env); + +#define fTLB_IDXMASK(INDEX) \ + ((INDEX) & (fPOW2_ROUNDUP( \ + fCAST4u(hexagon_tlb_get_num_entries(env_archcpu(env)->tlb))) - 1)) + +#define fTLB_NONPOW2WRAP(INDEX) \ + (((INDEX) >= hexagon_tlb_get_num_entries(env_archcpu(env)->tlb)) ? \ + ((INDEX) - hexagon_tlb_get_num_entries(env_archcpu(env)->tlb)) : \ + (INDEX)) + + +#define fTLBW(INDEX, VALUE) \ + hex_tlbw(env, (INDEX), (VALUE)) +#define fTLBW_EXTENDED(INDEX, VALUE) \ + hex_tlbw(env, (INDEX), (VALUE)) +#define fTLB_ENTRY_OVERLAP(VALUE) \ + (hex_tlb_check_overlap(env, VALUE, -1) != -2) +#define fTLB_ENTRY_OVERLAP_IDX(VALUE) \ + hex_tlb_check_overlap(env, VALUE, -1) +#define fTLBR(INDEX) \ + hexagon_tlb_read(env_archcpu(env)->tlb, \ + fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX))) +#define fTLBR_EXTENDED(INDEX) \ + hexagon_tlb_read(env_archcpu(env)->tlb, \ + fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX))) +#define fTLBP(TLBHI) \ + hex_tlb_lookup(env, ((TLBHI) >> 12), ((TLBHI) << 12)) +#define iic_flush_cache(p) + +#define fIN_DEBUG_MODE(TNUM) ({ \ + HexagonCPU *_cpu = env_archcpu(env); \ + uint32_t _isdbst = _cpu->globalregs ? \ + hexagon_globalreg_read(_cpu->globalregs, \ + HEX_SREG_ISDBST, env->threadId) : 0; \ + (GET_FIELD(ISDBST_DEBUGMODE, _isdbst) \ + & (0x1 << (TNUM))) != 0; }) + +#define fIN_DEBUG_MODE_NO_ISDB(TNUM) false +#define fIN_DEBUG_MODE_WARN(TNUM) false + +#ifdef QEMU_GENERATE + +/* + * Read tags back as zero for now: + * + * tag value in RD[31:10] for 32k, RD[31:9] for 16k + */ +#define fICTAGR(RS, RD, RD2) \ + do { \ + RD = ctx->zero; \ + } while (0) +#define fICTAGW(RS, RD) +#define fICDATAR(RS, RD) \ + do { \ + RD = ctx->zero; \ + } while (0) +#define fICDATAW(RS, RD) + +#define fDCTAGW(RS, RT) +/* tag: RD[23:0], state: RD[30:29] */ +#define fDCTAGR(INDEX, DST, DST_REG_NUM) \ + do { \ + DST = ctx->zero; \ + } while (0) +#else + +/* + * Read tags back as zero for now: + * + * tag value in RD[31:10] for 32k, RD[31:9] for 16k + */ +#define fICTAGR(RS, RD, RD2) \ + do { \ + RD = 0x00; \ + } while (0) +#define fICTAGW(RS, RD) +#define fICDATAR(RS, RD) \ + do { \ + RD = 0x00; \ + } while (0) +#define fICDATAW(RS, RD) + +#define fDCTAGW(RS, RT) +/* tag: RD[23:0], state: RD[30:29] */ +#define fDCTAGR(INDEX, DST, DST_REG_NUM) \ + do { \ + DST = 0; \ + } while (0) +#endif + +#else +#define ASSERT_DIRECT_TO_GUEST_UNSET(ENV, EXCP) do { } while (0) +#endif + +#define NUM_TLB_REGS(x) (hexagon_tlb_get_num_entries(env_archcpu(env)->tlb)) + +/* NMI routing not yet implemented; Y4_nmi is a no-op for now */ +#define fDO_NMI(THREAD_MASK) do { } while (0) + +#endif
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 0b9337d..7723591 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c
@@ -32,6 +32,7 @@ #include "translate.h" #include "genptr.h" #include "printinsn.h" +#include "exec/target_page.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -61,10 +62,19 @@ TCGv hex_llsc_addr; TCGv hex_llsc_val; TCGv_i64 hex_llsc_val_i64; +#ifndef CONFIG_USER_ONLY +TCGv_i64 hex_cycle_count; +#endif TCGv hex_vstore_addr[VSTORES_MAX]; TCGv hex_vstore_size[VSTORES_MAX]; TCGv hex_vstore_pending[VSTORES_MAX]; +#ifndef CONFIG_USER_ONLY +TCGv_i32 hex_greg[NUM_GREGS]; +TCGv_i32 hex_t_sreg[NUM_SREGS]; +TCGv_i32 hex_cause_code; +#endif + static const char * const hexagon_prednames[] = { "p0", "p1", "p2", "p3" }; @@ -117,11 +127,28 @@ return offset; } -static void gen_exception_raw(int excp) +static void gen_exception(int excp, uint32_t PC) { - gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp), + tcg_constant_i32(PC)); } +#ifndef CONFIG_USER_ONLY +static inline void gen_precise_exception(int excp, uint32_t PC) +{ + tcg_gen_movi_i32(hex_cause_code, excp); + gen_exception(HEX_EVENT_PRECISE, PC); +} + +static void gen_pcycle_counters(DisasContext *ctx) +{ + if (ctx->pcycle_enabled) { + tcg_gen_addi_i64(hex_cycle_count, hex_cycle_count, ctx->num_cycles); + } +} +#endif + + static void gen_exec_counters(DisasContext *ctx) { tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_PKT_CNT], @@ -130,6 +157,9 @@ hex_gpr[HEX_REG_QEMU_INSN_CNT], ctx->num_insns); tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_HVX_CNT], hex_gpr[HEX_REG_QEMU_HVX_CNT], ctx->num_hvx_insns); +#ifndef CONFIG_USER_ONLY + gen_pcycle_counters(ctx); +#endif } static bool use_goto_tb(DisasContext *ctx, target_ulong dest) @@ -187,11 +217,14 @@ ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_exception_end_tb(DisasContext *ctx, int excp) +void hex_gen_exception_end_tb(DisasContext *ctx, int excp) { gen_exec_counters(ctx); - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC); - gen_exception_raw(excp); +#ifdef CONFIG_USER_ONLY + gen_exception(excp, ctx->pkt.pc); +#else + gen_precise_exception(excp, ctx->pkt.pc); +#endif ctx->base.is_jmp = DISAS_NORETURN; } @@ -205,7 +238,7 @@ gen_exec_counters(ctx); tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], fail_pc); - gen_exception_raw(excp); + gen_exception(excp, fail_pc); ctx->base.is_jmp = DISAS_NORETURN; ctx->base.pc_next = fail_pc; } @@ -249,6 +282,16 @@ return false; } +static bool check_for_opcode(Packet *pkt, uint16_t opcode) +{ + for (int i = 0; i < pkt->num_insns; i++) { + if (pkt->insn[i].opcode == opcode) { + return true; + } + } + return false; +} + static bool need_slot_cancelled(Packet *pkt) { /* We only need slot_cancelled for conditional store instructions */ @@ -262,6 +305,90 @@ return false; } +#ifndef CONFIG_USER_ONLY +static bool sreg_write_ends_tb(int reg_num) +{ + return reg_num == HEX_SREG_SSR || + reg_num == HEX_SREG_STID || + reg_num == HEX_SREG_IMASK || + reg_num == HEX_SREG_IPENDAD || + reg_num == HEX_SREG_BESTWAIT || + reg_num == HEX_SREG_SCHEDCFG; +} + +static bool has_sreg_write_ends_tb(Packet const *pkt) +{ + for (int i = 0; i < pkt->num_insns; i++) { + Insn const *insn = &pkt->insn[i]; + uint16_t opcode = insn->opcode; + if (opcode == Y2_tfrsrcr) { + /* Write to a single sreg */ + int reg_num = insn->regno[0]; + if (sreg_write_ends_tb(reg_num)) { + return true; + } + } else if (opcode == Y4_tfrspcp) { + /* Write to a sreg pair */ + int reg_num = insn->regno[0]; + if (sreg_write_ends_tb(reg_num)) { + return true; + } + if (sreg_write_ends_tb(reg_num + 1)) { + return true; + } + } + } + return false; +} +#endif + +static bool pkt_ends_tb(Packet *pkt) +{ + if (pkt->pkt_has_cof) { + return true; + } +#ifndef CONFIG_USER_ONLY + /* System mode instructions that end TLB */ + if (check_for_opcode(pkt, Y2_swi) || + check_for_opcode(pkt, Y2_cswi) || + check_for_opcode(pkt, Y2_ciad) || + check_for_opcode(pkt, Y4_siad) || + check_for_opcode(pkt, Y2_wait) || + check_for_opcode(pkt, Y2_resume) || + check_for_opcode(pkt, Y2_iassignw) || + check_for_opcode(pkt, Y2_setimask) || + check_for_opcode(pkt, Y4_nmi) || + check_for_opcode(pkt, Y2_setprio) || + check_for_opcode(pkt, Y2_start) || + check_for_opcode(pkt, Y2_stop) || + check_for_opcode(pkt, Y2_k0lock) || + check_for_opcode(pkt, Y2_k0unlock) || + check_for_opcode(pkt, Y2_tlblock) || + check_for_opcode(pkt, Y2_tlbunlock) || + check_for_opcode(pkt, Y2_break) || + check_for_opcode(pkt, Y2_isync) || + check_for_opcode(pkt, Y2_syncht) || + check_for_opcode(pkt, Y2_tlbp) || + check_for_opcode(pkt, Y2_tlbw) || + check_for_opcode(pkt, Y5_ctlbw) || + check_for_opcode(pkt, Y5_tlbasidi)) { + return true; + } + + /* + * Check for sreg writes that would end the TB + */ + if (check_for_attrib(pkt, A_IMPLICIT_WRITES_SSR)) { + return true; + } + if (has_sreg_write_ends_tb(pkt)) { + return true; + } +#endif + return false; +} + + static bool need_next_PC(DisasContext *ctx) { /* Check for conditional control flow or HW loop end */ @@ -309,6 +436,16 @@ } } +#ifndef CONFIG_USER_ONLY +static void mark_implicit_sreg_write(DisasContext *ctx, int attrib, int snum) +{ + uint16_t opcode = ctx->insn->opcode; + if (GET_ATTRIB(opcode, attrib)) { + ctx_log_sreg_write(ctx, snum); + } +} +#endif + static void mark_implicit_reg_writes(DisasContext *ctx) { mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_FP, HEX_REG_FP); @@ -321,6 +458,12 @@ mark_implicit_usr_write(ctx, A_IMPLICIT_WRITES_USR); mark_implicit_usr_write(ctx, A_FPOP); + +#ifndef CONFIG_USER_ONLY + mark_implicit_sreg_write(ctx, A_IMPLICIT_WRITES_SGP0, HEX_SREG_SGP0); + mark_implicit_sreg_write(ctx, A_IMPLICIT_WRITES_SGP1, HEX_SREG_SGP1); + mark_implicit_sreg_write(ctx, A_IMPLICIT_WRITES_SSR, HEX_SREG_SSR); +#endif } static void mark_implicit_pred_write(DisasContext *ctx, int attrib, int pnum) @@ -418,7 +561,11 @@ static void gen_start_packet(DisasContext *ctx) { - target_ulong next_PC = ctx->base.pc_next + ctx->pkt.encod_pkt_size_in_bytes; + Packet *pkt = &ctx->pkt; + target_ulong next_PC = (check_for_opcode(pkt, Y2_k0lock) || + check_for_opcode(pkt, Y2_tlblock)) ? + ctx->base.pc_next : + ctx->base.pc_next + pkt->encod_pkt_size_in_bytes; int i; /* Clear out the disassembly context */ @@ -426,6 +573,10 @@ ctx->reg_log_idx = 0; bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS); bitmap_zero(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); +#ifndef CONFIG_USER_ONLY + ctx->greg_log_idx = 0; + ctx->sreg_log_idx = 0; +#endif ctx->preg_log_idx = 0; bitmap_zero(ctx->pregs_written, NUM_PREGS); ctx->future_vregs_idx = 0; @@ -458,6 +609,25 @@ * gen phase, so clear it again. */ bitmap_zero(ctx->pregs_written, NUM_PREGS); +#ifndef CONFIG_USER_ONLY + for (i = 0; i < HEX_SREG_GLB_START; i++) { + ctx->t_sreg_new_value[i] = NULL; + } + for (i = 0; i < ctx->sreg_log_idx; i++) { + int reg_num = ctx->sreg_log[i]; + if (reg_num < HEX_SREG_GLB_START && + (ctx->need_commit || reg_num == HEX_SREG_SSR)) { + ctx->t_sreg_new_value[reg_num] = tcg_temp_new(); + } + } + for (i = 0; i < NUM_GREGS; i++) { + ctx->greg_new_value[i] = NULL; + } + for (i = 0; i < ctx->greg_log_idx; i++) { + int reg_num = ctx->greg_log[i]; + ctx->greg_new_value[reg_num] = tcg_temp_new(); + } +#endif /* Initialize the runtime state for packet semantics */ if (need_slot_cancelled(&ctx->pkt)) { @@ -580,7 +750,7 @@ ctx->insn->generate(ctx); mark_store_width(ctx); } else { - gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_OPCODE); + hex_gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_OPCODE); } } @@ -615,6 +785,50 @@ } } +#ifndef CONFIG_USER_ONLY +static void gen_greg_writes(DisasContext *ctx) +{ + int i; + + for (i = 0; i < ctx->greg_log_idx; i++) { + int reg_num = ctx->greg_log[i]; + + tcg_gen_mov_tl(hex_greg[reg_num], ctx->greg_new_value[reg_num]); + } +} + + +static void gen_sreg_writes(DisasContext *ctx) +{ + int i; + + TCGv_i32 old_reg = tcg_temp_new_i32(); + for (i = 0; i < ctx->sreg_log_idx; i++) { + int reg_num = ctx->sreg_log[i]; + + if (reg_num == HEX_SREG_SSR) { + tcg_gen_mov_tl(old_reg, hex_t_sreg[reg_num]); + tcg_gen_mov_tl(hex_t_sreg[reg_num], ctx->t_sreg_new_value[reg_num]); + gen_helper_modify_ssr(tcg_env, ctx->t_sreg_new_value[reg_num], + old_reg); + } else if ((reg_num == HEX_SREG_STID) || + (reg_num == HEX_SREG_IMASK) || + (reg_num == HEX_SREG_IPENDAD)) { + if (ctx->need_commit && reg_num < HEX_SREG_GLB_START) { + tcg_gen_mov_tl(hex_t_sreg[reg_num], + ctx->t_sreg_new_value[reg_num]); + } + gen_helper_pending_interrupt(tcg_env); + } else if ((reg_num == HEX_SREG_BESTWAIT) || + (reg_num == HEX_SREG_SCHEDCFG)) { + gen_helper_resched(tcg_env); + } else if (ctx->need_commit && reg_num < HEX_SREG_GLB_START) { + tcg_gen_mov_tl(hex_t_sreg[reg_num], ctx->t_sreg_new_value[reg_num]); + } + } +} +#endif + static void gen_pred_writes(DisasContext *ctx) { /* Early exit if not needed or the log is empty */ @@ -804,6 +1018,8 @@ } } +#define PCYCLES_PER_PACKET 1 + static void update_exec_counters(DisasContext *ctx) { int num_real_insns = 0; @@ -823,6 +1039,7 @@ ctx->num_packets++; ctx->num_insns += num_real_insns; ctx->num_hvx_insns += num_hvx_insns; + ctx->num_cycles += PCYCLES_PER_PACKET; } static void gen_commit_packet(DisasContext *ctx) @@ -908,6 +1125,10 @@ process_store_log(ctx); gen_reg_writes(ctx); +#ifndef CONFIG_USER_ONLY + gen_greg_writes(ctx); + gen_sreg_writes(ctx); +#endif gen_pred_writes(ctx); if (ctx->pkt.pkt_has_hvx) { gen_commit_hvx(ctx); @@ -920,7 +1141,7 @@ ctx->pkt.vhist_insn->generate(ctx); } - if (ctx->pkt.pkt_has_cof) { + if (pkt_ends_tb(&ctx->pkt) || ctx->base.is_jmp == DISAS_NORETURN) { gen_end_tb(ctx); } } @@ -964,7 +1185,7 @@ HexagonCPU *hex_cpu = env_archcpu(cpu_env(cs)); uint32_t hex_flags = dcbase->tb->flags; - ctx->mem_idx = MMU_USER_IDX; + ctx->mem_idx = FIELD_EX32(hex_flags, TB_FLAGS, MMU_INDEX); ctx->num_packets = 0; ctx->num_insns = 0; ctx->num_hvx_insns = 0; @@ -972,6 +1193,10 @@ ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP); ctx->short_circuit = hex_cpu->short_circuit; ctx->hex_def = HEXAGON_CPU_GET_CLASS(hex_cpu)->hex_def; +#ifndef CONFIG_USER_ONLY + ctx->num_cycles = 0; + ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); +#endif } static void hexagon_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -1080,6 +1305,20 @@ opcode_init(); +#ifndef CONFIG_USER_ONLY + for (i = 0; i < NUM_GREGS; i++) { + hex_greg[i] = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, greg[i]), + hexagon_gregnames[i]); + } + for (i = 0; i < NUM_SREGS; i++) { + if (i < HEX_SREG_GLB_START) { + hex_t_sreg[i] = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, t_sreg[i]), + hexagon_sregnames[i]); + } + } +#endif for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { hex_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, gpr[i]), @@ -1101,6 +1340,12 @@ offsetof(CPUHexagonState, llsc_val), "llsc_val"); hex_llsc_val_i64 = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHexagonState, llsc_val_i64), "llsc_val_i64"); +#ifndef CONFIG_USER_ONLY + hex_cause_code = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, cause_code), "cause_code"); + hex_cycle_count = tcg_global_mem_new_i64(tcg_env, + offsetof(CPUHexagonState, t_cycle_count), "t_cycle_count"); +#endif for (i = 0; i < STORES_MAX; i++) { snprintf(store_addr_names[i], NAME_LEN, "store_addr_%d", i); hex_store_addr[i] = tcg_global_mem_new(tcg_env,
diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 1fc185e..0a7f37d 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h
@@ -41,6 +41,14 @@ DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); bool implicit_usr_write; +#ifndef CONFIG_USER_ONLY + int greg_log[GREG_WRITES_MAX]; + int greg_log_idx; + int sreg_log[SREG_WRITES_MAX]; + int sreg_log_idx; + TCGv_i32 t_sreg_new_value[HEX_SREG_GLB_START]; + TCGv_i32 greg_new_value[NUM_GREGS]; +#endif int preg_log[PRED_WRITES_MAX]; int preg_log_idx; DECLARE_BITMAP(pregs_written, NUM_PREGS); @@ -77,10 +85,40 @@ TCGv new_pred_value[NUM_PREGS]; TCGv branch_taken; TCGv dczero_addr; + bool pcycle_enabled; + uint32_t num_cycles; } DisasContext; bool is_gather_store_insn(DisasContext *ctx); +#ifndef CONFIG_USER_ONLY +static inline void ctx_log_greg_write(DisasContext *ctx, int rnum) +{ + assert(rnum <= HEX_GREG_G3); + ctx->greg_log[ctx->greg_log_idx] = rnum; + ctx->greg_log_idx++; +} + +static inline void ctx_log_greg_write_pair(DisasContext *ctx, int rnum) +{ + assert(!(rnum % 2)); + ctx_log_greg_write(ctx, rnum); + ctx_log_greg_write(ctx, rnum + 1); +} + +static inline void ctx_log_sreg_write(DisasContext *ctx, int rnum) +{ + ctx->sreg_log[ctx->sreg_log_idx] = rnum; + ctx->sreg_log_idx++; +} + +static inline void ctx_log_sreg_write_pair(DisasContext *ctx, int rnum) +{ + ctx_log_sreg_write(ctx, rnum); + ctx_log_sreg_write(ctx, rnum + 1); +} +#endif + static inline void ctx_log_pred_write(DisasContext *ctx, int pnum) { if (!test_bit(pnum, ctx->pregs_written)) { @@ -282,6 +320,13 @@ extern TCGv hex_vstore_addr[VSTORES_MAX]; extern TCGv hex_vstore_size[VSTORES_MAX]; extern TCGv hex_vstore_pending[VSTORES_MAX]; +#ifndef CONFIG_USER_ONLY +extern TCGv_i32 hex_greg[NUM_GREGS]; +extern TCGv_i32 hex_t_sreg[NUM_SREGS]; +#endif + + +void hex_gen_exception_end_tb(DisasContext *ctx, int excp); void process_store(DisasContext *ctx, int slot_num); @@ -295,4 +340,6 @@ FIELD(PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED, 4, 1) FIELD(PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX, 5, 2) +void gen_framecheck(DisasContext *ctx, TCGv_i32 addr, TCGv_i32 ea); + #endif
diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index c357e6e..86bcdf7 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py
@@ -259,6 +259,7 @@ def __init__(self, source_dir: str, build_dir: str, ('arm', 'virt'), ('aarch64', 'virt'), ('avr', 'mega2560'), + ('hexagon', 'virt'), ('m68k', 'virt'), ('or1k', 'virt'), ('riscv32', 'virt'),
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index bcd0a9c..37fee7a 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c
@@ -142,6 +142,13 @@ 0x04, 0x38, 0x01, 0x40 /* 0x40013804 = USART1 TXD */ }; +static const uint8_t bios_hexagon[] = { + 0x00, 0x40, 0x00, 0x01, /* immext(#0x10000000) */ + 0x00, 0xc0, 0x00, 0x78, /* r0 = ##0x10000000 */ + 0x54, 0xc0, 0x00, 0x3c, /* memb(r0+#0) = #0x54 Write 'T' */ + 0xf8, 0xff, 0xff, 0x59 /* jump 0x0 ; Loop back to start */ +}; + typedef struct testdef { const char *arch; /* Target architecture */ const char *machine; /* Name of the machine */ @@ -194,6 +201,7 @@ { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, { "arm", "stm32vldiscovery", "", "T", sizeof(kernel_stm32vldiscovery), kernel_stm32vldiscovery }, + { "hexagon", "virt", "", "TT", sizeof(bios_hexagon), NULL, bios_hexagon }, { NULL } };
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 4897325..b2d3649 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build
@@ -295,6 +295,8 @@ ['iommu-riscv-test'] : []) + \ (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) +qtests_hexagon = ['boot-serial-test'] + qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c',