diff --git a/MAINTAINERS b/MAINTAINERS index 2101b5121756..0a65e76ae1b4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -229,14 +229,25 @@ Hexagon TCG CPUs M: Brian Cain S: Supported F: target/hexagon/ +F: hw/intc/l2vic.[ch] +F: hw/hexagon/ +F: hw/timer/qct-qtimer.c +F: include/hw/hexagon/ +F: include/hw/timer/qct-qtimer.h X: target/hexagon/idef-parser/ X: target/hexagon/gen_idef_parser_funcs.py F: linux-user/hexagon/ F: tests/tcg/hexagon/ F: disas/hexagon.c F: configs/targets/hexagon-linux-user/default.mak +F: configs/devices/hexagon-softmmu/default.mak F: docker/dockerfiles/debian-hexagon-cross.docker F: gdb-xml/hexagon*.xml +F: docs/system/target-hexagon.rst +F: docs/devel/hexagon-sys.rst +F: docs/devel/hexagon-vm.rst +F: docs/devel/hexagon-l2vic.rst +F: tests/functional/test_hexagon_minivm.py T: git https://github.com/quic/qemu.git hex-next Hexagon idef-parser diff --git a/configs/devices/hexagon-softmmu/default.mak b/configs/devices/hexagon-softmmu/default.mak new file mode 100644 index 000000000000..37b4f9f3237a --- /dev/null +++ b/configs/devices/hexagon-softmmu/default.mak @@ -0,0 +1,8 @@ +# 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 +# CONFIG_L2VIC=y diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak new file mode 100644 index 000000000000..3ff448e20ae4 --- /dev/null +++ b/configs/targets/hexagon-softmmu.mak @@ -0,0 +1,7 @@ +# Default configuration for hexagon-softmmu + +TARGET_ARCH=hexagon +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES=gdb-xml/hexagon-core.xml gdb-xml/hexagon-hvx.xml gdb-xml/hexagon-sys.xml +TARGET_NEED_FDT=y + diff --git a/docs/devel/hexagon-l2vic.rst b/docs/devel/hexagon-l2vic.rst new file mode 100644 index 000000000000..088563627445 --- /dev/null +++ b/docs/devel/hexagon-l2vic.rst @@ -0,0 +1,59 @@ +Hexagon L2 Vectored Interrupt Controller +======================================== + + +.. code-block:: none + + +-------+ + | | +----------------+ + | l2vic | | hexagon core | + | | | | + | +-----| | | + ------> |VID0 >------------->irq2 -\ | + ------> | | | | | + ... > | | | | | + ------> | | | | + | +-----| | / | | \ | + | ... | | | | | | | + | +-----| | t0 t1 t2 t3 ...| + ------> |VIDN | | | + ------> | | | | + ------> | | | | + ------> | | | | + | +-----| | | + | | |Global SREG File| + | State | | | + | [ ]|<============|=>[VID ] | + | [ ]|<============|=>[VID1] | + | [ ]| | | + | [ ]| | | + | | | | + +-------+ +----------------+ + +L2VIC/Core Integration +---------------------- + +* hexagon core supports 8 external interrupt sources +* l2vic supports 1024 input interrupts mapped among 4 output interrupts +* l2vic has four output signals: { VID0, VID1, VID2, VID3 } +* l2vic device has a bank of registers per-VID that can be used to query + the status or assert new interrupts. +* Interrupts are 'steered' to threads based on { thread priority, 'EX' state, + thread interrupt mask, thread interrupt enable, global interrupt enable, + etc. }. +* Any hardware thread could conceivably handle any input interrupt, dependent + on state. +* The system register transfer instruction can read the VID0-VID3 values from + the l2vic when reading from hexagon core system registers "VID" and "VID1". +* When l2vic VID0 has multiple active interrupts, it pulses the VID0 output + IRQ and stores the IRQ number for the VID0 register field. Only after this + interrupt is cleared can the l2vic pulse the VID0 output IRQ again and provide + the next interrupt number on the VID0 register. +* The ``ciad`` instruction clears the l2vic input interrupt and un-disables the + core interrupt. If some/an l2vic VID0 interrupt is pending when this occurs, + the next interrupt should fire and any subseqeunt reads of the VID register + should reflect the newly raised interrupt. +* In QEMU, on an external interrupt or an unmasked-pending interrupt, + all vCPUs are triggered (has_work==true) and each will grab the IO lock + while considering the steering logic to determine whether they're the thread + that must handle the interrupt. diff --git a/docs/devel/hexagon-sys.rst b/docs/devel/hexagon-sys.rst new file mode 100644 index 000000000000..3972261a2bbe --- /dev/null +++ b/docs/devel/hexagon-sys.rst @@ -0,0 +1,106 @@ +.. _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. diff --git a/docs/devel/hexagon-vm.rst b/docs/devel/hexagon-vm.rst new file mode 100644 index 000000000000..fb16d56d59de --- /dev/null +++ b/docs/devel/hexagon-vm.rst @@ -0,0 +1,190 @@ +Hexagon Virtual Machine +======================= + +The hexagon virtual machine is a hypervisor that can partition a single +Hexagon DSP among multiple guest operating systems, and abstracts the +specific details of a DSP architectural revision for the sake of consistency +among generations. + +Events +------ + +The guest operating system should register the Guest Event Vector Base +via the ``vmsetvec`` virtual instruction at system startup. The vector table +and handlers are determined by the guest OS. + +Guests return from event handlers with ``vmrte``. This instruction will restore +the mode (user versus guest), interrupt enable state, PC, SP. + +.. list-table:: Event types + :header-rows: 1 + + * - Number + - Name + - Description + - Maskable + - Detail + * - 0 + - Reserved + - + - + - + * - 1 + - Machine check event + - unrecoverable VM state + - No + - execution terminates if unhandled + * - 2 + - General exception + - internal hardware or software exception + - No + - + * - 3-4 + - Reserved + - + - + - + * - 5 + - ``trap0`` + - ``trap0`` instruction + - No + - + * - 6 + - Reserved + - + - + - + * - 7 + - Interrupt + - external interrupts + - Yes + - increasing interrupt numbers have descending priority + +Startup +------- +In order to transition to user-mode, the guest OS must set the ``UM`` bit in +the guest status register and specify the address to start executing in +user mode in the guest event link register. + +Virtual Instructions +-------------------- + +.. list-table:: Virtual Instructions + :header-rows: 1 + + * - Instruction + - Behavior + - Operand + - Input + - Output + * - vmversion + - returns the VM version + - 0x0 + - requested VM version + - provided VM version + * - vmrte + - return from event + - 0x1 + - Event info in g3:0 + - N/A + * - vmsetvec + - set event vector + - 0x2 + - r0 is set to vector table addr + - r0 is 0 on success, 1 otherwise + * - vmsetie + - set interrupt enabled + - 0x3 + - r0 is set to 1 to enable, 0 to disable + - previous IE bit is stored as LSB of r0 + * - vmgetie + - get interrupt enabled + - 0x4 + - N/A + - current IE bit is stored as LSB of r0 + * - vmintop + - interrupt operation + - 0x5 + - r0 = Interrupt Op, r1-r4: Depends on Op + - r0 - value depends on operation + * - vmclrmap + - clear virtual memory map + - 0xa + - r0 = Interrupt Op, r1-r4: Depends on Op + - r0 - value depends on operation + * - vmnewmap + - set new virtual memory map + - 0xb + - + r0 contains logical address of new segment table + + r1 = type of translations: 0 indicates a logical address of a zero-terminated linear list, 1 indicates a set of page tables. + - r0 contains 0 on success, otherwise negative error code + * - vmcache + - VM cache control: not modeled + - 0xd + - + r0 contains the operation to be performed + + r1 = Starting virtual address + + r2 contains the length in bytes + - r0 contains 0 on success, otherwise -1. Cache behavior is not modeled so this operation always succeeds. + * - vmgettime + - Get virtual machine time + - 0xe + - N/A + - r0 contains the least significant 32 bits of timestamp, r1 contains the most significant 32 bits of timestamp + * - vmsettime + - Set virtual machine time + - 0xf + - r0 contains the least significant 32 bits of timestamp, r1 contains the most significant 32 bits of timestamp + - N/A + * - vmwait + - wait for interrupt + - 0x10 + - N/A + - r0 contains the interrupt number of the interrupt waking the guest + * - vmyield + - voluntarily yield VM task + - 0x11 + - N/A + - N/A + * - vmstart + - Create new virtual processor instance + - 0x12 + - r0 contains the starting execution address, r1 contains the starting stack pointer + - r0 contains the Virtual processor number of new virtual processor on success, otherwise -1 + * - vmstop + - terminate current virtual processor instance + - 0x13 + - N/A + - N/A + * - vmvpid + - get the virtual processor ID + - 0x14 + - N/A + - r0 contains the virtual processor number of virtual processor executing the instruction + * - vmsetregs + - Set guest registers + - 0x15 + - r0-3 hold g0-3 values + - N/A + * - vmgetregs + - Get guest registers + - 0x16 + - N/A + - r0-3 hold g0-3 values + * - vmtimerop + - perform an operation on a system timer + - 0x18 + - + getfreq = 0 + + getres = 1 + + gettime = 2 + + gettimeout = 3 + + settimeout = 4 + + deltatimeout = 5 + - r0 contains result of the timer operation call + * - vmgetinfo + - Get system info + - 0x1a + - Index of the system info parameter: + + + build_id = 0 + + info_boot_flags = 1 + - value of the indicated system info parameter diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index ab9fbc448263..0f4286688f52 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -12,6 +12,9 @@ Details about QEMU's various subsystems including how to add features to them. block-coroutine-wrapper clocks ebpf_rss + hexagon-sys + hexagon-vm + hexagon-l2vic migration/index multi-process reset diff --git a/docs/system/hexagon/cdsp.rst b/docs/system/hexagon/cdsp.rst new file mode 100644 index 000000000000..f755fbe0a5ba --- /dev/null +++ b/docs/system/hexagon/cdsp.rst @@ -0,0 +1,10 @@ +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 000000000000..03a6092a1281 --- /dev/null +++ b/docs/system/hexagon/emulation.rst @@ -0,0 +1,16 @@ +.. _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 V69 HVX Programmer's Reference Manual +`_. + +.. code-block:: bash + diff --git a/docs/system/target-hexagon.rst b/docs/system/target-hexagon.rst new file mode 100644 index 000000000000..894337a533cd --- /dev/null +++ b/docs/system/target-hexagon.rst @@ -0,0 +1,112 @@ +.. _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 Virtual Machine +----------------------- + +The hexagon virtual machine is a hypervisor that can partition a single +Hexagon DSP among multiple guest operating systems, and abstracts the +specific details of a DSP architectural revision for the sake of consistency +among generations. + +[minivm](https://github.com/quic/hexagonMVM) is a reference implementation +of this VM interface. + + +Hexagon Features +================ +.. toctree:: + hexagon/emulation + hexagon/cdsp + diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 224fadae71c4..e6dcdb9d4161 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -29,3 +29,4 @@ Contents: target-sparc64 target-i386 target-xtensa + target-hexagon diff --git a/gdb-xml/hexagon-sys.xml b/gdb-xml/hexagon-sys.xml new file mode 100644 index 000000000000..70256c1d39c6 --- /dev/null +++ b/gdb-xml/hexagon-sys.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw/Kconfig b/hw/Kconfig index 1b4e9bb07f7d..b363d4561a50 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -65,6 +65,7 @@ source sparc/Kconfig source sparc64/Kconfig source tricore/Kconfig source xtensa/Kconfig +source hexagon/Kconfig # Symbols used by multiple targets config TEST_DEVICES diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig new file mode 100644 index 000000000000..f3f011573105 --- /dev/null +++ b/hw/hexagon/Kconfig @@ -0,0 +1,14 @@ +config HEX_DSP + bool + default y + depends on HEXAGON && TCG + imply PTIMER + select L2VIC # Vector PIC + +config HEX_VIRT + bool + default y + depends on HEX_DSP && FDT + select DEVICE_TREE + select VIRTIO_MMIO + select PL011 diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c new file mode 100644 index 000000000000..cd6ca36db791 --- /dev/null +++ b/hw/hexagon/hexagon_dsp.c @@ -0,0 +1,203 @@ +/* + * Hexagon DSP Subsystem emulation. This represents a generic DSP + * subsystem with few peripherals, like the Compute DSP. + * + * Copyright (c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "exec/address-spaces.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/hexagon/hexagon.h" +#include "hw/loader.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "elf.h" +#include "cpu.h" +#include "include/migration/cpu.h" +#include "include/system/system.h" +#include "target/hexagon/internal.h" +#include "system/reset.h" + +#include "machine_configs.h.inc" + +static hwaddr isdb_secure_flag; +static hwaddr isdb_trusted_flag; +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)) { + isdb_secure_flag = st_value; + } + if (!g_strcmp0("isdb_trusted_flag", st_name)) { + isdb_trusted_flag = st_value; + } +} + +/* Board init. */ +static struct hexagon_board_boot_info hexagon_binfo; + +static void hexagon_load_kernel(HexagonCPU *cpu) +{ + uint64_t pentry; + long kernel_size; + + 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); + + 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(MachineState *machine, HexagonCPU *cpu) +{ + if (machine->kernel_filename) { + hexagon_load_kernel(cpu); + uint32_t mem = 1; + if (isdb_secure_flag) { + cpu_physical_memory_write(isdb_secure_flag, &mem, sizeof(mem)); + } + if (isdb_trusted_flag) { + cpu_physical_memory_write(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, + hexagon_machine_config *m_cfg) +{ + 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; + + MemoryRegion *address_space = get_system_memory(); + + MemoryRegion *config_table_rom = g_new(MemoryRegion, 1); + memory_region_init_rom(config_table_rom, NULL, "config_table.rom", + sizeof(m_cfg->cfgtable), &error_fatal); + memory_region_add_subregion(address_space, m_cfg->cfgbase, + config_table_rom); + + MemoryRegion *sram = g_new(MemoryRegion, 1); + memory_region_init_ram(sram, NULL, "ddr.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(address_space, 0x0, sram); + + Error **errp = NULL; + + 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)); + qdev_prop_set_uint32(DEVICE(cpu), "l2vic-base-addr", m_cfg->l2vic_base); + qdev_prop_set_uint32(DEVICE(cpu), "qtimer-base-addr", m_cfg->qtmr_rg0); + qdev_prop_set_uint32(DEVICE(cpu), "config-table-addr", m_cfg->cfgbase); + + if (i == 0) { + hexagon_init_bootstrap(machine, cpu); + if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) { + return; + } + DeviceState *l2vic_dev; + l2vic_dev = sysbus_create_varargs("l2vic", m_cfg->l2vic_base, + /* IRQ#, Evnt#,CauseCode */ + qdev_get_gpio_in(DEVICE(cpu), 0), + qdev_get_gpio_in(DEVICE(cpu), 1), + qdev_get_gpio_in(DEVICE(cpu), 2), + qdev_get_gpio_in(DEVICE(cpu), 3), + qdev_get_gpio_in(DEVICE(cpu), 4), + qdev_get_gpio_in(DEVICE(cpu), 5), + qdev_get_gpio_in(DEVICE(cpu), 6), + qdev_get_gpio_in(DEVICE(cpu), 7), + NULL); + sysbus_mmio_map(SYS_BUS_DEVICE(l2vic_dev), 1, + m_cfg->cfgtable.fastl2vic_base << 16); + } else if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) { + return; + } + + } + + hexagon_config_table *config_table = &m_cfg->cfgtable; + + config_table->subsystem_base = HEXAGON_CFG_ADDR_BASE(m_cfg->csr_base); + + rom_add_blob_fixed_as("config_table.rom", config_table, + sizeof(*config_table), 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->no_sdcard = 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, 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 = MACHINE_TYPE_NAME("V66G_1024"), + .parent = TYPE_MACHINE, + .class_init = v66g_1024_init, + }, +}; + +DEFINE_TYPES(hexagon_machine_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 000000000000..83ac4fab3d1b --- /dev/null +++ b/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc @@ -0,0 +1,62 @@ + +static hexagon_machine_config G_GNUC_UNUSED SA8775P_cdsp0 = { + .cfgbase = 0x24000000 + 0x180000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0x26300000 + 0x90000, + .l2vic_size = 0x00001000, + .csr_base = 0x26300000, + .qtmr_rg0 = 0x26300000 + 0xA1000, + .qtmr_rg1 = 0x26300000 + 0xA2000, + .cfgtable = { + .l2tcm_base = 0x00002400, + .reserved0 = 0x00000000, + .subsystem_base = 0x26380000, + .etm_base = 0x00002419, + .l2cfg_base = 0x0000241a, + .reserved1 = 0x241b0000, + .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, + .reserved3 = 0x00000001, + .reserved4 = 0x00000000, + .reserved5 = 0x00000003, + .coproc2_cvt_mpy_size = 0x0000000a, + .axi3_lowaddr = 0x000000e0, + }, +}; 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 000000000000..b501cd8c1c31 --- /dev/null +++ b/hw/hexagon/machine_cfg_v68n_1024.h.inc @@ -0,0 +1,63 @@ + +static hexagon_machine_config G_GNUC_UNUSED v68n_1024 = { + .cfgbase = 0xde000000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0xfc910000, + .l2vic_size = 0x00001000, + .csr_base = 0xfc900000, + .qtmr_rg0 = 0xfc921000, + .qtmr_rg1 = 0xfc922000, + .cfgtable = { + .l2tcm_base = 0x0000d800, + .reserved0 = 0x00000000, + .subsystem_base = 0x00002000, + .etm_base = 0x0000d819, + .l2cfg_base = 0x0000d81a, + .reserved1 = 0x00000000, + .l1s0_base = 0x0000d840, + .axi2_lowaddr = 0x00003000, + .streamer_base = 0xd81c0000, + .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, + .reserved3 = 0x1f1f1f1f, + .reserved4 = 0x1f1f1f1f, + .reserved5 = 0x1f1f1f1f, + .coproc2_cvt_mpy_size = 0x1f1f1f1f, + .axi3_lowaddr = 0x1f1f1f1f, + }, +}; + diff --git a/hw/hexagon/machine_configs.h.inc b/hw/hexagon/machine_configs.h.inc new file mode 100644 index 000000000000..0465fb9fa433 --- /dev/null +++ b/hw/hexagon/machine_configs.h.inc @@ -0,0 +1,71 @@ +/* + * Hexagon Baseboard System emulation. + * + * Copyright (c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +static hexagon_machine_config v66g_1024 = { + .cfgbase = 0xd8180000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0xfc910000, + .l2vic_size = 0x00001000, + .csr_base = 0xfc900000, + .qtmr_rg0 = 0xfc921000, + .qtmr_rg1 = 0xfc922000, + .cfgtable = { + .l2tcm_base = 0x0000d800, + .reserved0 = 0x0000d400, + .subsystem_base = 0x00002000, + .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, + .axi3_lowaddr = 0x00000000, + }, +}; + +#include "machine_cfg_sa8775_cdsp0.h.inc" +#include "machine_cfg_v68n_1024.h.inc" diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build new file mode 100644 index 000000000000..649ad6dc02b3 --- /dev/null +++ b/hw/hexagon/meson.build @@ -0,0 +1,7 @@ +hexagon_ss = ss.source_set() +hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c',)) + +hw_arch += {'hexagon': hexagon_ss} + +hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c',)) + diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c new file mode 100644 index 000000000000..1d6ee4876b8c --- /dev/null +++ b/hw/hexagon/virt.c @@ -0,0 +1,428 @@ +/* + * Hexagon virt emulation + * + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/char/pl011.h" +#include "hw/core/sysbus-fdt.h" +#include "hw/hexagon/virt.h" +#include "hw/hexagon/hexagon.h" +#include "hw/loader.h" +#include "hw/qdev-properties.h" +#include "hw/register.h" +#include "hw/timer/qct-qtimer.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/units.h" +#include "system/device_tree.h" +#include "system/reset.h" +#include "system/system.h" +#include "elf.h" +#include "machine_cfg_v68n_1024.h.inc" +#include + +static const int VIRTIO_DEV_COUNT = 2; + +static const MemMapEntry base_memmap[] = { + + [VIRT_UART0] = { 0x10000000, 0x00000200 }, + [VIRT_MMIO] = { 0x11000000, 0x00000100 }, + [VIRT_GPT] = { 0xab000000, 0x00001000 }, + [VIRT_FDT] = { 0x99900000, 0x00000200 }, +}; + +static const int irqmap[] = { + [VIRT_MMIO] = 8, /* ...to 8 + VIRTIO_DEV_COUNT - 1 */ + [VIRT_GPT] = 12, + [VIRT_UART0] = 50, +}; + + +static void create_fdt(HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + void *fdt = create_device_tree(&vms->fdt_size); + + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + ms->fdt = fdt; + + qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,hexagon-virt"); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1); + qemu_fdt_setprop_string(fdt, "/", "model", "linux,hexagon-virt"); + + 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"); + + uint8_t rng_seed[32]; + 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 hexagon_machine_config *m_cfg, Error **errp) +{ + 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, + errp); + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base, + &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, 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 irq_hvm_ic_phandle = -1; +static void fdt_add_hvm_pic_node(HexagonVirtMachineState *vms, + const hexagon_machine_config *m_cfg) +{ + MachineState *ms = MACHINE(vms); + irq_hvm_ic_phandle = qemu_fdt_alloc_phandle(ms->fdt); + + qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent", + irq_hvm_ic_phandle); + + qemu_fdt_add_subnode(ms->fdt, "/soc/interrupt-controller"); + qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller", + "#address-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller", + "#interrupt-cells", 2); + qemu_fdt_setprop_string(ms->fdt, "/soc/interrupt-controller", "compatible", + "qcom,h2-pic,hvm-pic"); + qemu_fdt_setprop(ms->fdt, "/soc/interrupt-controller", + "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, "/soc/interrupt-controller", "phandle", + irq_hvm_ic_phandle); + + sysbus_mmio_map(SYS_BUS_DEVICE(vms->l2vic), 1, + m_cfg->cfgtable.fastl2vic_base); +} + + +static void fdt_add_gpt_node(HexagonVirtMachineState *vms) +{ + g_autofree char *name = NULL; + MachineState *ms = MACHINE(vms); + + name = g_strdup_printf("/soc/gpt@%" PRIx64, + (int64_t) base_memmap[VIRT_GPT].base); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", + "qcom,h2-timer,hvm-timer"); + qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqmap[VIRT_GPT], 0); + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, + base_memmap[VIRT_GPT].base, + base_memmap[VIRT_GPT].size); +} + +static int32_t clock_phandle = -1; +static void fdt_add_clocks(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + clock_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", clock_phandle); +} + +static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart) +{ + char *nodename; + hwaddr base = base_memmap[uart].base; + hwaddr size = base_memmap[uart].size; + assert(uart == 0); + int irq = irqmap[VIRT_UART0 + uart]; + const char compat[] = "arm,pl011\0arm,primecell"; + const char clocknames[] = "uartclk\0apb_pclk"; + MachineState *ms = MACHINE(vms); + + pl011_create(base, qdev_get_gpio_in(vms->l2vic, irq), serial_hd(0)); + + 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, "interrupts", irq, 0); + qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clock_phandle, + clock_phandle); + qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, + sizeof(clocknames)); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + irq_hvm_ic_phandle); + + 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 fdt_add_virtio_devices(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + /* VirtIO MMIO devices */ + for (int i = 0; i < VIRTIO_DEV_COUNT; i++) { + char *nodename; + int irq = irqmap[VIRT_MMIO] + i; + size_t size = base_memmap[VIRT_MMIO].size; + hwaddr base = base_memmap[VIRT_MMIO].base + i * size; + + nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "virtio,mmio"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 1, + size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + irq_hvm_ic_phandle); + + sysbus_create_simple( + "virtio-mmio", base + i * size, + qdev_get_gpio_in(vms->l2vic, irqmap[VIRT_MMIO] + i)); + + g_free(nodename); + } +} + +static void create_qtimer(HexagonVirtMachineState *vms, + const hexagon_machine_config *m_cfg) +{ + Error **errp = NULL; + QCTQtimerState *qtimer = QCT_QTIMER(qdev_new(TYPE_QCT_QTIMER)); + + object_property_set_uint(OBJECT(qtimer), "nr_frames", 2, errp); + object_property_set_uint(OBJECT(qtimer), "nr_views", 1, errp); + object_property_set_uint(OBJECT(qtimer), "cnttid", 0x111, errp); + sysbus_realize_and_unref(SYS_BUS_DEVICE(qtimer), errp); + + + unsigned QTMR0_IRQ = 3; + sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 0, 0xfab20000); + sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 1, m_cfg->qtmr_rg0); + sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), 0, + qdev_get_gpio_in(vms->l2vic, QTMR0_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), 1, + qdev_get_gpio_in(vms->l2vic, 4)); +} + +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; + + /* copy in the device tree */ + qemu_fdt_dumpdtb(ms->fdt, fdtsize); + + 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, NULL, &entry, 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 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); + Error **errp = NULL; + const hexagon_machine_config *m_cfg = &v68n_1024; + + qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline); + + vms->sys = get_system_memory(); + + memory_region_init_ram(&vms->ram, NULL, "ddr.ram", ms->ram_size, errp); + memory_region_add_subregion(vms->sys, 0x0, &vms->ram); + + if (m_cfg->l2tcm_size) { + memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size, + errp); + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base, + &vms->tcm); + } + + memory_region_init_rom(&vms->cfgtable, NULL, "config_table.rom", + sizeof(m_cfg->cfgtable), errp); + memory_region_add_subregion(vms->sys, m_cfg->cfgbase, &vms->cfgtable); + fdt_add_hvx(vms, m_cfg, errp); + const char *cpu_model = ms->cpu_type; + + if (!cpu_model) { + cpu_model = HEXAGON_CPU_TYPE_NAME("v73"); + } + + HexagonCPU *cpu_0 = 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) { + cpu_0 = cpu; + if (ms->kernel_filename) { + uint64_t entry = load_kernel(vms); + + qdev_prop_set_uint32(DEVICE(cpu_0), "exec-start-addr", entry); + } + } + qdev_prop_set_uint32(DEVICE(cpu), "l2vic-base-addr", m_cfg->l2vic_base); + qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); + qdev_prop_set_uint32(DEVICE(cpu), "hvx-contexts", + m_cfg->cfgtable.ext_contexts); + qdev_prop_set_uint32(DEVICE(cpu), "num-tlbs", + m_cfg->cfgtable.jtlb_size_entries); + + if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) { + return; + } + } + vms->l2vic = sysbus_create_varargs( + "l2vic", m_cfg->l2vic_base, qdev_get_gpio_in(DEVICE(cpu_0), 0), + qdev_get_gpio_in(DEVICE(cpu_0), 1), qdev_get_gpio_in(DEVICE(cpu_0), 2), + qdev_get_gpio_in(DEVICE(cpu_0), 3), qdev_get_gpio_in(DEVICE(cpu_0), 4), + qdev_get_gpio_in(DEVICE(cpu_0), 5), qdev_get_gpio_in(DEVICE(cpu_0), 6), + qdev_get_gpio_in(DEVICE(cpu_0), 7), NULL); + + fdt_add_hvm_pic_node(vms, m_cfg); + fdt_add_virtio_devices(vms); + fdt_add_cpu_nodes(vms); + fdt_add_clocks(vms); + fdt_add_uart(vms, 0); + fdt_add_gpt_node(vms); + create_qtimer(vms, m_cfg); + + hexagon_config_table *config_table = + (hexagon_config_table *)&m_cfg->cfgtable; + + config_table->l2tcm_base = + HEXAGON_CFG_ADDR_BASE(m_cfg->cfgtable.l2tcm_base); + config_table->subsystem_base = HEXAGON_CFG_ADDR_BASE(m_cfg->csr_base); + config_table->vtcm_base = HEXAGON_CFG_ADDR_BASE(m_cfg->cfgtable.vtcm_base); + config_table->l2cfg_base = + HEXAGON_CFG_ADDR_BASE(m_cfg->cfgtable.l2cfg_base); + config_table->fastl2vic_base = + HEXAGON_CFG_ADDR_BASE(m_cfg->cfgtable.fastl2vic_base); + + 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, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = virt_init; + mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v73"); + mc->default_ram_size = 4 * GiB; + mc->max_cpus = HEXAGON_MAX_CPUS; + mc->default_cpus = 6; + 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_MACHINE, + .instance_size = sizeof(HexagonVirtMachineState), + .class_init = virt_class_init, + .instance_init = virt_instance_init, +} }; + +DEFINE_TYPES(virt_machine_types) diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index dd405bdb5d21..471e02df27cf 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -8,6 +8,9 @@ config I8259 config PL190 bool +config L2VIC + bool + config IOAPIC bool select I8259 diff --git a/hw/intc/l2vic.c b/hw/intc/l2vic.c new file mode 100644 index 000000000000..7926ae5d8fa9 --- /dev/null +++ b/hw/intc/l2vic.c @@ -0,0 +1,417 @@ +/* + * QEMU L2VIC Interrupt Controller + * + * Arm PrimeCell PL190 Vector Interrupt Controller was used as a reference. + * Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "l2vic.h" +#include "trace.h" + +#define L2VICA(s, n) (s[(n) >> 2]) + +#define TYPE_L2VIC "l2vic" +#define L2VIC(obj) OBJECT_CHECK(L2VICState, (obj), TYPE_L2VIC) + +#define SLICE_MAX (L2VIC_INTERRUPT_MAX / 32) + +typedef struct L2VICState { + SysBusDevice parent_obj; + + QemuMutex active; + MemoryRegion iomem; + MemoryRegion fast_iomem; + uint32_t level; + /* + * offset 0:vid group 0 etc, 10 bits in each group + * are used: + */ + uint32_t vid_group[4]; + uint32_t vid0; + /* Clear Status of Active Edge interrupt, not used: */ + uint32_t int_clear[SLICE_MAX] QEMU_ALIGNED(16); + /* Enable interrupt source */ + uint32_t int_enable[SLICE_MAX] QEMU_ALIGNED(16); + /* Clear (set to 0) corresponding bit in int_enable */ + uint32_t int_enable_clear; + /* Set (to 1) corresponding bit in int_enable */ + uint32_t int_enable_set; + /* Present for debugging, not used */ + uint32_t int_pending[SLICE_MAX] QEMU_ALIGNED(16); + /* Generate an interrupt */ + uint32_t int_soft; + /* Which enabled interrupt is active */ + uint32_t int_status[SLICE_MAX] QEMU_ALIGNED(16); + /* Edge or Level interrupt */ + uint32_t int_type[SLICE_MAX] QEMU_ALIGNED(16); + /* L2 interrupt group 0-3 0x600-0x7FF */ + uint32_t int_group_n0[SLICE_MAX] QEMU_ALIGNED(16); + uint32_t int_group_n1[SLICE_MAX] QEMU_ALIGNED(16); + uint32_t int_group_n2[SLICE_MAX] QEMU_ALIGNED(16); + uint32_t int_group_n3[SLICE_MAX] QEMU_ALIGNED(16); + qemu_irq irq[8]; +} L2VICState; + + +/* + * Find out if this irq is associated with a group other than + * the default group + */ +static uint32_t *get_int_group(L2VICState *s, int irq) +{ + int n = irq & 0x1f; + if (n < 8) { + return s->int_group_n0; + } + if (n < 16) { + return s->int_group_n1; + } + if (n < 24) { + return s->int_group_n2; + } + return s->int_group_n3; +} + +static int find_slice(int irq) +{ + return irq / 32; +} + +static int get_vid(L2VICState *s, int irq) +{ + uint32_t *group = get_int_group(s, irq); + uint32_t slice = group[find_slice(irq)]; + /* Mask with 0x7 to remove the GRP:EN bit */ + uint32_t val = slice >> ((irq & 0x7) * 4); + if (val & 0x8) { + return val & 0x7; + } else { + return 0; + } +} + +static inline bool vid_active(L2VICState *s) + +{ + /* scan all 1024 bits in int_status arrary */ + const int size = sizeof(s->int_status) * CHAR_BIT; + const int active_irq = find_first_bit((unsigned long *)s->int_status, size); + return ((active_irq != size)) ? true : false; +} + +static bool l2vic_update(L2VICState *s, int irq) +{ + if (vid_active(s)) { + return true; + } + + bool pending = test_bit(irq, (unsigned long *)s->int_pending); + bool enable = test_bit(irq, (unsigned long *)s->int_enable); + if (pending && enable) { + int vid = get_vid(s, irq); + set_bit(irq, (unsigned long *)s->int_status); + clear_bit(irq, (unsigned long *)s->int_pending); + clear_bit(irq, (unsigned long *)s->int_enable); + /* ensure the irq line goes low after going high */ + s->vid0 = irq; + s->vid_group[get_vid(s, irq)] = irq; + + /* already low: now call pulse */ + /* pulse: calls qemu_upper() and then qemu_lower()) */ + qemu_irq_pulse(s->irq[vid + 2]); + trace_l2vic_delivered(irq, vid); + return true; + } + return false; +} + +static void l2vic_update_all(L2VICState *s) +{ + for (int i = 0; i < L2VIC_INTERRUPT_MAX; i++) { + if (l2vic_update(s, i) == true) { + /* once vid is active, no-one else can set it until ciad */ + return; + } + } +} + +static void l2vic_set_irq(void *opaque, int irq, int level) +{ + L2VICState *s = (L2VICState *)opaque; + if (level) { + qemu_mutex_lock(&s->active); + set_bit(irq, (unsigned long *)s->int_pending); + qemu_mutex_unlock(&s->active); + } + l2vic_update(s, irq); +} + +static void l2vic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + L2VICState *s = (L2VICState *)opaque; + qemu_mutex_lock(&s->active); + trace_l2vic_reg_write((unsigned)offset, (uint32_t)val); + + if (offset == L2VIC_VID_0) { + if ((int)val != L2VIC_CIAD_INSTRUCTION) { + s->vid0 = val; + } else { + /* ciad issued: clear int_status */ + clear_bit(s->vid0, (unsigned long *)s->int_status); + } + } else if (offset >= L2VIC_INT_ENABLEn && + offset < (L2VIC_INT_ENABLE_CLEARn)) { + L2VICA(s->int_enable, offset - L2VIC_INT_ENABLEn) = val; + } else if (offset >= L2VIC_INT_ENABLE_CLEARn && + offset < L2VIC_INT_ENABLE_SETn) { + L2VICA(s->int_enable, offset - L2VIC_INT_ENABLE_CLEARn) &= ~val; + } else if (offset >= L2VIC_INT_ENABLE_SETn && offset < L2VIC_INT_TYPEn) { + L2VICA(s->int_enable, offset - L2VIC_INT_ENABLE_SETn) |= val; + } else if (offset >= L2VIC_INT_TYPEn && offset < L2VIC_INT_TYPEn + 0x80) { + L2VICA(s->int_type, offset - L2VIC_INT_TYPEn) = val; + } else if (offset >= L2VIC_INT_STATUSn && offset < L2VIC_INT_CLEARn) { + L2VICA(s->int_status, offset - L2VIC_INT_STATUSn) = val; + } else if (offset >= L2VIC_INT_CLEARn && offset < L2VIC_SOFT_INTn) { + L2VICA(s->int_clear, offset - L2VIC_INT_CLEARn) = val; + } else if (offset >= L2VIC_INT_PENDINGn && + offset < L2VIC_INT_PENDINGn + 0x80) { + L2VICA(s->int_pending, offset - L2VIC_INT_PENDINGn) = val; + } else if (offset >= L2VIC_SOFT_INTn && offset < L2VIC_INT_PENDINGn) { + L2VICA(s->int_enable, offset - L2VIC_SOFT_INTn) |= val; + /* + * Need to reverse engineer the actual irq number. + */ + int irq = find_first_bit((unsigned long *)&val, + sizeof(s->int_enable[0]) * CHAR_BIT); + hwaddr byteoffset = offset - L2VIC_SOFT_INTn; + g_assert(irq != sizeof(s->int_enable[0]) * CHAR_BIT); + irq += byteoffset * 8; + + /* The soft-int interface only works with edge-triggered interrupts */ + if (test_bit(irq, (unsigned long *)s->int_type)) { + qemu_mutex_unlock(&s->active); + l2vic_set_irq(opaque, irq, 1); + qemu_mutex_lock(&s->active); + } + } else if (offset >= L2VIC_INT_GRPn_0 && offset < L2VIC_INT_GRPn_1) { + L2VICA(s->int_group_n0, offset - L2VIC_INT_GRPn_0) = val; + } else if (offset >= L2VIC_INT_GRPn_1 && offset < L2VIC_INT_GRPn_2) { + L2VICA(s->int_group_n1, offset - L2VIC_INT_GRPn_1) = val; + } else if (offset >= L2VIC_INT_GRPn_2 && offset < L2VIC_INT_GRPn_3) { + L2VICA(s->int_group_n2, offset - L2VIC_INT_GRPn_2) = val; + } else if (offset >= L2VIC_INT_GRPn_3 && offset < L2VIC_INT_GRPn_3 + 0x80) { + L2VICA(s->int_group_n3, offset - L2VIC_INT_GRPn_3) = val; + } else { + qemu_log_mask(LOG_UNIMP, "%s: offset %x unimplemented\n", __func__, + (int)offset); + } + l2vic_update_all(s); + qemu_mutex_unlock(&s->active); + return; +} + +static uint64_t l2vic_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t value; + L2VICState *s = (L2VICState *)opaque; + qemu_mutex_lock(&s->active); + + if (offset == L2VIC_VID_GRP_0) { + value = s->vid_group[0]; + } else if (offset == L2VIC_VID_GRP_1) { + value = s->vid_group[1]; + } else if (offset == L2VIC_VID_GRP_2) { + value = s->vid_group[2]; + } else if (offset == L2VIC_VID_GRP_3) { + value = s->vid_group[3]; + } else if (offset == L2VIC_VID_0) { + value = s->vid0; + } else if (offset >= L2VIC_INT_ENABLEn && + offset < L2VIC_INT_ENABLE_CLEARn) { + value = L2VICA(s->int_enable, offset - L2VIC_INT_ENABLEn); + } else if (offset >= L2VIC_INT_ENABLE_CLEARn && + offset < L2VIC_INT_ENABLE_SETn) { + value = 0; + } else if (offset >= L2VIC_INT_ENABLE_SETn && offset < L2VIC_INT_TYPEn) { + value = 0; + } else if (offset >= L2VIC_INT_TYPEn && offset < L2VIC_INT_TYPEn + 0x80) { + value = L2VICA(s->int_type, offset - L2VIC_INT_TYPEn); + } else if (offset >= L2VIC_INT_STATUSn && offset < L2VIC_INT_CLEARn) { + value = L2VICA(s->int_status, offset - L2VIC_INT_STATUSn); + } else if (offset >= L2VIC_INT_CLEARn && offset < L2VIC_SOFT_INTn) { + value = L2VICA(s->int_clear, offset - L2VIC_INT_CLEARn); + } else if (offset >= L2VIC_SOFT_INTn && offset < L2VIC_INT_PENDINGn) { + value = 0; + } else if (offset >= L2VIC_INT_PENDINGn && + offset < L2VIC_INT_PENDINGn + 0x80) { + value = L2VICA(s->int_pending, offset - L2VIC_INT_PENDINGn); + } else if (offset >= L2VIC_INT_GRPn_0 && offset < L2VIC_INT_GRPn_1) { + value = L2VICA(s->int_group_n0, offset - L2VIC_INT_GRPn_0); + } else if (offset >= L2VIC_INT_GRPn_1 && offset < L2VIC_INT_GRPn_2) { + value = L2VICA(s->int_group_n1, offset - L2VIC_INT_GRPn_1); + } else if (offset >= L2VIC_INT_GRPn_2 && offset < L2VIC_INT_GRPn_3) { + value = L2VICA(s->int_group_n2, offset - L2VIC_INT_GRPn_2); + } else if (offset >= L2VIC_INT_GRPn_3 && offset < L2VIC_INT_GRPn_3 + 0x80) { + value = L2VICA(s->int_group_n3, offset - L2VIC_INT_GRPn_3); + } else { + value = 0; + qemu_log_mask(LOG_GUEST_ERROR, "L2VIC: %s: offset 0x%x\n", __func__, + (int)offset); + } + + trace_l2vic_reg_read((unsigned)offset, value); + qemu_mutex_unlock(&s->active); + + return value; +} + +static const MemoryRegionOps l2vic_ops = { + .read = l2vic_read, + .write = l2vic_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +#define FASTL2VIC_ENABLE 0x0 +#define FASTL2VIC_DISABLE 0x1 +#define FASTL2VIC_INT 0x2 + +static void fastl2vic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + if (offset == 0) { + uint32_t cmd = (val >> 16) & 0x3; + uint32_t irq = val & 0x3ff; + uint32_t slice = (irq / 32) * 4; + val = 1 << (irq % 32); + + if (cmd == FASTL2VIC_ENABLE) { + l2vic_write(opaque, L2VIC_INT_ENABLE_SETn + slice, val, size); + } else if (cmd == FASTL2VIC_DISABLE) { + l2vic_write(opaque, L2VIC_INT_ENABLE_CLEARn + slice, val, size); + } else if (cmd == FASTL2VIC_INT) { + l2vic_write(opaque, L2VIC_SOFT_INTn + slice, val, size); + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write cmd %d\n", __func__, + cmd); + return; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write offset 0x%08x\n", + __func__, (unsigned int)offset); +} + +static const MemoryRegionOps fastl2vic_ops = { + .write = fastl2vic_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void l2vic_reset_hold(Object *obj, ResetType) +{ + L2VICState *s = L2VIC(obj); + memset(s->int_clear, 0, sizeof(s->int_clear)); + memset(s->int_enable, 0, sizeof(s->int_enable)); + memset(s->int_pending, 0, sizeof(s->int_pending)); + memset(s->int_status, 0, sizeof(s->int_status)); + memset(s->int_type, 0, sizeof(s->int_type)); + memset(s->int_group_n0, 0, sizeof(s->int_group_n0)); + memset(s->int_group_n1, 0, sizeof(s->int_group_n1)); + memset(s->int_group_n2, 0, sizeof(s->int_group_n2)); + memset(s->int_group_n3, 0, sizeof(s->int_group_n3)); + s->int_soft = 0; + s->vid0 = 0; + + l2vic_update_all(s); +} + + +static void reset_irq_handler(void *opaque, int irq, int level) +{ + L2VICState *s = (L2VICState *)opaque; + Object *obj = OBJECT(opaque); + if (level) { + l2vic_reset_hold(obj, RESET_TYPE_COLD); + } + l2vic_update_all(s); +} + +static void l2vic_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + L2VICState *s = L2VIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &l2vic_ops, s, "l2vic", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + memory_region_init_io(&s->fast_iomem, obj, &fastl2vic_ops, s, "fast", + 0x10000); + sysbus_init_mmio(sbd, &s->fast_iomem); + + qdev_init_gpio_in(dev, l2vic_set_irq, L2VIC_INTERRUPT_MAX); + qdev_init_gpio_in_named(dev, reset_irq_handler, "reset", 1); + for (i = 0; i < 8; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } + qemu_mutex_init(&s->active); /* TODO: Remove this is an experiment */ +} + +static const VMStateDescription vmstate_l2vic = { + .name = "l2vic", + .version_id = 1, + .minimum_version_id = 1, + .fields = + (VMStateField[]){ + VMSTATE_UINT32(level, L2VICState), + VMSTATE_UINT32_ARRAY(vid_group, L2VICState, 4), + VMSTATE_UINT32(vid0, L2VICState), + VMSTATE_UINT32_ARRAY(int_enable, L2VICState, SLICE_MAX), + VMSTATE_UINT32(int_enable_clear, L2VICState), + VMSTATE_UINT32(int_enable_set, L2VICState), + VMSTATE_UINT32_ARRAY(int_type, L2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_status, L2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_clear, L2VICState, SLICE_MAX), + VMSTATE_UINT32(int_soft, L2VICState), + VMSTATE_UINT32_ARRAY(int_pending, L2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_group_n0, L2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_group_n1, L2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_group_n2, L2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_group_n3, L2VICState, SLICE_MAX), + VMSTATE_END_OF_LIST() } +}; + +static void l2vic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_l2vic; + rc->phases.hold = l2vic_reset_hold; +} + +static const TypeInfo l2vic_info = { + .name = TYPE_L2VIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(L2VICState), + .instance_init = l2vic_init, + .class_init = l2vic_class_init, +}; + +static void l2vic_register_types(void) +{ + type_register_static(&l2vic_info); +} + +type_init(l2vic_register_types) diff --git a/hw/intc/l2vic.h b/hw/intc/l2vic.h new file mode 100644 index 000000000000..5a85a539a982 --- /dev/null +++ b/hw/intc/l2vic.h @@ -0,0 +1,37 @@ +/* + * QEMU L2VIC Interrupt Controller + * + * Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define L2VIC_VID_GRP_0 0x0 /* Read */ +#define L2VIC_VID_GRP_1 0x4 /* Read */ +#define L2VIC_VID_GRP_2 0x8 /* Read */ +#define L2VIC_VID_GRP_3 0xC /* Read */ +#define L2VIC_VID_0 0x10 /* Read SOFTWARE DEFINED */ +#define L2VIC_VID_1 0x14 /* Read SOFTWARE DEFINED NOT YET USED */ +#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */ +#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */ +#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */ +#define L2VIC_INT_TYPEn 0x280 /* Read/Write */ +#define L2VIC_INT_STATUSn 0x380 /* Read */ +#define L2VIC_INT_CLEARn 0x400 /* Write */ +#define L2VIC_SOFT_INTn 0x480 /* Write */ +#define L2VIC_INT_PENDINGn 0x500 /* Read */ +#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */ +#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */ +#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */ +#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */ + +#define L2VIC_INTERRUPT_MAX 1024 +#define L2VIC_CIAD_INSTRUCTION -1 +/* + * Note about l2vic groups: + * Each interrupt to L2VIC can be configured to associate with one of + * four groups. + * Group 0 interrupts go to IRQ2 via VID 0 (SSR: 0xC2, the default) + * Group 1 interrupts go to IRQ3 via VID 1 (SSR: 0xC3) + * Group 2 interrupts go to IRQ4 via VID 2 (SSR: 0xC4) + * Group 3 interrupts go to IRQ5 via VID 3 (SSR: 0xC5) + */ diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 510fdfb68865..919abe5eec28 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -67,6 +67,8 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) + +specific_ss.add(when: 'CONFIG_L2VIC', if_true: files('l2vic.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 3dcf14719833..bc66260fc0cb 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -303,6 +303,10 @@ sh_intc_register(const char *s, int id, unsigned short v, int c, int m) "%s %u - sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " -> 0x%lx" sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx" sh_intc_set(int id, int enable) "setting interrupt group %d to %d" +# l2vic.c +l2vic_reg_write(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32 +l2vic_reg_read(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32 +l2vic_delivered(int irq, int vid) "l2vic: delivered %d (vid %d)" # loongson_ipi.c loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 diff --git a/hw/meson.build b/hw/meson.build index b827c82c5d7b..91969d6fec18 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -64,3 +64,4 @@ subdir('sparc') subdir('sparc64') subdir('tricore') subdir('xtensa') +subdir('hexagon') diff --git a/hw/timer/meson.build b/hw/timer/meson.build index f5f9eed2d0a9..6c30bf602226 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -34,3 +34,5 @@ specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c')) system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c')) + +specific_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('qct-qtimer.c')) diff --git a/hw/timer/qct-qtimer.c b/hw/timer/qct-qtimer.c new file mode 100644 index 000000000000..413f7249eef0 --- /dev/null +++ b/hw/timer/qct-qtimer.c @@ -0,0 +1,519 @@ +/* + * Qualcomm QCT QTimer + * + * Copyright(c) 2019-2025 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/timer/qct-qtimer.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" + +/* Common timer implementation. */ + +#define QTIMER_MEM_SIZE_BYTES 0x1000 +#define QTIMER_MEM_REGION_SIZE_BYTES 0x1000 +#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL +#define QTMR_TIMER_INDEX_MASK (0xf000) +#define HIGH_32(val) (0x0ffffffffULL & (val >> 32)) +#define LOW_32(val) (0x0ffffffffULL & val) + +/* + * QTimer version reg: + * + * 3 2 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Major | Minor | Step | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static unsigned int TIMER_VERSION = 0x20020000; + +/* + * qct_qtimer_read/write: + * if offset < 0x1000 read restricted registers: + * QCT_QTIMER_AC_CNTFREQ/CNTSR/CNTTID/CNTACR/CNTOFF_(LO/HI)/QCT_QTIMER_VERSION + */ +static uint64_t qct_qtimer_read(void *opaque, hwaddr offset, unsigned size) +{ + QCTQtimerState *s = (QCTQtimerState *)opaque; + uint32_t frame = 0; + + switch (offset) { + case QCT_QTIMER_AC_CNTFRQ: + return s->freq; + case QCT_QTIMER_AC_CNTSR: + return s->secure; + case QCT_QTIMER_AC_CNTTID: + return s->cnttid; + case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END: + frame = (offset - 0x40) / 0x4; + if (frame >= s->nr_frames) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: QCT_QTIMER_AC_CNT: Bad offset %x\n", __func__, + (int)offset); + return 0x0; + } + return s->timer[frame].cnt_ctrl; + case QCT_QTIMER_VERSION: + return TIMER_VERSION; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: QCT_QTIMER_AC_CNT: Bad offset %x\n", + __func__, (int)offset); + return 0x0; + } + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%x\n", __func__, + (int)offset); + return 0; +} + +static void qct_qtimer_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + QCTQtimerState *s = (QCTQtimerState *)opaque; + uint32_t frame = 0; + + if (offset < 0x1000) { + switch (offset) { + case QCT_QTIMER_AC_CNTFRQ: + s->freq = value; + return; + case QCT_QTIMER_AC_CNTSR: + if (value > 0xFF) + qemu_log_mask(LOG_GUEST_ERROR, + "%s: QCT_QTIMER_AC_CNTSR: Bad value %x\n", + __func__, (int)value); + else + s->secure = value; + return; + case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END: + frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 0x4; + if (frame >= s->nr_frames) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: QCT_QTIMER_AC_CNT: Bad offset %x\n", + __func__, (int)offset); + return; + } + s->timer[frame].cnt_ctrl = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: QCT_QTIMER_AC_CNT: Bad offset %x\n", __func__, + (int)offset); + return; + } + } else + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n", __func__, + (int)offset); +} + +static const MemoryRegionOps qct_qtimer_ops = { + .read = qct_qtimer_read, + .write = qct_qtimer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_qct_qtimer = { + .name = "qct-qtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]){ VMSTATE_END_OF_LIST() } +}; + +static void qct_qtimer_init(Object *obj) +{ + QCTQtimerState *s = QCT_QTIMER(obj); + + object_property_add_uint32_ptr(obj, "secure", &s->secure, + OBJ_PROP_FLAG_READ); + object_property_add_uint32_ptr(obj, "frame_id", &s->frame_id, + OBJ_PROP_FLAG_READ); +} + +static void hex_timer_update(QCTHextimerState *s) +{ + /* Update interrupts. */ + int level = s->int_level && (s->control & QCT_QTIMER_CNTP_CTL_ENABLE); + qemu_set_irq(s->irq, level); +} + +static MemTxResult hex_timer_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + QCTQtimerState *qct_s = (QCTQtimerState *)opaque; + uint32_t slot_nr = (offset & 0xF000) >> 12; + uint32_t reg_offset = offset & 0xFFF; + uint32_t view = slot_nr % qct_s->nr_views; + uint32_t frame = slot_nr / qct_s->nr_views; + + if (frame >= qct_s->nr_frames) { + *data = 0; + return MEMTX_ACCESS_ERROR; + } + QCTHextimerState *s = &qct_s->timer[frame]; + + + /* + * This is the case where we have 2 views, but the second one is not + * implemented. + */ + if (view && !(qct_s->cnttid & (0x4 << (frame * 4)))) { + *data = 0; + return MEMTX_OK; + } + + switch (reg_offset) { + case (QCT_QTIMER_CNT_FREQ): /* Ticks/Second */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RFRQ)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !((s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0PCTEN) || + (s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0VCTEN))) { + return MEMTX_ACCESS_ERROR; + } + + *data = s->freq; + return MEMTX_OK; + case (QCT_QTIMER_CNTP_CVAL_LO): /* TimerLoad */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + *data = LOW_32((s->cntval)); + return MEMTX_OK; + case (QCT_QTIMER_CNTP_CVAL_HI): /* TimerLoad */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + *data = HIGH_32((s->cntval)); + return MEMTX_OK; + case QCT_QTIMER_CNTPCT_LO: + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RPCT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0PCTEN)) { + return MEMTX_ACCESS_ERROR; + } + + *data = LOW_32((s->cntpct + (ptimer_get_count(s->timer)))); + return MEMTX_OK; + case QCT_QTIMER_CNTPCT_HI: + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RPCT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0PCTEN)) { + return MEMTX_ACCESS_ERROR; + } + + *data = HIGH_32((s->cntpct + (ptimer_get_count(s->timer)))); + return MEMTX_OK; + case (QCT_QTIMER_CNTP_TVAL): /* CVAL - CNTP */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + *data = + (s->cntval - (HIGH_32((s->cntpct + (ptimer_get_count(s->timer)))) + + LOW_32((s->cntpct + (ptimer_get_count(s->timer)))))); + return MEMTX_OK; + case (QCT_QTIMER_CNTP_CTL): /* TimerMIS */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + *data = s->int_level; + return MEMTX_OK; + case QCT_QTIMER_CNTPL0ACR: + if (view) { + *data = 0; + } else { + *data = s->cntpl0acr; + } + return MEMTX_OK; + + case QCT_QTIMER_VERSION: + *data = TIMER_VERSION; + return MEMTX_OK; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n", __func__, + (int)offset); + *data = 0; + return MEMTX_ACCESS_ERROR; + } +} + +/* + * Reset the timer limit after settings have changed. + * May only be called from inside a ptimer transaction block. + */ +static void hex_timer_recalibrate(QCTHextimerState *s, int reload) +{ + uint64_t limit; + /* Periodic. */ + limit = s->limit; + ptimer_set_limit(s->timer, limit, reload); +} + +static MemTxResult hex_timer_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + QCTQtimerState *qct_s = (QCTQtimerState *)opaque; + uint32_t slot_nr = (offset & 0xF000) >> 12; + uint32_t reg_offset = offset & 0xFFF; + uint32_t view = slot_nr % qct_s->nr_views; + uint32_t frame = slot_nr / qct_s->nr_views; + + if (frame >= qct_s->nr_frames) { + return MEMTX_ACCESS_ERROR; + } + QCTHextimerState *s = &qct_s->timer[frame]; + + /* + * This is the case where we have 2 views, but the second one is not + * implemented. + */ + if (view && !(qct_s->cnttid & (0x4 << (frame * 4)))) { + return MEMTX_OK; + } + + switch (reg_offset) { + case (QCT_QTIMER_CNTP_CVAL_LO): /* TimerLoad */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + + s->int_level = 0; + s->cntval = value; + ptimer_transaction_begin(s->timer); + if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) { + /* + * Pause the timer if it is running. This may cause some + * inaccuracy due to rounding, but avoids other issues. + */ + ptimer_stop(s->timer); + } + hex_timer_recalibrate(s, 1); + if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) { + ptimer_run(s->timer, 0); + } + ptimer_transaction_commit(s->timer); + break; + case (QCT_QTIMER_CNTP_CVAL_HI): + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + break; + case (QCT_QTIMER_CNTP_CTL): /* Timer control register */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + ptimer_transaction_begin(s->timer); + if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) { + /* + * Pause the timer if it is running. This may cause some + * inaccuracy due to rounding, but avoids other issues. + */ + ptimer_stop(s->timer); + } + s->control = value; + hex_timer_recalibrate(s, s->control & QCT_QTIMER_CNTP_CTL_ENABLE); + ptimer_set_freq(s->timer, s->freq); + ptimer_set_period(s->timer, 1); + if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) { + ptimer_run(s->timer, 0); + } + ptimer_transaction_commit(s->timer); + break; + case (QCT_QTIMER_CNTP_TVAL): /* CVAL - CNTP */ + if (!(s->cnt_ctrl & QCT_QTIMER_AC_CNTACR_RWPT)) { + return MEMTX_ACCESS_ERROR; + } + + if (view && !(s->cntpl0acr & QCT_QTIMER_CNTPL0ACR_PL0CTEN)) { + return MEMTX_ACCESS_ERROR; + } + + ptimer_transaction_begin(s->timer); + if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) { + /* + * Pause the timer if it is running. This may cause some + * inaccuracy due to rounding, but avoids other issues. + */ + ptimer_stop(s->timer); + } + s->cntval = s->cntpct + value; + ptimer_set_freq(s->timer, s->freq); + ptimer_set_period(s->timer, 1); + if (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) { + ptimer_run(s->timer, 0); + } + ptimer_transaction_commit(s->timer); + break; + case QCT_QTIMER_CNTPL0ACR: + if (view) { + break; + } + + s->cntpl0acr = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n", __func__, + (int)offset); + return MEMTX_ACCESS_ERROR; + } + hex_timer_update(s); + return MEMTX_OK; +} + +static void hex_timer_tick(void *opaque) +{ + QCTHextimerState *s = (QCTHextimerState *)opaque; + if ((s->cntpct >= s->cntval) && (s->int_level != 1)) { + s->int_level = 1; + hex_timer_update(s); + return; + } + s->cntpct += s->limit; +} + +static const MemoryRegionOps hex_timer_ops = { + .read_with_attrs = hex_timer_read, + .write_with_attrs = hex_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_hex_timer = { + .name = "hex_timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]){ VMSTATE_UINT32(control, QCTHextimerState), + VMSTATE_UINT32(cnt_ctrl, QCTHextimerState), + VMSTATE_UINT64(cntpct, QCTHextimerState), + VMSTATE_UINT64(cntval, QCTHextimerState), + VMSTATE_UINT64(limit, QCTHextimerState), + VMSTATE_UINT32(int_level, QCTHextimerState), + VMSTATE_PTIMER(timer, QCTHextimerState), + VMSTATE_END_OF_LIST() } +}; + +static void qct_qtimer_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + QCTQtimerState *s = QCT_QTIMER(dev); + unsigned int i; + + if (s->nr_frames > QCT_QTIMER_TIMER_FRAME_ELTS) { + error_setg(errp, "nr_frames too high"); + return; + } + + if (s->nr_views > QCT_QTIMER_TIMER_VIEW_ELTS) { + error_setg(errp, "nr_views too high"); + return; + } + + memory_region_init_io(&s->iomem, OBJECT(sbd), &qct_qtimer_ops, s, "qutimer", + QTIMER_MEM_SIZE_BYTES); + sysbus_init_mmio(sbd, &s->iomem); + + memory_region_init_io(&s->view_iomem, OBJECT(sbd), &hex_timer_ops, s, + "qutimer_views", + QTIMER_MEM_SIZE_BYTES * s->nr_frames * s->nr_views); + sysbus_init_mmio(sbd, &s->view_iomem); + + for (i = 0; i < s->nr_frames; i++) { + s->timer[i].limit = 1; + s->timer[i].control = QCT_QTIMER_CNTP_CTL_ENABLE; + s->timer[i].cnt_ctrl = + (QCT_QTIMER_AC_CNTACR_RWPT | QCT_QTIMER_AC_CNTACR_RWVT | + QCT_QTIMER_AC_CNTACR_RVOFF | QCT_QTIMER_AC_CNTACR_RFRQ | + QCT_QTIMER_AC_CNTACR_RPVCT | QCT_QTIMER_AC_CNTACR_RPCT); + s->timer[i].qtimer = s; + s->timer[i].freq = QTIMER_DEFAULT_FREQ_HZ; + + s->secure |= (1 << i); + + sysbus_init_irq(sbd, &(s->timer[i].irq)); + + (s->timer[i]).timer = + ptimer_init(hex_timer_tick, &s->timer[i], PTIMER_POLICY_LEGACY); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_hex_timer, + &s->timer[i]); + } +} + +static const Property qct_qtimer_properties[] = { + DEFINE_PROP_UINT32("freq", QCTQtimerState, freq, QTIMER_DEFAULT_FREQ_HZ), + DEFINE_PROP_UINT32("nr_frames", QCTQtimerState, nr_frames, 2), + DEFINE_PROP_UINT32("nr_views", QCTQtimerState, nr_views, 1), + DEFINE_PROP_UINT32("cnttid", QCTQtimerState, cnttid, 0x11), +}; + +static void qct_qtimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + + device_class_set_props(k, qct_qtimer_properties); + k->realize = qct_qtimer_realize; + k->vmsd = &vmstate_qct_qtimer; +} + +static const TypeInfo qct_qtimer_info = { + .name = TYPE_QCT_QTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(QCTQtimerState), + .instance_init = qct_qtimer_init, + .class_init = qct_qtimer_class_init, +}; + +static void qct_qtimer_register_types(void) +{ + type_register_static(&qct_qtimer_info); +} + +type_init(qct_qtimer_register_types) diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h new file mode 100644 index 000000000000..0afaac3b1f85 --- /dev/null +++ b/include/hw/hexagon/hexagon.h @@ -0,0 +1,151 @@ +/* + * Hexagon Baseboard System emulation. + * + * Copyright (c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#ifndef HW_HEXAGON_H +#define HW_HEXAGON_H + +#include "exec/memory.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) + +typedef union { + 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]; +} hexagon_config_table; + +typedef struct { + /* 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_rg0; + uint32_t qtmr_rg1; + hexagon_config_table cfgtable; +} hexagon_machine_config; + +#endif diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h new file mode 100644 index 000000000000..1f858d54c08e --- /dev/null +++ b/include/hw/hexagon/virt.h @@ -0,0 +1,42 @@ +/* + * Definitions for hexagon virt board. + * + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HEXAGONVIRT_H +#define HW_HEXAGONVIRT_H + +#include "target/hexagon/cpu.h" +#include "hw/boards.h" + +#define HEXAGON_MAX_CPUS 8 + +struct HexagonVirtMachineState { + /*< private >*/ + MachineState parent_obj; + + int fdt_size; + MemoryRegion *sys; + MemoryRegion cfgtable; + MemoryRegion ram; + MemoryRegion tcm; + MemoryRegion vtcm; + DeviceState *l2vic; +}; + +void hexagon_load_fdt(const struct HexagonVirtMachineState *vms); + +enum { + VIRT_UART0, + VIRT_GPT, + VIRT_MMIO, + VIRT_FDT, +}; + +#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt") +OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE) + +#endif /* HW_HEXAGONVIRT_H */ + diff --git a/include/hw/timer/qct-qtimer.h b/include/hw/timer/qct-qtimer.h new file mode 100644 index 000000000000..ab02181d0339 --- /dev/null +++ b/include/hw/timer/qct-qtimer.h @@ -0,0 +1,85 @@ +/* + * Qualcomm QCT QTimer + * + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef TIMER_QCT_QTIMER_H +#define TIMER_QCT_QTIMER_H + +#include "hw/ptimer.h" +#include "hw/sysbus.h" + +#define TYPE_QCT_QTIMER "qct-qtimer" +#define TYPE_QCT_HEXTIMER "qct-hextimer" +OBJECT_DECLARE_SIMPLE_TYPE(QCTQtimerState, QCT_QTIMER) +OBJECT_DECLARE_SIMPLE_TYPE(QCTHextimerState, QCT_HEXTIMER) + +struct QCTHextimerState { + QCTQtimerState *qtimer; + ptimer_state *timer; + uint64_t cntval; /* + * Physical timer compare value interrupt when cntpct > + * cntval + */ + uint64_t cntpct; /* Physical counter */ + uint32_t control; + uint32_t cnt_ctrl; + uint32_t cntpl0acr; + uint64_t limit; + uint32_t freq; + uint32_t int_level; + qemu_irq irq; +}; + +#define QCT_QTIMER_TIMER_FRAME_ELTS (8) +#define QCT_QTIMER_TIMER_VIEW_ELTS (2) +struct QCTQtimerState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion view_iomem; + uint32_t secure; + struct QCTHextimerState timer[QCT_QTIMER_TIMER_FRAME_ELTS]; + uint32_t frame_id; + uint32_t freq; + uint32_t nr_frames; + uint32_t nr_views; + uint32_t cnttid; +}; + +#define QCT_QTIMER_AC_CNTFRQ (0x000) +#define QCT_QTIMER_AC_CNTSR (0x004) +#define QCT_QTIMER_AC_CNTSR_NSN_1 (1 << 0) +#define QCT_QTIMER_AC_CNTSR_NSN_2 (1 << 1) +#define QCT_QTIMER_AC_CNTSR_NSN_3 (1 << 2) +#define QCT_QTIMER_AC_CNTTID (0x08) +#define QCT_QTIMER_AC_CNTACR_0 (0x40) +#define QCT_QTIMER_AC_CNTACR_1 (0x44) +#define QCT_QTIMER_AC_CNTACR_2 (0x48) +#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */ +#define QCT_QTIMER_AC_CNTACR_RWVT (1 << 4) /* R/W of CNTV_* regs */ +#define QCT_QTIMER_AC_CNTACR_RVOFF (1 << 3) /* R/W of CNTVOFF register */ +#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */ +#define QCT_QTIMER_AC_CNTACR_RPVCT (1 << 1) /* R/W of CNTVCT register */ +#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */ +#define QCT_QTIMER_VERSION (0x0fd0) +#define QCT_QTIMER_CNTPCT_LO (0x000) +#define QCT_QTIMER_CNTPCT_HI (0x004) +#define QCT_QTIMER_CNT_FREQ (0x010) +#define QCT_QTIMER_CNTPL0ACR (0x014) +#define QCT_QTIMER_CNTPL0ACR_PL0CTEN (1 << 9) +#define QCT_QTIMER_CNTPL0ACR_PL0TVEN (1 << 8) +#define QCT_QTIMER_CNTPL0ACR_PL0VCTEN (1 << 1) +#define QCT_QTIMER_CNTPL0ACR_PL0PCTEN (1 << 0) +#define QCT_QTIMER_CNTP_CVAL_LO (0x020) +#define QCT_QTIMER_CNTP_CVAL_HI (0x024) +#define QCT_QTIMER_CNTP_TVAL (0x028) +#define QCT_QTIMER_CNTP_CTL (0x02c) +#define QCT_QTIMER_CNTP_CTL_ISTAT (1 << 2) +#define QCT_QTIMER_CNTP_CTL_INTEN (1 << 1) +#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0) +#define QCT_QTIMER_AC_CNTACR_START 0x40 +#define QCT_QTIMER_AC_CNTACR_END 0x5C + +#endif /* TIMER_QCT_QTIMER_H */ diff --git a/qapi/machine.json b/qapi/machine.json index a6b8795b09ed..a7070bad4d52 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -33,7 +33,7 @@ # Since: 3.0 ## { 'enum' : 'SysEmuTarget', - 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'hppa', 'i386', + 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'hexagon', 'hppa', 'i386', 'loongarch64', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', 'mips64el', 'mipsel', 'or1k', 'ppc', 'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4', diff --git a/target/Kconfig b/target/Kconfig index d0c7b59d9c71..37781146b9bb 100644 --- a/target/Kconfig +++ b/target/Kconfig @@ -16,6 +16,7 @@ source sh4/Kconfig 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 000000000000..7e556f350633 --- /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 d053d6848715..87c2f6a53f6c 100644 --- a/target/hexagon/arch.c +++ b/target/hexagon/arch.c @@ -208,6 +208,11 @@ void arch_fpop_start(CPUHexagonState *env) * 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 9e3a05f88281..e6523a739b10 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -19,20 +19,41 @@ DEF_ATTRIB(AA_DUMMY, "Dummy Zeroth Attribute", "", "") /* Misc */ +DEF_ATTRIB(FAKEINSN, "Not a real instruction", "", "") +DEF_ATTRIB(MAPPING, "Not real -- asm mapped", "", "") +DEF_ATTRIB(CONDMAPPING, "Not real -- mapped based on values", "", "") DEF_ATTRIB(EXTENSION, "Extension instruction", "", "") +DEF_ATTRIB(SHARED_EXTENSION, "Shared extension instruction", "", "") +DEF_ATTRIB(CABAC, + "Cabac Instruction. Used in conjuction with QDSP6_CABAC_PRESENT", "", + "") +DEF_ATTRIB(EXPERIMENTAL, "This may not work correctly not supported by RTL.", + "", "") DEF_ATTRIB(PRIV, "Not available in user or guest mode", "", "") DEF_ATTRIB(GUEST, "Not available in user mode", "", "") DEF_ATTRIB(FPOP, "Floating Point Operation", "", "") +DEF_ATTRIB(FPDOUBLE, "Double-precision Floating Point Operation", "", "") +DEF_ATTRIB(FPSINGLE, "Single-precision Floating Point Operation", "", "") +DEF_ATTRIB(SFMAKE, "Single Float Make", "", "") +DEF_ATTRIB(DFMAKE, "Single Float Make", "", "") + +DEF_ATTRIB(NO_TIMING_LOG, "Does not get logged to the timing model", "", "") DEF_ATTRIB(EXTENDABLE, "Immediate may be extended", "", "") +DEF_ATTRIB(EXT_UPPER_IMMED, "Extend upper case immediate", "", "") +DEF_ATTRIB(EXT_LOWER_IMMED, "Extend lower case immediate", "", "") +DEF_ATTRIB(MUST_EXTEND, "Immediate must be extended", "", "") +DEF_ATTRIB(NA_NT, "Non-Allocating Non-Temporal instruction", "", "") +DEF_ATTRIB(INVPRED, "The predicate is inverted for true/false sense", "", "") DEF_ATTRIB(ARCHV2, "V2 architecture", "", "") DEF_ATTRIB(ARCHV3, "V3 architecture", "", "") DEF_ATTRIB(ARCHV4, "V4 architecture", "", "") DEF_ATTRIB(ARCHV5, "V5 architecture", "", "") +DEF_ATTRIB(PACKED, "Packable instruction", "", "") DEF_ATTRIB(SUBINSN, "sub-instruction", "", "") /* Load and Store attributes */ @@ -46,21 +67,48 @@ DEF_ATTRIB(MEMSIZE_4B, "Memory width is 4 bytes", "", "") DEF_ATTRIB(MEMSIZE_8B, "Memory width is 8 bytes", "", "") DEF_ATTRIB(SCALAR_LOAD, "Load is scalar", "", "") DEF_ATTRIB(SCALAR_STORE, "Store is scalar", "", "") -DEF_ATTRIB(REGWRSIZE_1B, "Memory width is 1 byte", "", "") -DEF_ATTRIB(REGWRSIZE_2B, "Memory width is 2 bytes", "", "") -DEF_ATTRIB(REGWRSIZE_4B, "Memory width is 4 bytes", "", "") -DEF_ATTRIB(REGWRSIZE_8B, "Memory width is 8 bytes", "", "") +DEF_ATTRIB(REGWRSIZE_1B, "ETM Memory width is 1 byte", "", "") +DEF_ATTRIB(REGWRSIZE_2B, "ETM Memory width is 2 bytes", "", "") +DEF_ATTRIB(REGWRSIZE_4B, "ETM Memory width is 4 bytes", "", "") +DEF_ATTRIB(REGWRSIZE_8B, "ETM 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", "", "") +DEF_ATTRIB(LLSC, "load-locked/store-conditional instruction", "", "") DEF_ATTRIB(RLS_INNER, "Store release inner visibility", "", "") +DEF_ATTRIB(RLS_OUTER, "Store release outer visibility", "", "") DEF_ATTRIB(RLS_ALL_THREAD, "Store release among all threads", "", "") DEF_ATTRIB(RLS_SAME_THREAD, "Store release with the same thread", "", "") +/* Load and Store Addressing Mode Attributes */ +DEF_ATTRIB(EA_REG_ONLY, "EA = input register only", "", "") +DEF_ATTRIB(EA_IMM_ONLY, "EA = immediate only", "", "") +DEF_ATTRIB(EA_REG_PLUS_IMM, "EA = register plus immediate", "", "") +DEF_ATTRIB(EA_REG_PLUS_REGSCALED, "EA = register plus scaled register", "", "") +DEF_ATTRIB(EA_IMM_PLUS_REGSCALED, "EA = immediate plus scaled register", "", "") +DEF_ATTRIB(EA_BREV_REG, "EA = bit-reversed input register", "", "") +DEF_ATTRIB(EA_GP_IMM, "EA = GP plus immediate (unless extended)", "", "") +DEF_ATTRIB(EA_PAGECROSS, "EA calculation can have a Page Cross Stall", "", "") + +DEF_ATTRIB(PM_ANY, "Post Modify", "", "") +DEF_ATTRIB(PM_I, "Post Modify by Immediate", "", "") +DEF_ATTRIB(PM_M, "Post Modify by M register", "", "") +DEF_ATTRIB(PM_CIRI, "Post Modify with Circular Addressing by immediate", "", "") +DEF_ATTRIB(PM_CIRR, "Post Modify with Circular Addressing by I field", "", "") + +DEF_ATTRIB(VMEM, "VMEM-type", "", "") +DEF_ATTRIB(VBUF, "Touches the VBUF", "", "") +DEF_ATTRIB(VDBG, "Vector debugging instruction", "", "") + /* V6 Vector attributes */ DEF_ATTRIB(CVI, "Executes on the HVX extension", "", "") +DEF_ATTRIB(NT_VMEM, "Non-temporal memory access", "", "") +DEF_ATTRIB(VMEMU, "Unaligned memory access", "", "") DEF_ATTRIB(CVI_NEW, "New value memory instruction executes on HVX", "", "") DEF_ATTRIB(CVI_VM, "Memory instruction executes on HVX", "", "") @@ -69,109 +117,415 @@ DEF_ATTRIB(CVI_VP_VS, "Double vector permute/shft insn executes on HVX", "", "") DEF_ATTRIB(CVI_VX, "Multiply instruction executes on HVX", "", "") DEF_ATTRIB(CVI_VX_DV, "Double vector multiply insn executes on HVX", "", "") DEF_ATTRIB(CVI_VS, "Shift instruction executes on HVX", "", "") -DEF_ATTRIB(CVI_VS_3SRC, "This shift needs to borrow a source register", "", "") +DEF_ATTRIB( + CVI_VS_3SRC, + "This shift instruction needs to borrow a source register from the VP slot", + "", "") DEF_ATTRIB(CVI_VS_VX, "Permute/shift and multiply insn executes on HVX", "", "") DEF_ATTRIB(CVI_VA, "ALU instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VA_2SRC, + "This alu instruction executes on multimedia vector engine and " + "requires two vectro sources", + "", "") DEF_ATTRIB(CVI_VA_DV, "Double vector alu instruction executes on HVX", "", "") DEF_ATTRIB(CVI_4SLOT, "Consumes all the vector execution resources", "", "") DEF_ATTRIB(CVI_TMP, "Transient Memory Load not written to register", "", "") DEF_ATTRIB(CVI_REMAP, "Register Renaming not written to register file", "", "") +DEF_ATTRIB(CVI_TMP_SRC, "Transient reassign", "", "") +DEF_ATTRIB(CVI_EXTRACT, "HVX Extract Instruction that goes through L2", "", "") +DEF_ATTRIB(CVI_EARLY, "HVX instructions that require early sources", "", "") +DEF_ATTRIB(CVI_LATE, "HVX insn that always require late sources", "", "") +DEF_ATTRIB(CVI_VV_LATE, "HVX insn that always require late Vv source", "", "") +DEF_ATTRIB(CVI_REQUIRES_TMPLOAD, ".tmp load must be included in packet", "", "") +DEF_ATTRIB(CVI_PUMP_2X, "Goes through the pipeline twice", "", "") +DEF_ATTRIB(CVI_PUMP_4X, "Goes through the pipeline four times", "", "") DEF_ATTRIB(CVI_GATHER, "CVI Gather operation", "", "") DEF_ATTRIB(CVI_SCATTER, "CVI Scatter operation", "", "") DEF_ATTRIB(CVI_SCATTER_RELEASE, "CVI Store Release for scatter", "", "") +DEF_ATTRIB(CVI_GATHER_RELEASE, "CVI Store Release for gather", "", "") DEF_ATTRIB(CVI_TMP_DST, "CVI instruction that doesn't write a register", "", "") +DEF_ATTRIB(CVI_SCATTER_WORD_ACC, "CVI Scatter Word Accum (second pass)", "", "") +DEF_ATTRIB(CVI_SCATTER_ACC, "CVI Scatter Accumulate", "", "") +DEF_ATTRIB(CVI_VX_VSRC0_IS_DST, + "For the assembler to handle the special case of non-linear " + "instructions with Vxx specified both as src and dst in syntax ", + "", "") + +DEF_ATTRIB(CVI_VX_ACC_FWD, "VX Accumulator Forwarding", "", "") + +DEF_ATTRIB(CVI_VX_NO_TMP_LD, + "VX Accumulator renaming not allowed from tmp load instruction", "", + "") + +DEF_ATTRIB(RESTRICT_CVI_NOVP, + "Instructions with this attribute are assigned to the original " + "shift unit and can not be assigned to the shift/permute unit", + "", "") + +DEF_ATTRIB(CVI_GATHER_ADDR_2B, "CVI Scatter/Gather address is halfword", "", "") +DEF_ATTRIB(CVI_GATHER_ADDR_4B, "CVI Scatter/Gather address is word", "", "") + +DEF_ATTRIB(VFETCH, "memory fetch op to L2 for a single vector", "", "") + DEF_ATTRIB(CVI_SLOT23, "Can execute in slot 2 or slot 3 (HVX)", "", "") -DEF_ATTRIB(VTCM_ALLBANK_ACCESS, "Allocates in all VTCM schedulers.", "", "") +DEF_ATTRIB(HVX_FLT, "This a floating point HVX instruction.", "", "") + +DEF_ATTRIB( + VTCM_ALLBANK_ACCESS, + "This instruction allocates in all VTCM schedulers due to a region access.", + "", "") +DEF_ATTRIB(XUMINOR, "XU minor SMTable instruction", "", "") + +DEF_ATTRIB(SYNC_MARKER, "This instruction needs a sync marker.", "", "") + /* Change-of-flow attributes */ DEF_ATTRIB(JUMP, "Jump-type instruction", "", "") +DEF_ATTRIB(DIRECT, "Uses an PC-relative immediate field", "", "") DEF_ATTRIB(INDIRECT, "Absolute register jump", "", "") +DEF_ATTRIB(CJUMP, "Conditional jump", "", "") DEF_ATTRIB(CALL, "Function call instruction", "", "") +DEF_ATTRIB(RET, "Function return instruction", "", "") +DEF_ATTRIB(PERM, "Permute instruction", "", "") DEF_ATTRIB(COF, "Change-of-flow instruction", "", "") DEF_ATTRIB(HINTED_COF, "This instruction is a hinted change-of-flow", "", "") DEF_ATTRIB(CONDEXEC, "May be cancelled by a predicate", "", "") +DEF_ATTRIB(DOTOLD, "Uses a predicate generated in a previous packet", "", "") +DEF_ATTRIB(DOTNEW, "Uses a predicate generated in the same packet", "", "") DEF_ATTRIB(DOTNEWVALUE, "Uses a register value generated in this pkt", "", "") DEF_ATTRIB(NEWCMPJUMP, "Compound compare and jump", "", "") DEF_ATTRIB(NVSTORE, "New-value store", "", "") DEF_ATTRIB(MEMOP, "memop", "", "") -DEF_ATTRIB(ROPS_2, "Compound instruction worth 2 RISC-ops", "", "") -DEF_ATTRIB(ROPS_3, "Compound instruction worth 3 RISC-ops", "", "") +DEF_ATTRIB(ROPS_2, "Compound instruction worth 2 wimpy RISC-ops", "", "") +DEF_ATTRIB(ROPS_3, "Compound instruction worth 3 wimpy RISC-ops", "", "") /* access to implicit registers */ DEF_ATTRIB(IMPLICIT_WRITES_LR, "Writes the link register", "", "UREG.LR") +DEF_ATTRIB(IMPLICIT_READS_LR, "Reads the link register", "UREG.LR", "") +DEF_ATTRIB(IMPLICIT_READS_LC0, "Reads loop count for loop 0", "UREG.LC0", "") +DEF_ATTRIB(IMPLICIT_READS_LC1, "Reads loop count for loop 1", "UREG.LC1", "") +DEF_ATTRIB(IMPLICIT_READS_SA0, "Reads start address for loop 0", "UREG.SA0", "") +DEF_ATTRIB(IMPLICIT_READS_SA1, "Reads start address for loop 1", "UREG.SA1", "") +DEF_ATTRIB(IMPLICIT_WRITES_PC, "Writes the program counter", "", "UREG.PC") +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_READS_FP, "Reads the frame pointer", "UREG.FP", "") +DEF_ATTRIB(IMPLICIT_WRITES_GP, "Writes the GP register", "", "UREG.GP") +DEF_ATTRIB(IMPLICIT_READS_GP, "Reads the GP register", "UREG.GP", "") 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") DEF_ATTRIB(IMPLICIT_WRITES_SA0, "Writes start addr for loop 0", "", "UREG.SA0") DEF_ATTRIB(IMPLICIT_WRITES_SA1, "Writes start addr for loop 1", "", "UREG.SA1") +DEF_ATTRIB(IMPLICIT_WRITES_R00, "Writes Register 0", "", "UREG.R00") DEF_ATTRIB(IMPLICIT_WRITES_P0, "Writes Predicate 0", "", "UREG.P0") 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_R00, "Reads Register 0", "UREG.R00", "") +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_READS_Q3, "Reads Vector Predicate 3", "UREG.Q3", "") +DEF_ATTRIB(IMPLICIT_READS_CS, "Reads the CS/M register", "UREG.CS", "") +DEF_ATTRIB(IMPLICIT_READS_FRAMEKEY, "Reads FRAMEKEY", "UREG.FRAMEKEY", "") +DEF_ATTRIB(IMPLICIT_READS_FRAMELIMIT, "Reads FRAMELIMIT", "UREG.FRAMELIMIT", "") +DEF_ATTRIB(IMPLICIT_READS_ELR, "Reads the ELR register", "MREG.ELR", "") +DEF_ATTRIB(IMPLICIT_READS_SGP0, "Reads the SGP0 register", "MREG.SGP0", "") +DEF_ATTRIB(IMPLICIT_READS_SGP1, "Reads the SGP1 register", "MREG.SGP1", "") +DEF_ATTRIB(IMPLICIT_WRITES_SGP0, "Reads the SGP0 register", "", "MREG.SGP0") +DEF_ATTRIB(IMPLICIT_WRITES_SGP1, "Reads the SGP1 register", "", "MREG.SGP1") +DEF_ATTRIB(IMPLICIT_WRITES_STID_PRIO_ANYTHREAD, "Reads", "", "MREG.STID.PRIO") +DEF_ATTRIB(IMPLICIT_WRITES_SRBIT, "Writes the OVF bit", "", "UREG.SR.OVF") +DEF_ATTRIB(IMPLICIT_WRITES_FPFLAGS, "May write FP flags", "", "UREG.SR.FPFLAGS") +DEF_ATTRIB(IMPLICIT_WRITES_LPCFG, "Writes the loop config", "", "UREG.SR.LPCFG") +DEF_ATTRIB(IMPLICIT_WRITES_CVBITS, "Writes the CV flags", "", "UREG.SR.CV") +DEF_ATTRIB(IMPLICIT_READS_FPRND, "May read FP rnd mode", "UREG.SR.FPRND", "") +DEF_ATTRIB(IMPLICIT_READS_SSR, "May read SSR values", "MREG.SSR", "") +DEF_ATTRIB(IMPLICIT_READS_CCR, "May read CCR values", "MREG.CCR", "") +DEF_ATTRIB(IMPLICIT_WRITES_CCR, "May write CCR values", "", "MREG.CCR") +DEF_ATTRIB(IMPLICIT_WRITES_SSR, "May write SSR values", "", "MREG.SSR") +DEF_ATTRIB(IMPLICIT_READS_GELR, "May read GELR values", "GREG.GELR", "") +DEF_ATTRIB(IMPLICIT_READS_GEVB, "May read GEVB values", "MREG.GEVB", "") +DEF_ATTRIB(IMPLICIT_READS_GSR, "May read GSR values", "GREG.GSR", "") +DEF_ATTRIB(IMPLICIT_READS_GOSP, "May read GOSP values", "GREG.GOSP", "") +DEF_ATTRIB(IMPLICIT_WRITES_GELR, "May write GELR values", "", "GREG.GELR") +DEF_ATTRIB(IMPLICIT_WRITES_GSR, "May write GSR values", "", "GREG.GSR") +DEF_ATTRIB(IMPLICIT_WRITES_GOSP, "May write GOSP values", "", "GREG.GOSP") +DEF_ATTRIB(IMPLICIT_READS_IPENDAD_IPEND, "May read", "MREG.IPENDAD.IPEND", "") +DEF_ATTRIB(IMPLICIT_WRITES_IPENDAD_IPEND, "May write", "", "MREG.IPENDAD.IPEND") +DEF_ATTRIB(IMPLICIT_READS_IPENDAD_IAD, "May read", "MREG.IPENDAD.IAD", "") +DEF_ATTRIB(IMPLICIT_WRITES_IPENDAD_IAD, "May write", "", "MREG.IPENDAD.IAD") +DEF_ATTRIB(IMPLICIT_WRITES_IMASK_ANYTHREAD, "May write", "", "MREG.IMASK") +DEF_ATTRIB(IMPLICIT_READS_IMASK_ANYTHREAD, "May read", "MREG.IMASK", "") +DEF_ATTRIB(IMPLICIT_READS_SYSCFG_K0LOCK, "May read", "MREG.SYSCFG.K0LOCK", "") +DEF_ATTRIB(IMPLICIT_WRITES_SYSCFG_K0LOCK, "May write", "", "MREG.SYSCFG.K0LOCK") +DEF_ATTRIB(IMPLICIT_READS_SYSCFG_TLBLOCK, "May read", "MREG.SYSCFG.TLBLOCK", "") +DEF_ATTRIB(IMPLICIT_WRITES_SYSCFG_TLBLOCK, "May wr", "", "MREG.SYSCFG.TLBLOCK") +DEF_ATTRIB(IMPLICIT_WRITES_SYSCFG_GCA, "May write", "", "MREG.SYSCFG.GCA") +DEF_ATTRIB(IMPLICIT_READS_SYSCFG_GCA, "May read", "MREG.SYSCFG.GCA", "") DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "") -DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the SP register", "", "") + +/* Other things the instruction does */ +DEF_ATTRIB(ACC, "Has a multiply", "", "") +DEF_ATTRIB(MPY, "Has a multiply", "", "") +DEF_ATTRIB(SATURATE, "Does signed saturation", "", "") +DEF_ATTRIB(USATURATE, "Does unsigned saturation", "", "") +DEF_ATTRIB(CIRCADDR, "Uses circular addressing mode", "", "") +DEF_ATTRIB(BREVADDR, "Uses bit reverse addressing mode", "", "") +DEF_ATTRIB(BIDIRSHIFTL, "Uses a bidirectional shift left", "", "") +DEF_ATTRIB(BIDIRSHIFTR, "Uses a bidirectional shift right", "", "") +DEF_ATTRIB(BRANCHADDER, "Contains a PC-plus-immediate operation.", "", "") +DEF_ATTRIB(CRSLOT23, "Can execute in slot 2 or slot 3 (CR)", "", "") DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "") DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "") DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "") -DEF_ATTRIB(CRSLOT23, "Can execute in slot 2 or slot 3 (CR)", "", "") +/* Instruction Types */ + +DEF_ATTRIB(IT_ALU, "ALU type", "", "") +DEF_ATTRIB(IT_ALU_ADDSUB, "ALU add or subtract type", "", "") +DEF_ATTRIB(IT_ALU_MINMAX, "ALU MIN or MAX type", "", "") +DEF_ATTRIB(IT_ALU_MOVE, "ALU data movement type", "", "") +DEF_ATTRIB(IT_ALU_LOGICAL, "ALU logical operation type", "", "") +DEF_ATTRIB(IT_ALU_SHIFT, "ALU shift operation type", "", "") +DEF_ATTRIB(IT_ALU_SHIFT_AND_OP, "ALU shift and additional op type", "", "") +DEF_ATTRIB(IT_ALU_CMP, "ALU compare operation type", "", "") + +DEF_ATTRIB(IT_LOAD, "Loads from memory", "", "") +DEF_ATTRIB(IT_STORE, "Stores to memory", "", "") + +DEF_ATTRIB(IT_MPY, "Multiply type", "", "") +DEF_ATTRIB(IT_MPY_32, "32-bit Multiply type", "", "") + +DEF_ATTRIB(IT_COF, "Change-of-flow type", "", "") +DEF_ATTRIB(IT_HWLOOP, "Sets up hardware loop registers", "", "") + +DEF_ATTRIB(IT_MISC, "misc instruction type", "", "") + DEF_ATTRIB(IT_NOP, "nop instruction", "", "") DEF_ATTRIB(IT_EXTENDER, "constant extender instruction", "", "") +/* Exceptions the instruction can generate */ + +DEF_ATTRIB(EXCEPTION_TLB, "Can generate a TLB Miss Exception", "", "") +DEF_ATTRIB(EXCEPTION_ACCESS, "Can generate Access Violation Exception", "", "") +DEF_ATTRIB(EXCEPTION_SWI, "Software Interrupt (trap) exception", "", "") + + +/* Documentation Notes */ +DEF_ATTRIB(NOTE_ARCHV2, "Only available in the V2 architecture", "", "") + +DEF_ATTRIB(NOTE_PACKET_PC, "The PC is the addr of the start of the pkt", "", "") + +DEF_ATTRIB(NOTE_PACKET_NPC, "Next PC is the address following pkt", "", "") + +DEF_ATTRIB(NOTE_CONDITIONAL, "can be conditionally executed", "", "") + +DEF_ATTRIB(NOTE_NEWVAL_SLOT0, "New-value oprnd must execute on slot 0", "", "") + +DEF_ATTRIB(NOTE_RELATIVE_ADDRESS, "A PC-relative address is formed", "", "") + +DEF_ATTRIB(NOTE_LA_RESTRICT, "Cannot be in the last pkt of a HW loop", "", "") + +DEF_ATTRIB(NOTE_OOBVSHIFT, "Possible shift overflow", "", "") +DEF_ATTRIB(NOTE_BIDIRSHIFT, "Bidirectional shift", "", "") + +DEF_ATTRIB(NOTE_CVFLAGS, "Sets the Carry and Overflow flags in USR.", "", "") +DEF_ATTRIB(NOTE_SR_OVF_WHEN_SATURATING, "Might set OVF bit", "", "") +DEF_ATTRIB(NOTE_STNT, + "Non Temporal Data. The :nt appendix is a hint to the " + "microarchitecture indicating that the life of the cache line is " + "short. This information is used throughout the cache hierarchy to " + "make replacement and allocation decisions.", + "", "") +DEF_ATTRIB(NOTE_PRIV, "Monitor-level feature", "", "") +DEF_ATTRIB(NOTE_GUEST, "Guest-level feature", "", "") +DEF_ATTRIB(NOTE_NOPACKET, "solo instruction", "", "") +DEF_ATTRIB(NOTE_AXOK, "May only be grouped with ALU32 or non-FP XTYPE.", "", "") +DEF_ATTRIB(NOTE_NOSLOT1, "Packet with this insn must have slot 1 empty", "", "") +DEF_ATTRIB(NOTE_SLOT1_AOK, "Packet must have slot 1 empty or ALU32", "", "") +DEF_ATTRIB(NOTE_NOSLOT01, "Packet must have both slot 1 and 2 empty", "", "") +DEF_ATTRIB(NOTE_NEEDS_MEMLD, "Must be grouped with a memory load", "", "") +DEF_ATTRIB(NOTE_LATEPRED, "The predicate can not be used as a .new", "", "") +DEF_ATTRIB(NOTE_COMPAT_ACCURACY, "In the future accuracy may increase", "", "") +DEF_ATTRIB(NOTE_NVSLOT0, "Can execute only in slot 0 (ST)", "", "") +DEF_ATTRIB(NOTE_DEPRECATED, "Will be deprecated in a future version.", "", "") +DEF_ATTRIB(NOTE_NONAPALIV1, "may not work correctly in Napali V1.", "", "") +DEF_ATTRIB(NOTE_NOLAHAINAV1, "This may not work correctly in Lahaina V1.", "", + "") +DEF_ATTRIB(NOTE_BADTAG_UNDEF, "Undefined if a tag is non-present", "", "") +DEF_ATTRIB(NOTE_NOSLOT2_MPY, "Packet cannot have a slot 2 multiply", "", "") +DEF_ATTRIB(NOTE_HVX_ONLY, "Only available on a core with HVX.", "", "") + +DEF_ATTRIB(NOTE_NOCOF_RESTRICT, "Cannot be grouped with any COF", "", "") +DEF_ATTRIB(NOTE_BRANCHADDER_MAX1, "One PC-plus-offset calculation", "", "") + +DEF_ATTRIB(NOTE_CRSLOT23, "Execute on either slot2 or slot3 (CR)", "", "") +DEF_ATTRIB(NOTE_EXTENSION_AUDIO, "Hexagon audio extensions", "", "") +DEF_ATTRIB(NOTE_FETCHNT, + "Non Temporal Data Cache Prefetch. The :nt appendix is a hint to " + "the microarchitecture indicating that the life of the cache line " + "fetched is short. This information is used throughout the cache " + "hierarchy to make replacement and allocation decisions.", + "", "") +DEF_ATTRIB(NOTE_VECX_V67, "This instruction is only available on V67", "", "") + +DEF_ATTRIB(NOTE_NOVP, + "This instruction cannot be paired with a HVX permute instruction", + "", "") +DEF_ATTRIB(NOTE_VA_UNARY, + "If a packet contains this instruction and a HVX ALU op then the " + "ALU OP must be unary.", + "", "") + + +/* V6 MMVector Notes for Documentation */ +DEF_ATTRIB(NOTE_ANY_RESOURCE, "Can use any HVX resource.", "", "") +DEF_ATTRIB(NOTE_ANY2_RESOURCE, "Uses any pair of the HVX resources", "", "") +DEF_ATTRIB(NOTE_PERMUTE_RESOURCE, "Uses the HVX permute resource.", "", "") +DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "") +DEF_ATTRIB(NOTE_MPY_RESOURCE, "Uses a HVX multiply resource.", "", "") +DEF_ATTRIB(NOTE_MPYDV_RESOURCE, "Uses both HVX multiply resources.", "", "") +DEF_ATTRIB(NOTE_NT_VMEM, "Non-temporal hint to the micro-architecture", "", "") +DEF_ATTRIB(NOTE_ALL_RESOURCE, "Uses all HVX resources.", "", "") +DEF_ATTRIB(NOTE_VMEM, "Immediates are in multiples of vector length.", "", "") +DEF_ATTRIB(NOTE_ANY_VS_VX_RESOURCE, "Consumes two resources", "", "") + +DEF_ATTRIB(NOTE_RT8, "Input scalar register Rt is limited to R0-R7", "", "") + +DEF_ATTRIB(NOTE_MX, "This is in-memory matrix multiply instruction.", "", "") +DEF_ATTRIB(NOTE_VX_ACC_FWD, + "The accumulator (Vxx) source of this instruction must be generate " + "in the previous packet to avoid a stall. The accumulator cannot " + "come from a .tmp operation.", + "", "") +DEF_ATTRIB(NOTE_TMP_NO_VX, + "The tmp load instruction destination register cannot be an " + "accumulator register.", + "", "") + +DEF_ATTRIB( + NOTE_NO_ECC, + "ECC is not supported for scatter and gather instructions. Enabling ECC " + "with unprotected access instructions result in undetermined behavior.", + "", "") + +/* FP8 instructions */ +DEF_ATTRIB(HVX_FP8, "HVX FP8 extension instruction", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_8, "HVX IEEE FP extension instruction: 8-bit output", + "", "") + /* Restrictions to make note of */ +DEF_ATTRIB(RESTRICT_LOOP_LA, "Cannot be in the last packet of a loop", "", "") +DEF_ATTRIB(RESTRICT_NEEDS_MEMLD, "Must be grouped with a load", "", "") DEF_ATTRIB(RESTRICT_COF_MAX1, "One change-of-flow per packet", "", "") DEF_ATTRIB(RESTRICT_NOPACKET, "Not allowed in a packet", "", "") +DEF_ATTRIB(RESTRICT_NOSRMOVE, "Do not write SR in the same packet", "", "") DEF_ATTRIB(RESTRICT_SLOT0ONLY, "Must execute on slot0", "", "") DEF_ATTRIB(RESTRICT_SLOT1ONLY, "Must execute on slot1", "", "") DEF_ATTRIB(RESTRICT_SLOT2ONLY, "Must execute on slot2", "", "") DEF_ATTRIB(RESTRICT_SLOT3ONLY, "Must execute on slot3", "", "") +DEF_ATTRIB(RESTRICT_NOSLOT2_MPY, "A packet cannot have a slot 2 mpy", "", "") DEF_ATTRIB(RESTRICT_NOSLOT1, "No slot 1 instruction in parallel", "", "") +DEF_ATTRIB(RESTRICT_SLOT1_AOK, "Slot 1 insn must be empty or A-type", "", "") +DEF_ATTRIB(RESTRICT_NOSLOT01, "No slot 0 or 1 instructions in parallel", "", "") +DEF_ATTRIB(RESTRICT_NOSLOT1_STORE, "Packet must not have slot 1 store", "", "") +DEF_ATTRIB(RESTRICT_NOSLOT0_LOAD, "Packet must not have a slot 1 load", "", "") +DEF_ATTRIB(RESTRICT_NOCOF, "Cannot be grouped with any COF", "", "") +DEF_ATTRIB(RESTRICT_BRANCHADDER_MAX1, "One PC-plus-offset calculation", "", "") DEF_ATTRIB(RESTRICT_PREFERSLOT0, "Try to encode into slot 0", "", "") +DEF_ATTRIB(RESTRICT_SINGLE_MEM_FIRST, "Single memory op must be last", "", "") DEF_ATTRIB(RESTRICT_PACKET_AXOK, "May exist with A-type or X-type", "", "") +DEF_ATTRIB(RESTRICT_PACKET_SOMEREGS_OK, "Relaxed grouping rules", "", "") +DEF_ATTRIB(RESTRICT_LATEPRED, "Predicate can not be used as a .new.", "", "") + +DEF_ATTRIB(PAIR_1OF2, "For assembler", "", "") +DEF_ATTRIB(PAIR_2OF2, "For assembler", "", "") +DEF_ATTRIB(NOTE_MX_PAIR, + "Weights and Activations need to be paired in a packet.", "", "") +DEF_ATTRIB(NOTE_RESTRICT_CVI_NOVP, + "This instruction cannot use the permute/shift resource", "", "") + +/* Performance based preferences */ +DEF_ATTRIB(PREFER_SLOT3, "Complex XU prefering slot3", "", "") + +DEF_ATTRIB(RELAX_COF_1ST, "COF can be fisrt in assembly order", "", "") +DEF_ATTRIB(RELAX_COF_2ND, "COF can be second in assembly order", "", "") DEF_ATTRIB(ICOP, "Instruction cache op", "", "") +DEF_ATTRIB(INTRINSIC_RETURNS_UNSIGNED, "Intrinsic returns an unsigned", "", "") + +DEF_ATTRIB(PRED_BIT_1, "The branch uses bit 1 as the prediction bit", "", "") +DEF_ATTRIB(PRED_BIT_4, "The branch uses bit 4 as the prediction bit", "", "") +DEF_ATTRIB(PRED_BIT_8, "The branch uses bit 8 as the prediction bit", "", "") +DEF_ATTRIB(PRED_BIT_12, "The branch uses bit 12 as the prediction bit", "", "") +DEF_ATTRIB(PRED_BIT_13, "The branch uses bit 13 as the prediction bit", "", "") +DEF_ATTRIB(PRED_BIT_7, "The branch uses bit 7 as the prediction bit", "", "") +DEF_ATTRIB(HWLOOP0_SETUP, "Sets up HW loop0", "", "") +DEF_ATTRIB(HWLOOP1_SETUP, "Sets up HW loop1", "", "") DEF_ATTRIB(HWLOOP0_END, "Ends HW loop0", "", "") DEF_ATTRIB(HWLOOP1_END, "Ends HW loop1", "", "") DEF_ATTRIB(RET_TYPE, "return type", "", "") +DEF_ATTRIB(HINTJR, "hintjr type", "", "") DEF_ATTRIB(DCZEROA, "dczeroa type", "", "") +DEF_ATTRIB(ICTAGOP, "ictag op type", "", "") DEF_ATTRIB(ICFLUSHOP, "icflush op type", "", "") DEF_ATTRIB(DCFLUSHOP, "dcflush op type", "", "") +DEF_ATTRIB(DCTAGOP, "dctag op type", "", "") DEF_ATTRIB(L2FLUSHOP, "l2flush op type", "", "") +DEF_ATTRIB(L2TAGOP, "l2tag op type", "", "") DEF_ATTRIB(DCFETCH, "dcfetch type", "", "") +DEF_ATTRIB(BIMODAL_BRANCH, "Updates the bimodal branch predictor", "", "") +DEF_ATTRIB(VECINSN, "Long Vector Instruction", "", "") +DEF_ATTRIB(MEMSIZE_32B, "Memory width is 32 bytes", "", "") +DEF_ATTRIB(FOUR_PHASE, "Four Phase Instruction", "", "") DEF_ATTRIB(L2FETCH, "Instruction is l2fetch type", "", "") +DEF_ATTRIB(PREDUSE_BSB, "Instructions need back-skip-back scheduling", "", "") DEF_ATTRIB(ICINVA, "icinva", "", "") DEF_ATTRIB(DCCLEANINVA, "dccleaninva", "", "") +DEF_ATTRIB(EXTENSION_AUDIO, "audio extension", "", "") + +DEF_ATTRIB(MEMCPY, "memcpy or dma-type instruction", "", "") DEF_ATTRIB(NO_INTRINSIC, "Don't generate an intrisic", "", "") -/* Documentation Notes */ -DEF_ATTRIB(NOTE_CONDITIONAL, "can be conditionally executed", "", "") -DEF_ATTRIB(NOTE_NEWVAL_SLOT0, "New-value oprnd must execute on slot 0", "", "") -DEF_ATTRIB(NOTE_PRIV, "Monitor-level feature", "", "") -DEF_ATTRIB(NOTE_NOPACKET, "solo instruction", "", "") -DEF_ATTRIB(NOTE_AXOK, "May only be grouped with ALU32 or non-FP XTYPE.", "", "") -DEF_ATTRIB(NOTE_LATEPRED, "The predicate can not be used as a .new", "", "") -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(NO_XML, "Don't generate a XML docs for this instruction", "", "") -/* V6 MMVector Notes for Documentation */ -DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "") -/* Restrictions to make note of */ -DEF_ATTRIB(RESTRICT_NOSLOT1_STORE, "Packet must not have slot 1 store", "", "") -DEF_ATTRIB(RESTRICT_LATEPRED, "Predicate can not be used as a .new.", "", "") +DEF_ATTRIB(DMA, "User-DMA instruction", "", "") +DEF_ATTRIB(VERIF_DMASTEP, + "Hiphop needs to step dma prior to executing this packet", "", "") +DEF_ATTRIB(VERIF_DMATICK, + "DMA gets a tick in verif mode for this instruction after a commit", + "", "") + +DEF_ATTRIB(HVX_IEEE_FP, "HVX IEEE FP extension instruction", "", "") +DEF_ATTRIB(NOTE_HVX_IEEE_FP, + "Only supported on the HVX cores with the IEEE FP extension", "", "") + +DEF_ATTRIB(HVX_IEEE_FP_DV_ONE, + "HVX IEEE FP extension instruction - dual pipes: P2 and P3 - output " + "only on P2", + "", "") +DEF_ATTRIB(HVX_IEEE_FP_ACC, "HVX IEEE FP accumulate instruction", "", "") +DEF_ATTRIB(HVX_IEEE_BF, + "HVX IEEE BF extension instruction: 16-bit bfloat input", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_BF, + "HVX IEEE FP extension instruction: 16-bit bfloat output", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_16, + "HVX IEEE FP extension instruction: 16-bit output", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_32, + "HVX IEEE FP extension instruction: 32-bit output", "", "") +DEF_ATTRIB(HVX_IEEE_FP_BINARY_LATE, + "HVX IEEE FP extension instruction: Both inputs can arrive late", "", + "") /* Keep this as the last attribute: */ DEF_ATTRIB(ZZ_LASTATTRIB, "Last attribute in the file", "", "") diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 71b4a9b83ec2..bc157d6b7ea7 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -18,10 +18,19 @@ #ifndef HEXAGON_CPU_PARAM_H #define HEXAGON_CPU_PARAM_H +#ifdef CONFIG_USER_ONLY #define TARGET_PAGE_BITS 16 /* 64K pages */ +#else +#define TARGET_PAGE_BITS 12 /* 4K pages */ +#endif #define TARGET_LONG_BITS 32 #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +/* + * Hexagon processors have a strong memory model. + */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL) + #endif diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 0b7fc98f6ce1..dffafe8d8865 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -26,6 +26,16 @@ #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" #include "exec/gdbstub.h" +#include "cpu_helper.h" +#include "max.h" +#include "hex_mmu.h" + +#ifndef CONFIG_USER_ONLY +#include "macros.h" +#include "sys_macros.h" +#include "qemu/main-loop.h" +#include "hex_interrupts.h" +#endif static void hexagon_v66_cpu_init(Object *obj) { } static void hexagon_v67_cpu_init(Object *obj) { } @@ -50,6 +60,17 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) } static const Property hexagon_cpu_properties[] = { +#if !defined(CONFIG_USER_ONLY) + DEFINE_PROP_UINT32("num-tlbs", HexagonCPU, num_tlbs, MAX_TLB_ENTRIES), + DEFINE_PROP_UINT32("l2vic-base-addr", HexagonCPU, l2vic_base_addr, + 0xffffffffULL), + DEFINE_PROP_UINT32("qtimer-base-addr", HexagonCPU, qtimer_base_addr, + 0xffffffffULL), + DEFINE_PROP_UINT32("hvx-contexts", HexagonCPU, hvx_contexts, 0), + DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffffULL), + DEFINE_PROP_UINT64("config-table-addr", HexagonCPU, config_table_addr, + 0xffffffffULL), +#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), @@ -67,6 +88,35 @@ const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { "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", "rgdr2", + "rgdr", "turkey", "duck", "chicken", +}; + +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 @@ -216,8 +266,7 @@ static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags) 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_reg(f, env, HEX_SREG_BADVA); print_reg(f, env, HEX_REG_CS0); print_reg(f, env, HEX_REG_CS1); #endif @@ -262,9 +311,29 @@ static void hexagon_cpu_synchronize_from_tb(CPUState *cs, cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } +#ifndef CONFIG_USER_ONLY +bool hexagon_thread_is_enabled(CPUHexagonState *env) +{ + target_ulong modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + bool E_bit = thread_enabled_mask & (0x1 << env->threadId); + + return E_bit; +} +#endif + static bool hexagon_cpu_has_work(CPUState *cs) { +#ifndef CONFIG_USER_ONLY + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + return hexagon_thread_is_enabled(env) && + (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_SWI + | CPU_INTERRUPT_K0_UNLOCK | CPU_INTERRUPT_TLB_UNLOCK)); +#else return true; +#endif } static void hexagon_restore_state_to_opc(CPUState *cs, @@ -274,6 +343,29 @@ static void hexagon_restore_state_to_opc(CPUState *cs, cpu_env(cs)->gpr[HEX_REG_PC] = data[0]; } + +#ifndef CONFIG_USER_ONLY +static void mmu_reset(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + if (cs->cpu_index == 0) { + memset(env->hex_tlb, 0, sizeof(*env->hex_tlb)); + } +} + +void hexagon_cpu_soft_reset(CPUHexagonState *env) +{ + BQL_LOCK_GUARD(); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_SSR, 0); + hexagon_ssr_set_cause(env, HEX_CAUSE_RESET); + + target_ulong evb = ARCH_GET_SYSTEM_REG(env, HEX_SREG_EVB); + ARCH_SET_THREAD_REG(env, HEX_REG_PC, evb); +} +#endif + + +#define HEXAGON_CFG_ADDR_BASE(addr) (((addr) >> 16) & 0x0fffff) static void hexagon_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); @@ -288,6 +380,36 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) 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 + HexagonCPU *cpu = HEXAGON_CPU(cs); + + if (cs->cpu_index == 0) { + memset(env->g_sreg, 0, sizeof(target_ulong) * NUM_SREGS); + } + memset(env->t_sreg, 0, sizeof(target_ulong) * NUM_SREGS); + memset(env->greg, 0, sizeof(target_ulong) * NUM_GREGS); + + if (cs->cpu_index == 0) { + ARCH_SET_SYSTEM_REG(env, HEX_SREG_MODECTL, 0x1); + *(env->g_pcycle_base) = 0; + } + mmu_reset(env); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_HTID, cs->cpu_index); + hexagon_cpu_soft_reset(env); + env->threadId = cs->cpu_index; + 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->wait_next_pc = 0; + env->cause_code = -1; + ARCH_SET_THREAD_REG(env, HEX_REG_PC, cpu->boot_addr); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_CFGBASE, + HEXAGON_CFG_ADDR_BASE(cpu->config_table_addr)); + +#endif } static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info) @@ -307,29 +429,299 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) return; } +#ifndef CONFIG_USER_ONLY + HexagonCPU *cpu = HEXAGON_CPU(cs); + if (cpu->num_tlbs > MAX_TLB_ENTRIES) { + error_setg(errp, "Number of TLBs selected is invalid"); + return; + } +#endif + gdb_register_coprocessor(cs, hexagon_hvx_gdb_read_register, hexagon_hvx_gdb_write_register, gdb_find_static_feature("hexagon-hvx.xml"), 0); +#ifndef CONFIG_USER_ONLY + gdb_register_coprocessor(cs, hexagon_sys_gdb_read_register, + hexagon_sys_gdb_write_register, + gdb_find_static_feature("hexagon-sys.xml"), 0); +#endif + qemu_init_vcpu(cs); - cpu_reset(cs); +#ifndef CONFIG_USER_ONLY + CPUHexagonState *env = cpu_env(cs); + hex_mmu_realize(env); + if (cs->cpu_index == 0) { + env->g_sreg = g_new0(target_ulong, NUM_SREGS); + env->g_pcycle_base = g_malloc0(sizeof(*env->g_pcycle_base)); + } else { + CPUState *cpu0_s = NULL; + CPUHexagonState *env0 = NULL; + CPU_FOREACH(cpu0_s) { + assert(cpu0_s->cpu_index == 0); + env0 = &(HEXAGON_CPU(cpu0_s)->env); + + break; + } + env->g_sreg = env0->g_sreg; + env->g_pcycle_base = env0->g_pcycle_base; + } +#endif mcc->parent_realize(dev, errp); } +#if !defined(CONFIG_USER_ONLY) +static void hexagon_cpu_set_irq(void *opaque, int irq, int level) +{ + HexagonCPU *cpu = opaque; + CPUHexagonState *env = &cpu->env; + + 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) { +#if !defined(CONFIG_USER_ONLY) + HexagonCPU *cpu = HEXAGON_CPU(obj); + qdev_init_gpio_in(DEVICE(cpu), hexagon_cpu_set_irq, 8); +#endif } #include "hw/core/tcg-cpu-ops.h" +#if !defined(CONFIG_USER_ONLY) +static bool get_physical_address(CPUHexagonState *env, hwaddr *phys, int *prot, + int *size, int32_t *excp, target_ulong 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, int 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_page_debug(CPUState *cs, vaddr addr) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + hwaddr phys_addr; + int prot; + int 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, target_ulong VA, int slot, + MMUAccessType access_type) +{ + ARCH_SET_SYSTEM_REG(env, HEX_SREG_BADVA, VA); + + if (access_type == MMU_INST_FETCH || slot == 0) { + ARCH_SET_SYSTEM_REG(env, HEX_SREG_BADVA0, VA); + ARCH_SET_SYSTEM_REG(env, 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) { + ARCH_SET_SYSTEM_REG(env, HEX_SREG_BADVA0, INVALID_BADVA); + ARCH_SET_SYSTEM_REG(env, 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, target_ulong VA, int slot, + MMUAccessType access_type) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + 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, target_ulong VA, int slot, + MMUAccessType access_type, int32_t excp); +static void raise_perm_exception(CPUState *cs, target_ulong VA, int slot, + MMUAccessType access_type, int32_t excp) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + 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) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + int slot = 0 /* This is broken FIXME */; + hwaddr phys; + int prot = 0; + int page_size = 0; + int32_t excp = 0; + bool ret = 0; + + qemu_log_mask( + CPU_LOG_MMU, + "%s: tid = 0x%x, 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; + } else { + 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 = { + .get_phys_page_debug = hexagon_cpu_get_phys_page_debug, +}; + +static bool hexagon_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + 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; +} + +#endif + static const TCGCPUOps hexagon_tcg_ops = { .initialize = hexagon_translate_init, .translate_code = hexagon_translate_code, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, .restore_state_to_opc = hexagon_restore_state_to_opc, +#if !defined(CONFIG_USER_ONLY) + .cpu_exec_interrupt = hexagon_cpu_exec_interrupt, + .tlb_fill = hexagon_tlb_fill, + .cpu_exec_halt = hexagon_cpu_has_work, + .do_interrupt = hexagon_cpu_do_interrupt, +#endif /* !CONFIG_USER_ONLY */ }; +static int hexagon_cpu_mmu_index(CPUState *cs, bool ifetch) +{ +#ifndef CONFIG_USER_ONLY + BQL_LOCK_GUARD(); + CPUHexagonState *env = cpu_env(cs); + uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + uint8_t mmuen = GET_SYSCFG_FIELD(SYSCFG_MMUEN, syscfg); + if (!mmuen) { + return MMU_KERNEL_IDX; + } + + int 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; +} + + static void hexagon_cpu_class_init(ObjectClass *c, void *data) { HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c); @@ -346,6 +738,7 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) cc->class_by_name = hexagon_cpu_class_by_name; cc->has_work = hexagon_cpu_has_work; + cc->mmu_index = hexagon_cpu_mmu_index; cc->dump_state = hexagon_dump_state; cc->set_pc = hexagon_cpu_set_pc; cc->get_pc = hexagon_cpu_get_pc; @@ -354,9 +747,38 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) 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) +{ + target_ulong ssr = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + int ssr_ce = GET_SSR_FIELD(SSR_CE, ssr); + + if (reg <= HEX_GREG_G3) { + return env->greg[reg]; + } + switch (reg) { + case HEX_GREG_GPCYCLELO: + return ssr_ce ? hexagon_get_sys_pcycle_count_low(env) : 0; + + case HEX_GREG_GPCYCLEHI: + return ssr_ce ? hexagon_get_sys_pcycle_count_high(env) : 0; + + default: + qemu_log_mask(LOG_UNIMP, "reading greg %d not yet supported.\n", reg); + return 0; + } +} +#endif + #define DEFINE_CPU(type_name, initfn) \ { \ .name = type_name, \ diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 79e60d4bfa1b..f462af2a8e77 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -20,12 +20,23 @@ #include "fpu/softfloat-types.h" +#define NUM_GREGS 32 +#define GREG_WRITES_MAX 32 +#define NUM_SREGS 64 +#define SREG_WRITES_MAX 64 + #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-common.h" #include "hex_regs.h" #include "mmvec/mmvec.h" #include "hw/registerfields.h" +#ifndef CONFIG_USER_ONLY +#include "reg_fields.h" +typedef struct CPUHexagonTLBContext CPUHexagonTLBContext; +#endif + #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -34,11 +45,47 @@ #define REG_WRITES_MAX 32 #define PRED_WRITES_MAX 5 /* 4 insns + endloop */ #define VSTORES_MAX 2 +#define VECTOR_UNIT_MAX 8 + +#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 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 + +#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 enum { + HEX_LOCK_UNLOCKED = 0, + HEX_LOCK_WAITING = 1, + HEX_LOCK_OWNER = 2, + HEX_LOCK_QUEUED = 3 +} hex_lock_state_t; +#endif #define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU -#define MMU_USER_IDX 0 - typedef struct { target_ulong va; uint8_t width; @@ -71,12 +118,34 @@ typedef struct { typedef struct CPUArchState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; + target_ulong 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; + uint64_t t_cycle_count; + uint64_t *g_pcycle_base; +#ifndef CONFIG_USER_ONLY + /* Some system registers are per thread and some are global. */ + target_ulong t_sreg[NUM_SREGS]; + target_ulong t_sreg_written[NUM_SREGS]; + target_ulong *g_sreg; + + target_ulong greg[NUM_GREGS]; + target_ulong greg_written[NUM_GREGS]; + target_ulong wait_next_pc; + + /* This alias of CPUState.cpu_index is used by imported sources: */ + target_ulong threadId; + hex_lock_state_t tlb_lock_state; + hex_lock_state_t k0_lock_state; + target_ulong tlb_lock_count; + target_ulong k0_lock_count; + CPUHexagonTLBContext *hex_tlb; +#endif + target_ulong next_PC; target_ulong new_value_usr; MemLog mem_log_stores[STORES_MAX]; @@ -122,16 +191,44 @@ struct ArchCPU { bool lldb_compat; target_ulong lldb_stack_adjust; bool short_circuit; +#ifndef CONFIG_USER_ONLY + uint32_t num_tlbs; + uint32_t l2vic_base_addr; + uint32_t qtimer_base_addr; + uint32_t hvx_contexts; + uint32_t boot_addr; + uint64_t config_table_addr; +#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); +uint32_t hexagon_sreg_read(CPUHexagonState *env, uint32_t reg); +void hexagon_gdb_sreg_write(CPUHexagonState *env, uint32_t reg, uint32_t val); +void hexagon_cpu_soft_reset(CPUHexagonState *env); +#endif + +#include "exec/cpu-all.h" + +#ifndef CONFIG_USER_ONLY +#include "cpu_helper.h" +#endif + static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { @@ -141,10 +238,27 @@ static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, if (*pc == env->gpr[HEX_REG_SA0]) { hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); } - *flags = hex_flags; if (*pc & PCALIGN_MASK) { hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); } +#ifndef CONFIG_USER_ONLY + target_ulong syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + + bool pcycle_enabled = extract32(syscfg, + reg_field_info[SYSCFG_PCYCLEEN].offset, + reg_field_info[SYSCFG_PCYCLEEN].width); + + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, + cpu_mmu_index(env_cpu(env), false)); + + if (pcycle_enabled) { + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, PCYCLE_ENABLED, 1); + } +#else + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, PCYCLE_ENABLED, true); + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, MMU_USER_IDX); +#endif + *flags = hex_flags; } typedef HexagonCPU ArchCPU; @@ -152,7 +266,10 @@ typedef HexagonCPU ArchCPU; void hexagon_translate_init(void); void hexagon_translate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc); - -#include "exec/cpu-all.h" +#ifndef CONFIG_USER_ONLY +void hexagon_cpu_do_interrupt(CPUState *cpu); +void register_trap_exception(CPUHexagonState *env, int type, int imm, + target_ulong PC); +#endif #endif /* HEXAGON_CPU_H */ diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index ff596e2a94c9..c7cc426ec888 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -24,19 +24,88 @@ #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_PC_NOT_ALIGNED = 0x01e, - HEX_CAUSE_PRIV_NO_UREAD = 0x024, - HEX_CAUSE_PRIV_NO_UWRITE = 0x025, + HEX_CAUSE_RESET = 0x000, + HEX_CAUSE_BIU_PRECISE = 0x001, + HEX_CAUSE_UNSUPORTED_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, + 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_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, + HEX_CAUSE_VIC0 = 0x0c2, + HEX_CAUSE_VIC1 = 0x0c3, + HEX_CAUSE_VIC2 = 0x0c4, + HEX_CAUSE_VIC3 = 0x0c5, +}; + +enum data_cache_state { + HEX_DC_STATE_INVALID = 0x0, + HEX_DC_STATE_VALID = 0x1, + HEX_DC_STATE_RESERVED = 0x2, + HEX_DC_STATE_UNUSED_WT = 0x3, }; #define PACKET_WORDS_MAX 4 diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c new file mode 100644 index 000000000000..d4b7cbfba170 --- /dev/null +++ b/target/hexagon/cpu_helper.c @@ -0,0 +1,477 @@ +/* + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_helper.h" +#include "system/cpus.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#include "exec/helper-proto.h" +#else +#include "hw/boards.h" +#include "hw/hexagon/hexagon.h" +#include "hex_interrupts.h" +#include "hex_mmu.h" +#endif +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "qemu/log.h" +#include "tcg/tcg-op.h" +#include "internal.h" +#include "macros.h" +#include "sys_macros.h" +#include "arch.h" + + +#ifndef CONFIG_USER_ONLY + +uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index) +{ + g_assert_not_reached(); +} + +uint32_t arch_get_system_reg(CPUHexagonState *env, uint32_t reg) +{ + if (reg == HEX_SREG_PCYCLELO) { + return hexagon_get_sys_pcycle_count_low(env); + } else if (reg == HEX_SREG_PCYCLEHI) { + return hexagon_get_sys_pcycle_count_high(env); + } + + g_assert(reg < NUM_SREGS); + return reg < HEX_SREG_GLB_START ? env->t_sreg[reg] : env->g_sreg[reg]; +} + +uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env) +{ + uint64_t cycles = 0; + CPUState *cs; + CPU_FOREACH(cs) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env_ = &cpu->env; + cycles += env_->t_cycle_count; + } + return *(env->g_pcycle_base) + cycles; +} + +uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env) +{ + return hexagon_get_sys_pcycle_count(env) >> 32; +} + +uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env) +{ + return extract64(hexagon_get_sys_pcycle_count(env), 0, 32); +} + +void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, + uint32_t cycles_hi) +{ + uint64_t cur_cycles = hexagon_get_sys_pcycle_count(env); + uint64_t cycles = + ((uint64_t)cycles_hi << 32) | extract64(cur_cycles, 0, 32); + hexagon_set_sys_pcycle_count(env, cycles); +} + +void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, + uint32_t cycles_lo) +{ + uint64_t cur_cycles = hexagon_get_sys_pcycle_count(env); + uint64_t cycles = extract64(cur_cycles, 32, 32) | cycles_lo; + hexagon_set_sys_pcycle_count(env, cycles); +} + +void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t cycles) +{ + *(env->g_pcycle_base) = cycles; + + CPUState *cs; + CPU_FOREACH(cs) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env_ = &cpu->env; + env_->t_cycle_count = 0; + } +} + +static void set_wait_mode(CPUHexagonState *env) +{ + g_assert(bql_locked()); + + const uint32_t modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + uint32_t 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_wait_thread(CPUHexagonState *env, target_ulong PC) +{ + 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); + + CPUState *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 %d 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 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; + bool found; + + g_assert(bql_locked()); + int nr_cpus = 0; + CPU_FOREACH(cs) { + nr_cpus++; + } + + for (int htid = 0; htid < nr_cpus; ++htid) { + if (!(mask & (0x1 << htid))) { + continue; + } + + found = false; + CPU_FOREACH(cs) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + env = &cpu->env; + if (env->threadId == htid) { + found = true; + break; + } + } + if (!found) { + cpu_abort(cs, "Error: Hexagon: Illegal resume thread mask 0x%x", + mask); + } + + if (get_exe_mode(env) != HEX_EXE_MODE_WAIT) { + /* this thread not currently in wait mode */ + continue; + } + hexagon_resume_thread(env); + } +} + + +static MMVector VRegs[VECTOR_UNIT_MAX][NUM_VREGS]; +static MMQReg QRegs[VECTOR_UNIT_MAX][NUM_QREGS]; + +/* + * EXT_CONTEXTS + * SSR.XA 2 4 6 8 + * 000 HVX Context 0 HVX Context 0 HVX Context 0 HVX Context 0 + * 001 HVX Context 1 HVX Context 1 HVX Context 1 HVX Context 1 + * 010 HVX Context 0 HVX Context 2 HVX Context 2 HVX Context 2 + * 011 HVX Context 1 HVX Context 3 HVX Context 3 HVX Context 3 + * 100 HVX Context 0 HVX Context 0 HVX Context 4 HVX Context 4 + * 101 HVX Context 1 HVX Context 1 HVX Context 5 HVX Context 5 + * 110 HVX Context 0 HVX Context 2 HVX Context 2 HVX Context 6 + * 111 HVX Context 1 HVX Context 3 HVX Context 3 HVX Context 7 + */ +static int parse_context_idx(CPUHexagonState *env, uint8_t XA) +{ + int ret; + HexagonCPU *cpu = env_archcpu(env); + if (cpu->hvx_contexts == 6 && XA >= 6) { + ret = XA - 6 + 2; + } else { + ret = XA % cpu->hvx_contexts; + } + g_assert(ret >= 0 && ret < VECTOR_UNIT_MAX); + return ret; +} + +static void check_overcommitted_hvx(CPUHexagonState *env, uint32_t ssr) +{ + if (!GET_FIELD(SSR_XE, ssr)) { + return; + } + + uint8_t XA = GET_SSR_FIELD(SSR_XA, ssr); + + CPUState *cs; + CPU_FOREACH(cs) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env_ = &cpu->env; + if (env_ == env) { + continue; + } + /* Check if another thread has the XE bit set and same XA */ + uint32_t ssr_ = ARCH_GET_SYSTEM_REG(env_, HEX_SREG_SSR); + if (GET_SSR_FIELD(SSR_XE2, ssr_) && GET_FIELD(SSR_XA, ssr_) == XA) { + qemu_log_mask(LOG_GUEST_ERROR, + "setting SSR.XA '%d' on thread %d but thread" + " %d has same extension active\n", XA, env->threadId, + env_->threadId); + } + } +} + +void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old) +{ + g_assert(bql_locked()); + + bool old_EX = GET_SSR_FIELD(SSR_EX, old); + bool old_UM = GET_SSR_FIELD(SSR_UM, old); + bool old_GM = GET_SSR_FIELD(SSR_GM, old); + bool old_IE = GET_SSR_FIELD(SSR_IE, old); + uint8_t old_XA = GET_SSR_FIELD(SSR_XA, old); + bool new_EX = GET_SSR_FIELD(SSR_EX, new); + bool new_UM = GET_SSR_FIELD(SSR_UM, new); + bool new_GM = GET_SSR_FIELD(SSR_GM, new); + bool new_IE = GET_SSR_FIELD(SSR_IE, new); + uint8_t new_XA = GET_SSR_FIELD(SSR_XA, new); + + if ((old_EX != new_EX) || + (old_UM != new_UM) || + (old_GM != new_GM)) { + hex_mmu_mode_change(env); + } + + uint8_t old_asid = GET_SSR_FIELD(SSR_ASID, old); + uint8_t new_asid = GET_SSR_FIELD(SSR_ASID, new); + if (new_asid != old_asid) { + CPUState *cs = env_cpu(env); + tlb_flush(cs); + } + + if (old_XA != new_XA) { + int old_unit = parse_context_idx(env, old_XA); + int new_unit = parse_context_idx(env, new_XA); + + /* Ownership exchange */ + memcpy(VRegs[old_unit], env->VRegs, sizeof(env->VRegs)); + memcpy(QRegs[old_unit], env->QRegs, sizeof(env->QRegs)); + memcpy(env->VRegs, VRegs[new_unit], sizeof(env->VRegs)); + memcpy(env->QRegs, QRegs[new_unit], sizeof(env->QRegs)); + + check_overcommitted_hvx(env, new); + } + + /* 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) +{ + g_assert(bql_locked()); + + const uint32_t modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + uint32_t 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) +{ + g_assert(bql_locked()); + + const uint32_t old = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, 1); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause); + const uint32_t new = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + + hexagon_modify_ssr(env, new, old); +} + + +int get_exe_mode(CPUHexagonState *env) +{ + g_assert(bql_locked()); + + target_ulong modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + bool E_bit = thread_enabled_mask & (0x1 << env->threadId); + uint32_t thread_wait_mask = GET_FIELD(MODECTL_W, modectl); + bool W_bit = thread_wait_mask & (0x1 << env->threadId); + target_ulong isdbst = ARCH_GET_SYSTEM_REG(env, HEX_SREG_ISDBST); + uint32_t debugmode = GET_FIELD(ISDBST_DEBUGMODE, isdbst); + bool D_bit = debugmode & (0x1 << env->threadId); + + /* Figure 4-2 */ + 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 void set_enable_mask(CPUHexagonState *env) +{ + g_assert(bql_locked()); + + const uint32_t modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + uint32_t 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); +} + +static uint32_t clear_enable_mask(CPUHexagonState *env) +{ + g_assert(bql_locked()); + + const uint32_t modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + uint32_t 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) +{ + BQL_LOCK_GUARD(); + + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + 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) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + 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 target_ulong get_thread0_r2(void) +{ + CPUState *cs; + CPU_FOREACH(cs) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *thread = &cpu->env; + if (thread->threadId == 0) { + return thread->gpr[2]; + } + } + g_assert_not_reached(); +} + +void hexagon_stop_thread(CPUHexagonState *env) + +{ + BQL_LOCK_GUARD(); + + uint32_t thread_enabled_mask = clear_enable_mask(env); + CPUState *cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + if (!thread_enabled_mask) { + /* All threads are stopped, exit */ + exit(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 = ARCH_GET_SYSTEM_REG(env, 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; +} + +#endif diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h new file mode 100644 index 000000000000..e49a2a9dc5d1 --- /dev/null +++ b/target/hexagon/cpu_helper.h @@ -0,0 +1,65 @@ +/* + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef HEXAGON_CPU_HELPER_H +#define HEXAGON_CPU_HELPER_H + +uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index); +uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env); +uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env); +uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env); +void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t); +void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t); +void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t); +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_wait_thread(CPUHexagonState *env, target_ulong PC); +void hexagon_resume_threads(CPUHexagonState *env, uint32_t mask); + +static inline void arch_set_thread_reg(CPUHexagonState *env, uint32_t reg, + uint32_t val) +{ + g_assert(reg < TOTAL_PER_THREAD_REGS); + env->gpr[reg] = val; +} + +static inline uint32_t arch_get_thread_reg(CPUHexagonState *env, uint32_t reg) +{ + g_assert(reg < TOTAL_PER_THREAD_REGS); + return env->gpr[reg]; +} + +#ifndef CONFIG_USER_ONLY +static inline void arch_set_system_reg(CPUHexagonState *env, uint32_t reg, + uint32_t val) +{ + g_assert(reg < NUM_SREGS); + if (reg < HEX_SREG_GLB_START) { + env->t_sreg[reg] = val; + } else { + env->g_sreg[reg] = val; + } +} +#endif + +uint32_t arch_get_system_reg(CPUHexagonState *env, uint32_t reg); + +#define ARCH_GET_THREAD_REG(ENV, REG) \ + arch_get_thread_reg(ENV, REG) +#define ARCH_SET_THREAD_REG(ENV, REG, VAL) \ + arch_set_thread_reg(ENV, REG, VAL) +#define ARCH_GET_SYSTEM_REG(ENV, REG) \ + arch_get_system_reg(ENV, REG) +#define ARCH_SET_SYSTEM_REG(ENV, REG, VAL) \ + arch_set_system_reg(ENV, REG, VAL) + +#endif + diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 23deba2426f8..a5073330b849 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -193,6 +193,8 @@ static bool decode_opcode_can_jump(int opcode) 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) { @@ -236,9 +238,9 @@ static void decode_set_insn_attr_fields(Packet *pkt) if (GET_ATTRIB(opcode, A_SCALAR_STORE) && !GET_ATTRIB(opcode, A_MEMSIZE_0B)) { if (pkt->insn[i].slot == 0) { - pkt->pkt_has_store_s0 = true; + pkt->pkt_has_scalar_store_s0 = true; } else { - pkt->pkt_has_store_s1 = true; + pkt->pkt_has_scalar_store_s1 = true; } } } @@ -371,6 +373,18 @@ static void decode_shuffle_for_execution(Packet *packet) 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 @@ -489,7 +503,6 @@ decode_insns(DisasContext *ctx, Insn *insn, uint32_t encoding) insn->iclass = iclass_bits(encoding); return 1; } - g_assert_not_reached(); } else { uint32_t iclass = get_duplex_iclass(encoding); unsigned int slot0_subinsn = get_slot0_subinsn(encoding); @@ -512,6 +525,11 @@ decode_insns(DisasContext *ctx, Insn *insn, uint32_t encoding) } g_assert_not_reached(); } + /* + * invalid/unrecognized opcode; return 1 and let gen_insn() raise an + * exception when it sees this empty insn. + */ + return 1; } static void decode_add_endloop_insn(Insn *insn, int loopnum) diff --git a/target/hexagon/gdbstub.c b/target/hexagon/gdbstub.c index 12d6b3bbcbb1..1f7e6201da38 100644 --- a/target/hexagon/gdbstub.c +++ b/target/hexagon/gdbstub.c @@ -76,6 +76,53 @@ int hexagon_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) g_assert_not_reached(); } +#ifndef CONFIG_USER_ONLY +int hexagon_sys_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + if (n < NUM_SREGS) { + return gdb_get_regl(mem_buf, hexagon_sreg_read(env, n)); + } + n -= NUM_SREGS; + + if (n < NUM_GREGS) { + return gdb_get_regl(mem_buf, hexagon_greg_read(env, n)); + } + n -= NUM_GREGS; + + n -= TOTAL_PER_THREAD_REGS; + + if (n < NUM_PREGS) { + env->pred[n] = ldtul_p(mem_buf) & 0xff; + return sizeof(uint8_t); + } + + n -= NUM_PREGS; + + g_assert_not_reached(); +} + +int hexagon_sys_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + if (n < NUM_SREGS) { + hexagon_gdb_sreg_write(env, n, ldtul_p(mem_buf)); + return sizeof(target_ulong); + } + n -= NUM_SREGS; + + if (n < NUM_GREGS) { + return env->greg[n] = ldtul_p(mem_buf); + } + n -= NUM_GREGS; + + g_assert_not_reached(); +} +#endif static int gdb_get_vreg(CPUHexagonState *env, GByteArray *mem_buf, int n) { int total = 0; diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index 3ac7cc2cfe57..df4a5bbbde26 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py @@ -42,6 +42,14 @@ 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 ("A_PRIV" in hex_common.attribdict[tag] or + "A_GUEST" in hex_common.attribdict[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): @@ -74,6 +82,10 @@ def gen_analyze_func(f, tag, regs, imms): if reg.is_written(): reg.analyze_write(f, tag, regno) + 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") diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index c1f806ac4b25..32e3bac74625 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -69,7 +69,7 @@ def gen_helper_function(f, tag, tagregs, tagimms): if hex_common.need_slot(tag): if "A_LOAD" in hex_common.attribdict[tag]: f.write(hex_common.code_fmt(f"""\ - bool pkt_has_store_s1 = slotval & 0x1; + bool pkt_has_scalar_store_s1 = slotval & 0x1; """)) f.write(hex_common.code_fmt(f"""\ uint32_t slot = slotval >> 1; @@ -109,26 +109,23 @@ 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]: + for tag in hex_common.get_user_tags(): + if hex_common.tag_ignore(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": + if hex_common.skip_qemu_helper(tag): continue - if tag == "Y6_diag1": + 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__": diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index 77f8e0a6a322..59c8bdd05c0f 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py @@ -59,27 +59,28 @@ 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]: + for tag in hex_common.get_user_tags(): + if hex_common.tag_ignore(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": + + if hex_common.skip_qemu_helper(tag): continue - if tag == "Y6_diag1": + if hex_common.is_idef_parser_enabled(tag): continue + 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__": diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py index 2f6e826f76d6..32bce9b00286 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 bbbb02df3a23..94dd1f876b21 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 94a19ff412e2..17ba3f9db95e 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 8a3b801287c7..146aadc73764 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) \ @@ -1133,6 +1134,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; \ @@ -1342,6 +1346,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 @@ -1361,13 +1370,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_func_table.py b/target/hexagon/gen_tcg_func_table.py index 299a39b1aa02..70c8db5c44c8 100755 --- a/target/hexagon/gen_tcg_func_table.py +++ b/target/hexagon/gen_tcg_func_table.py @@ -41,19 +41,9 @@ def main(): f.write("#define HEXAGON_FUNC_TABLE_H\n\n") f.write("const SemanticInsn opcode_genptr[XX_LAST_OPCODE] = {\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): continue f.write(f" [{tag}] = generate_{tag},\n") diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index c2ba91ddc044..65bfa046b867 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 @@ -50,6 +50,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") @@ -97,6 +109,11 @@ def gen_tcg_func(f, tag, regs, imms): if reg.is_written(): reg.log_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") @@ -121,18 +138,7 @@ 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): 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 000000000000..e98a84818b47 --- /dev/null +++ b/target/hexagon/gen_tcg_sys.h @@ -0,0 +1,128 @@ +/* + * Copyright(c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 tmp = tcg_temp_new(); \ + tcg_gen_mov_tl(tmp, RxV); \ + tcg_gen_mov_tl(RxV, hex_t_sreg[HEX_SREG_SGP0]); \ + tcg_gen_mov_tl(ctx->t_sreg_new_value[HEX_SREG_SGP0], tmp); \ + } while (0) + +#define fGEN_TCG_Y4_crswap1(SHORTCODE) \ + do { \ + TCGv tmp = tcg_temp_new(); \ + tcg_gen_mov_tl(tmp, RxV); \ + tcg_gen_mov_tl(RxV, hex_t_sreg[HEX_SREG_SGP1]); \ + tcg_gen_mov_tl(ctx->t_sreg_new_value[HEX_SREG_SGP1], tmp); \ + } while (0) + +#define fGEN_TCG_Y4_crswap10(SHORTCODE) \ + do { \ + g_assert_not_reached(); \ + 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]); \ + 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); \ + } 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 new_ssr = tcg_temp_new(); \ + 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) + +#define fGEN_TCG_Y4_nmi(SHORTCODE) \ + gen_helper_nmi(tcg_env, RsV) + +#endif diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 2c5e15cfcf6f..f38968271b17 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) @@ -128,6 +133,164 @@ TCGv get_result_pred(DisasContext *ctx, int pnum) } } +#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 val) +{ + tcg_gen_mov_tl(ctx->greg_new_value[rnum], val); +} + +G_GNUC_UNUSED +static void gen_log_greg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) +{ + TCGv val32 = tcg_temp_new(); + + /* 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 target_ulong 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, + [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_SCHEDCFG] = 0xfffffef0, + [HEX_SREG_CFGBASE] = IMMUTABLE, + [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_ISDBEN] = 0xfffffffe, + [HEX_SREG_TIMERLO] = IMMUTABLE, + [HEX_SREG_TIMERHI] = IMMUTABLE, +}; + +G_GNUC_UNUSED +static void gen_log_sreg_write(DisasContext *ctx, int rnum, TCGv val) +{ + const target_ulong 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); + tcg_gen_mov_tl(ctx->t_sreg_new_value[rnum], val); + } else { + gen_masked_reg_write(val, hex_g_sreg[rnum], reg_mask); + gen_helper_sreg_write(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 val32 = tcg_temp_new(); + + /* 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 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_tl(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 badva = tcg_temp_new(); + gen_helper_sreg_read(badva, tcg_env, + tcg_constant_tl(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_tl(reg_num)); + } +} + +G_GNUC_UNUSED +static void gen_read_greg(TCGv dst, int reg_num) +{ + gen_helper_greg_read(dst, tcg_env, tcg_constant_tl(reg_num)); +} + +G_GNUC_UNUSED +static void gen_read_greg_pair(TCGv_i64 dst, int reg_num) +{ + gen_helper_greg_read_pair(dst, tcg_env, tcg_constant_tl(reg_num)); +} +#endif + + void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val) { TCGv pred = get_result_pred(ctx, pnum); @@ -395,7 +558,8 @@ static inline void gen_store_conditional8(DisasContext *ctx, #ifndef CONFIG_HEXAGON_IDEF_PARSER static TCGv gen_slotval(DisasContext *ctx) { - int slotval = (ctx->pkt->pkt_has_store_s1 & 1) | (ctx->insn->slot << 1); + int slotval = + (ctx->pkt->pkt_has_scalar_store_s1 & 1) | (ctx->insn->slot << 1); return tcg_constant_tl(slotval); } #endif @@ -471,14 +635,15 @@ static void gen_write_new_pc_addr(DisasContext *ctx, TCGv addr, tcg_gen_brcondi_tl(cond, pred, 0, pred_false); } + TCGv PC_wr = ctx->need_next_pc ? hex_next_PC : hex_gpr[HEX_REG_PC]; if (ctx->pkt->pkt_has_multi_cof) { /* If there are multiple branches in a packet, ignore the second one */ - tcg_gen_movcond_tl(TCG_COND_NE, hex_gpr[HEX_REG_PC], + tcg_gen_movcond_tl(TCG_COND_NE, PC_wr, ctx->branch_taken, tcg_constant_tl(0), - hex_gpr[HEX_REG_PC], addr); + PC_wr, addr); tcg_gen_movi_tl(ctx->branch_taken, 1); } else { - tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], addr); + tcg_gen_mov_tl(PC_wr, addr); } if (cond != TCG_COND_ALWAYS) { diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index f8baa599c88c..5bcb2f48097c 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,29 @@ DEF_HELPER_4(probe_noshuf_load, void, env, i32, int, int) 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_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, void, env, i32, i32) +DEF_HELPER_3(sreg_write_pair, void, env, i32, i64) +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_2(nmi, 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 758e5fd12dfe..1242786e77ab 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 "A_PRIV" in attribdict[tag] or "A_GUEST" in attribdict[tag] + + +def tag_ignore(tag): + tag_skips = ( + "Y6_diag", + "Y6_diag0", + "Y6_diag1", + ) + attr_skips = ( + "A_FAKEINSN", + "A_MAPPING", + ) + return tag in tag_skips or \ + any(attr in attribdict[tag] for attr in 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,13 @@ 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("fLOAD_LOCKED", "A_LLSC") + add_qemu_macro_attrib("fSTORE_LOCKED", "A_LLSC") + 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 +255,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 +266,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: @@ -243,11 +288,17 @@ def need_PC(tag): def need_next_PC(tag): - return "A_CALL" in attribdict[tag] + return "A_CALL" in attribdict[tag] or tag == "J2_trap0" or tag == "J2_trap1" def need_pkt_has_multi_cof(tag): - return "A_COF" in attribdict[tag] + if ("A_JUMP" in attribdict[tag] + or "A_CALL" in attribdict[tag] + or tag == "J2_rte"): + if tag == "J4_hintjumpr": + return False + return True + return False def need_pkt_need_commit(tag): @@ -339,7 +390,7 @@ def __init__(self, regtype, regid): self.reg_num = f"{regtype}{regid}N" def decl_reg_num(self, f, regno): f.write(code_fmt(f"""\ - const int {self.reg_num} = insn->regno[{regno}]; + const int G_GNUC_UNUSED {self.reg_num} = insn->regno[{regno}]; """)) def idef_arg(self, declared): declared.append(self.reg_tcg()) @@ -349,6 +400,12 @@ def helper_arg(self): self.reg_tcg(), f"{self.helper_arg_type()} {self.helper_arg_name()}" ) + def from_subtype(self, subtype): + if subtype == "": + return self + raise Exception( + f"unknown subtype '{subtype}' on generic Register class") + # # Every register is either Single or Pair or Hvx @@ -366,12 +423,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): @@ -1009,6 +1070,136 @@ def analyze_write(self, f, tag, regno): ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper); """)) +class GuestRegister(Register): + def gen_check_impl(self, f, regno): + if self.is_written(): + f.write(code_fmt(f"""\ + if (!greg_writable(insn->regno[{regno}], + {str(self.is_pair()).lower()})) {{ + return; + }} + """)) + else: + f.write(code_fmt(f"""\ +check_greg_impl(insn->regno[{regno}], {str(self.is_pair()).lower()}); + """)) + +class GuestDest(GuestRegister, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + self.gen_check_impl(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = tcg_temp_new(); + gen_read_greg({self.reg_tcg()}, {self.reg_num}); + """)) + def log_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_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno); + self.gen_check_impl(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = tcg_temp_new(); + gen_read_greg({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + // const int {self.reg_num} = insn->regno[{regno}]; + """)) + +class GuestPairDest(GuestRegister, Pair, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + self.gen_check_impl(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 log_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_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + self.gen_check_impl(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): + f.write(code_fmt(f"""\ + // const int {self.reg_num} = insn->regno[{regno}]; + """)) + +class SystemDest(Register, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = tcg_temp_new(); + gen_read_sreg({self.reg_tcg()}, {self.reg_num}); + """)) + def log_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_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno); + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = tcg_temp_new(); + gen_read_sreg({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + // const int {self.reg_num} = insn->regno[{regno}]; + """)) + +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(); + gen_read_sreg_pair({self.reg_tcg()}, {self.reg_num}); + """)) + def log_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_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): + f.write(code_fmt(f"""\ + // const int {self.reg_num} = insn->regno[{regno}]; + """)) + def init_registers(): regs = { GprDest("R", "d"), @@ -1055,6 +1246,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 @@ -1070,11 +1271,22 @@ def init_registers(): for reg in new_regs: new_registers[f"{reg.regtype}{reg.regid}"] = reg -def get_register(tag, regtype, regid): - if f"{regtype}{regid}V" in semdict[tag]: - return registers[f"{regtype}{regid}"] - else: - return new_registers[f"{regtype}{regid}"] +def is_new_reg(tag, regid): + if regid[0] in "NO": + return True + return regid[0] == "P" and \ + f"{regid}N" in semdict[tag] and \ + f"{regid}V" not in semdict[tag] + +def get_register(tag, regtype, regid, subtype=""): + regid = f"{regtype}{regid}" + is_new = is_new_reg(tag, regid) + try: + reg = new_registers[regid] if is_new else registers[regid] + except KeyError: + raise Exception(f"Unknown {'new ' if is_new else ''}register {regid}" +\ + f"from '{tag}' with syntax '{semdict[tag]}'") from None + return reg.from_subtype(subtype) def helper_ret_type(tag, regs): ## If there is a scalar result, it is the return type @@ -1187,6 +1399,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") @@ -1194,6 +1407,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 000000000000..86ed06c90690 --- /dev/null +++ b/target/hexagon/hex_interrupts.c @@ -0,0 +1,328 @@ +/* + * Copyright(c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "hex_interrupts.h" +#include "macros.h" +#include "sys_macros.h" +#include "system/cpus.h" +#include "cpu_helper.h" + +static bool hex_is_qualified_for_int(CPUHexagonState *env, int int_num); + +static bool get_syscfg_gie(CPUHexagonState *env) +{ + target_ulong syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + return GET_SYSCFG_FIELD(SYSCFG_GIE, syscfg); +} + +static bool get_ssr_ex(CPUHexagonState *env) +{ + target_ulong ssr = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + return GET_SSR_FIELD(SSR_EX, ssr); +} + +static bool get_ssr_ie(CPUHexagonState *env) +{ + target_ulong ssr = ARCH_GET_SYSTEM_REG(env, 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) +{ + target_ulong old = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, ex); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause); + target_ulong new = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + hexagon_modify_ssr(env, new, old); +} + +static bool get_iad_bit(CPUHexagonState *env, int int_num) +{ + target_ulong ipendad = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IPENDAD); + target_ulong iad = GET_FIELD(IPENDAD_IAD, ipendad); + return extract32(iad, int_num, 1); +} + +static void set_iad_bit(CPUHexagonState *env, int int_num, int val) +{ + target_ulong ipendad = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IPENDAD); + target_ulong iad = GET_FIELD(IPENDAD_IAD, ipendad); + iad = deposit32(iad, int_num, 1, val); + fSET_FIELD(ipendad, IPENDAD_IAD, iad); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_IPENDAD, ipendad); +} + +static uint32_t get_ipend(CPUHexagonState *env) +{ + target_ulong ipendad = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IPENDAD); + return GET_FIELD(IPENDAD_IPEND, ipendad); +} + +static inline bool get_ipend_bit(CPUHexagonState *env, int int_num) +{ + target_ulong ipendad = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IPENDAD); + target_ulong ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + return extract32(ipend, int_num, 1); +} + +static void clear_ipend(CPUHexagonState *env, uint32_t mask) +{ + target_ulong ipendad = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IPENDAD); + target_ulong ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend &= ~mask; + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_IPENDAD, ipendad); +} + +static void set_ipend(CPUHexagonState *env, uint32_t mask) +{ + target_ulong ipendad = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IPENDAD); + target_ulong ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend |= mask; + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_IPENDAD, ipendad); +} + +static void set_ipend_bit(CPUHexagonState *env, int int_num, int val) +{ + target_ulong ipendad = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IPENDAD); + target_ulong ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend = deposit32(ipend, int_num, 1, val); + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_IPENDAD, ipendad); +} + +static bool get_imask_bit(CPUHexagonState *env, int int_num) +{ + target_ulong imask = ARCH_GET_SYSTEM_REG(env, HEX_SREG_IMASK); + return extract32(imask, int_num, 1); +} + +static uint32_t get_prio(CPUHexagonState *env) +{ + target_ulong stid = ARCH_GET_SYSTEM_REG(env, HEX_SREG_STID); + return extract32(stid, reg_field_info[STID_PRIO].offset, + reg_field_info[STID_PRIO].width); +} + +static void set_elr(CPUHexagonState *env, target_ulong val) +{ + ARCH_SET_SYSTEM_REG(env, HEX_SREG_ELR, val); +} + +static bool get_schedcfgen(CPUHexagonState *env) +{ + target_ulong schedcfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SCHEDCFG); + 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) { + HexagonCPU *hex_cpu = HEXAGON_CPU(cs); + CPUHexagonState *hex_env = &hex_cpu->env; + 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); + target_ulong evb = ARCH_GET_SYSTEM_REG(env, HEX_SREG_EVB); + 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 %d 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; + bool schedcfgen; + + /* Early exit if nothing pending */ + if (get_ipend(env) == 0) { + restore_state(env, false); + return false; + } + + max_ints = reg_field_info[IPENDAD_IPEND].width; + 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)) { + qemu_log_mask(CPU_LOG_INT, + "%s: thread[%d] pc = 0x%x 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[%d] int %d handled_\n", + __func__, env->threadId, i); + hex_accept_int(env, i); + int_handled = true; + break; + } + bool syscfg_gie = get_syscfg_gie(env); + bool iad = get_iad_bit(env, i); + bool ssr_ie = get_ssr_ie(env); + bool imask = get_imask_bit(env, i); + + qemu_log_mask(CPU_LOG_INT, + "%s: thread[%d] 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[%d] 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) { + HexagonCPU *hex_cpu = HEXAGON_CPU(cs); + CPUHexagonState *hex_env = &hex_cpu->env; + const int exe_mode = get_exe_mode(hex_env); + if (exe_mode != HEX_EXE_MODE_OFF) { + cs->interrupt_request |= 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 000000000000..7b4e99dcd30f --- /dev/null +++ b/target/hexagon/hex_interrupts.h @@ -0,0 +1,15 @@ +/* + * Copyright(c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 000000000000..348c27d801cf --- /dev/null +++ b/target/hexagon/hex_mmu.c @@ -0,0 +1,611 @@ +/* + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/qemu-print.h" +#include "cpu.h" +#include "system/cpus.h" +#include "internal.h" +#include "exec/exec-all.h" +#include "hex_mmu.h" +#include "cpu_helper.h" +#include "macros.h" +#include "sys_macros.h" +#include "reg_fields.h" + +#define GET_TLB_FIELD(ENTRY, FIELD) \ + ((uint64_t)fEXTRACTU_BITS(ENTRY, reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset)) + +/* + * PPD (physical page descriptor) is formed by putting the PTE_PA35 field + * in the MSB of the PPD + */ +#define GET_PPD(ENTRY) \ + ((GET_TLB_FIELD((ENTRY), PTE_PPD) | \ + (GET_TLB_FIELD((ENTRY), PTE_PA35) << reg_field_info[PTE_PPD].width))) + +#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, + NUM_PGSIZE_TYPES +} tlb_pgsize_t; + +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, 0111 */ +}; + +static inline int hex_tlb_pgsize(uint64_t entry) +{ + if (entry == 0) { + qemu_log_mask(CPU_LOG_MMU, "%s: Supplied TLB entry was 0!\n", __func__); + return 0; + } + int size = ctz64(entry); + g_assert(size < NUM_PGSIZE_TYPES); + return size; +} + +static inline uint32_t hex_tlb_page_size(uint64_t entry) +{ + return 1 << (TARGET_PAGE_BITS + 2 * hex_tlb_pgsize(GET_PPD(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(entry)]; + uint64_t pagenum = hex_tlb_phys_page_num(entry); + uint64_t PA = (pagenum << TARGET_PAGE_BITS) & (~pagemask); + return PA; +} + +static inline uint64_t hex_tlb_virt_addr(uint64_t entry) +{ + return GET_TLB_FIELD(entry, PTE_VPN) << TARGET_PAGE_BITS; +} + +static bool hex_dump_mmu_entry(FILE *f, uint64_t entry) +{ + if (GET_TLB_FIELD(entry, PTE_V)) { + fprintf(f, "0x%016" PRIx64 ": ", entry); + uint64_t PA = hex_tlb_phys_addr(entry); + uint64_t VA = hex_tlb_virt_addr(entry); + fprintf(f, "V:%" PRId64 " G:%" PRId64 " A1:%" PRId64 " A0:%" PRId64, + GET_TLB_FIELD(entry, PTE_V), GET_TLB_FIELD(entry, PTE_G), + GET_TLB_FIELD(entry, PTE_ATR1), GET_TLB_FIELD(entry, PTE_ATR0)); + fprintf(f, " ASID:0x%02" PRIx64 " VA:0x%08" PRIx64, + GET_TLB_FIELD(entry, PTE_ASID), VA); + fprintf(f, + " X:%" PRId64 " W:%" PRId64 " R:%" PRId64 " U:%" PRId64 + " C:%" PRId64, + GET_TLB_FIELD(entry, PTE_X), GET_TLB_FIELD(entry, PTE_W), + GET_TLB_FIELD(entry, PTE_R), GET_TLB_FIELD(entry, PTE_U), + GET_TLB_FIELD(entry, PTE_C)); + fprintf(f, " PA:0x%09" PRIx64 " SZ:%s (0x%x)", PA, + pgsize_str[hex_tlb_pgsize(entry)], hex_tlb_page_size(entry)); + fprintf(f, "\n"); + return true; + } + + /* Not valid */ + return false; +} + +void dump_mmu(CPUHexagonState *env) +{ + int i; + + HexagonCPU *cpu = env_archcpu(env); + for (i = 0; i < cpu->num_tlbs; i++) { + uint64_t entry = env->hex_tlb->entries[i]; + if (GET_TLB_FIELD(entry, PTE_V)) { + qemu_printf("0x%016" PRIx64 ": ", entry); + uint64_t PA = hex_tlb_phys_addr(entry); + uint64_t VA = hex_tlb_virt_addr(entry); + qemu_printf( + "V:%" PRId64 " G:%" PRId64 " A1:%" PRId64 " A0:%" PRId64, + GET_TLB_FIELD(entry, PTE_V), GET_TLB_FIELD(entry, PTE_G), + GET_TLB_FIELD(entry, PTE_ATR1), GET_TLB_FIELD(entry, PTE_ATR0)); + qemu_printf(" ASID:0x%02" PRIx64 " VA:0x%08" PRIx64, + GET_TLB_FIELD(entry, PTE_ASID), VA); + qemu_printf( + " X:%" PRId64 " W:%" PRId64 " R:%" PRId64 " U:%" PRId64 + " C:%" PRId64, + GET_TLB_FIELD(entry, PTE_X), GET_TLB_FIELD(entry, PTE_W), + GET_TLB_FIELD(entry, PTE_R), GET_TLB_FIELD(entry, PTE_U), + GET_TLB_FIELD(entry, PTE_C)); + qemu_printf(" PA:0x%09" PRIx64 " SZ:%s (0x%x)", PA, + pgsize_str[hex_tlb_pgsize(entry)], + hex_tlb_page_size(entry)); + qemu_printf("\n"); + } + } +} + +static inline void hex_log_tlbw(uint32_t index, uint64_t entry) +{ + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + if (qemu_log_enabled()) { + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "tlbw[%03d]: ", index); + if (!hex_dump_mmu_entry(logfile, entry)) { + fprintf(logfile, "invalid\n"); + } + qemu_log_unlock(logfile); + } + } + } +} + +void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value) +{ + uint32_t myidx = fTLB_NONPOW2WRAP(fTLB_IDXMASK(index)); + bool old_entry_valid = GET_TLB_FIELD(env->hex_tlb->entries[myidx], PTE_V); + if (old_entry_valid && hexagon_cpu_mmu_enabled(env)) { + /* FIXME - Do we have to invalidate everything here? */ + CPUState *cs = env_cpu(env); + + tlb_flush(cs); + } + env->hex_tlb->entries[myidx] = (value); + hex_log_tlbw(myidx, value); +} + +void hex_mmu_realize(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + if (cs->cpu_index == 0) { + env->hex_tlb = g_malloc0(sizeof(CPUHexagonTLBContext)); + } else { + CPUState *cpu0_s = NULL; + CPUHexagonState *env0 = NULL; + CPU_FOREACH(cpu0_s) { + assert(cpu0_s->cpu_index == 0); + env0 = &(HEXAGON_CPU(cpu0_s)->env); + break; + } + env->hex_tlb = env0->hex_tlb; + } +} + +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); +} + +static inline bool hex_tlb_entry_match_noperm(uint64_t entry, uint32_t asid, + target_ulong VA) +{ + if (GET_TLB_FIELD(entry, PTE_V)) { + if (GET_TLB_FIELD(entry, PTE_G)) { + /* Global entry - ingnore ASID */ + } else if (asid != NO_ASID) { + uint32_t tlb_asid = GET_TLB_FIELD(entry, PTE_ASID); + if (tlb_asid != asid) { + return false; + } + } + + uint64_t page_size = hex_tlb_page_size(entry); + uint64_t page_start = hex_tlb_virt_addr(entry); + page_start &= ~(page_size - 1); + if (page_start <= VA && VA < page_start + page_size) { + /* FIXME - Anything else we need to check? */ + return true; + } + } + return false; +} + +static inline void hex_tlb_entry_get_perm(CPUHexagonState *env, uint64_t entry, + MMUAccessType access_type, + int mmu_idx, int *prot, + int32_t *excp) +{ + bool perm_x = GET_TLB_FIELD(entry, PTE_X); + bool perm_w = GET_TLB_FIELD(entry, PTE_W); + bool perm_r = GET_TLB_FIELD(entry, PTE_R); + bool perm_u = GET_TLB_FIELD(entry, PTE_U); + 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; + env->cause_code = HEX_CAUSE_FETCH_NO_UPAGE; + } else if (!perm_x) { + *excp = HEX_EVENT_PRECISE; + env->cause_code = HEX_CAUSE_FETCH_NO_XPAGE; + } + break; + case MMU_DATA_LOAD: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + env->cause_code = HEX_CAUSE_PRIV_NO_UREAD; + } else if (!perm_r) { + *excp = HEX_EVENT_PRECISE; + env->cause_code = HEX_CAUSE_PRIV_NO_READ; + } + break; + case MMU_DATA_STORE: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + env->cause_code = HEX_CAUSE_PRIV_NO_UWRITE; + } else if (!perm_w) { + *excp = HEX_EVENT_PRECISE; + env->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(CPUHexagonState *env, uint64_t entry, + uint8_t asid, target_ulong VA, + MMUAccessType access_type, hwaddr *PA, + int *prot, int *size, int32_t *excp, + int mmu_idx) +{ + if (hex_tlb_entry_match_noperm(entry, asid, VA)) { + hex_tlb_entry_get_perm(env, entry, access_type, mmu_idx, prot, excp); + *PA = hex_tlb_phys_addr(entry); + *size = hex_tlb_page_size(entry); + return true; + } + return false; +} + +bool hex_tlb_find_match(CPUHexagonState *env, target_ulong VA, + MMUAccessType access_type, hwaddr *PA, int *prot, + int *size, int32_t *excp, int mmu_idx) +{ + *PA = 0; + *prot = 0; + *size = 0; + *excp = 0; + uint32_t ssr = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr); + int i; + HexagonCPU *cpu = env_archcpu(env); + for (i = 0; i < cpu->num_tlbs; i++) { + uint64_t entry = env->hex_tlb->entries[i]; + if (hex_tlb_entry_match(env, entry, asid, VA, access_type, PA, prot, + size, excp, mmu_idx)) { + return true; + } + } + return false; +} + +static uint32_t hex_tlb_lookup_by_asid(CPUHexagonState *env, uint32_t asid, + uint32_t VA) +{ + uint32_t not_found = 0x80000000; + uint32_t idx = not_found; + int i; + + HexagonCPU *cpu = env_archcpu(env); + for (i = 0; i < cpu->num_tlbs; i++) { + uint64_t entry = env->hex_tlb->entries[i]; + if (hex_tlb_entry_match_noperm(entry, asid, VA)) { + if (idx != not_found) { + env->cause_code = HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH; + break; + } + idx = i; + } + } + + if (idx == not_found) { + qemu_log_mask(CPU_LOG_MMU, "%s: 0x%x, 0x%08x => NOT FOUND\n", + __func__, asid, VA); + } else { + qemu_log_mask(CPU_LOG_MMU, "%s: 0x%x, 0x%08x => %d\n", + __func__, asid, VA, idx); + } + + return idx; +} + +/* Called from tlbp instruction */ +uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA) +{ + return hex_tlb_lookup_by_asid(env, GET_SSR_FIELD(SSR_ASID, ssr), VA); +} + +static bool hex_tlb_is_match(CPUHexagonState *env, + uint64_t entry1, uint64_t entry2, + bool consider_gbit) +{ + bool valid1 = GET_TLB_FIELD(entry1, PTE_V); + bool valid2 = GET_TLB_FIELD(entry2, PTE_V); + uint64_t size1 = hex_tlb_page_size(entry1); + uint64_t vaddr1 = hex_tlb_virt_addr(entry1); + vaddr1 &= ~(size1 - 1); + uint64_t size2 = hex_tlb_page_size(entry2); + uint64_t vaddr2 = hex_tlb_virt_addr(entry2); + vaddr2 &= ~(size2 - 1); + int asid1 = GET_TLB_FIELD(entry1, PTE_ASID); + int asid2 = GET_TLB_FIELD(entry2, PTE_ASID); + bool gbit1 = GET_TLB_FIELD(entry1, PTE_G); + bool gbit2 = GET_TLB_FIELD(entry2, PTE_G); + + 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; +} + +/* + * 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) +{ + int matches = 0; + int last_match = 0; + int i; + + HexagonCPU *cpu = env_archcpu(env); + for (i = 0; i < cpu->num_tlbs; i++) { + if (hex_tlb_is_match(env, entry, env->hex_tlb->entries[i], false)) { + matches++; + last_match = i; + } + } + + if (matches == 1) { + return last_match; + } + if (matches == 0) { + return -2; + } + return -1; +} + +static inline void print_thread(const char *str, CPUState *cs) +{ + g_assert(bql_locked()); + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *thread = &cpu->env; + 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 = %d: %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); + } +} + +/* + * A tlb_lock is taken with either a tlbfault or an explicit + * tlblock insn. The insn tlblock only advances the PC + * after the lock is acquired, similar to k0lock. + */ +void hex_tlb_lock(CPUHexagonState *env) +{ + qemu_log_mask(CPU_LOG_MMU, "hex_tlb_lock: %d\n", env->threadId); + BQL_LOCK_GUARD(); + g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1)); + + uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + 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%x, 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 */ + uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + 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 %d attempted to tlbunlock without having the " + "lock, tlb_lock state = %d\n", + env->threadId, 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) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *thread = &cpu->env; + + /* + * 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 000000000000..7c9598281af2 --- /dev/null +++ b/target/hexagon/hex_mmu.h @@ -0,0 +1,30 @@ +/* + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef HEXAGON_MMU_H +#define HEXAGON_MMU_H + +#include "max.h" + +struct CPUHexagonTLBContext { + uint64_t entries[MAX_TLB_ENTRIES]; +}; + +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_realize(CPUHexagonState *env); +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, target_ulong VA, + MMUAccessType access_type, hwaddr *PA, int *prot, + int *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(CPUHexagonState *env); +#endif diff --git a/target/hexagon/hex_regs.h b/target/hexagon/hex_regs.h index bddfc28021c6..ea8c62eba9ce 100644 --- a/target/hexagon/hex_regs.h +++ b/target/hexagon/hex_regs.h @@ -81,4 +81,119 @@ enum { 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 */ + +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 diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c new file mode 100644 index 000000000000..d0ffde958914 --- /dev/null +++ b/target/hexagon/hexswi.c @@ -0,0 +1,267 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#ifdef CONFIG_USER_ONLY +#include "exec/helper-proto.h" +#include "qemu.h" +#endif +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "arch.h" +#include "internal.h" +#include "macros.h" +#include "sys_macros.h" +#include "tcg/tcg-op.h" +#ifndef CONFIG_USER_ONLY +#include "hex_mmu.h" +#endif + +#ifndef CONFIG_USER_ONLY + + +static void set_addresses(CPUHexagonState *env, target_ulong pc_offset, + target_ulong exception_index) + +{ + ARCH_SET_SYSTEM_REG(env, HEX_SREG_ELR, + ARCH_GET_THREAD_REG(env, HEX_REG_PC) + pc_offset); + ARCH_SET_THREAD_REG(env, HEX_REG_PC, + ARCH_GET_SYSTEM_REG(env, HEX_SREG_EVB) | + (exception_index << 2)); +} + +static const char *event_name[] = { + [HEX_EVENT_RESET] = "HEX_EVENT_RESET", + [HEX_EVENT_IMPRECISE] = "HEX_EVENT_IMPRECISE", + [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) + +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + BQL_LOCK_GUARD(); + + qemu_log_mask(CPU_LOG_INT, "\t%s: event 0x%x:%s, cause 0x%x(%d)\n", + __func__, cs->exception_index, + event_name[cs->exception_index], env->cause_code, + env->cause_code); + + env->llsc_addr = ~0; + + uint32_t ssr = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + if (GET_SSR_FIELD(SSR_EX, ssr) == 1) { + ARCH_SET_SYSTEM_REG(env, HEX_SREG_DIAG, env->cause_code); + 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"); + + /* + * Detect the code sequence the standalone runtime uses + * to signal the program has finished. + * r0 = #0x18; r2 = #0x0; trap0(#0) + */ + if (env->gpr[HEX_REG_R00] == 0x18) { + exit(env->gpr[HEX_REG_R02]); + } + } + + 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%x) caught: " + "Cause code (0x%x) " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + cs->exception_index, env->cause_code, env->threadId, + ARCH_GET_THREAD_REG(env, HEX_REG_PC), + ARCH_GET_SYSTEM_REG(env, 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 %d/0x%x: " + "Unknown cause code %d/0x%x\n", + cs->exception_index, 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%x) caught: " + "Cause code (0x%x) " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + cs->exception_index, env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + ARCH_GET_SYSTEM_REG(env, 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 %d/0x%x: " + "Unknown cause code %d/0x%x\n", + cs->exception_index, cs->exception_index, env->cause_code, + env->cause_code); + break; + } + break; + + case HEX_EVENT_FPTRAP: + hexagon_ssr_set_cause(env, env->cause_code); + ARCH_SET_THREAD_REG(env, HEX_REG_PC, + ARCH_GET_SYSTEM_REG(env, HEX_SREG_EVB) | + (cs->exception_index << 2)); + 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%x) caught: " + "Cause code (0x%x) " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + cs->exception_index, env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + ARCH_GET_SYSTEM_REG(env, 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_UNSUPORTED_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 %d/0x%x: " + "Unknown cause code %d/0x%x\n", + cs->exception_index, 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%x/0x%x\n", + cs->exception_index, env->cause_code); + break; + } + + cs->exception_index = HEX_EVENT_NONE; +} + +void register_trap_exception(CPUHexagonState *env, int traptype, int imm, + target_ulong 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); +} +#endif diff --git a/target/hexagon/idef-parser/README.rst b/target/hexagon/idef-parser/README.rst index 7199177ee33e..235e3debee3c 100644 --- a/target/hexagon/idef-parser/README.rst +++ b/target/hexagon/idef-parser/README.rst @@ -637,7 +637,7 @@ tinycode for the Hexagon ``add`` instruction :: ---- 00021094 - mov_i32 pkt_has_store_s1,$0x0 + mov_i32 pkt_has_scalar_store_s1,$0x0 add_i32 tmp0,r2,r2 mov_i32 loc2,tmp0 mov_i32 new_r1,loc2 diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index a7dcd85fe43d..3316c230f8a5 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1725,7 +1725,7 @@ void gen_cancel(Context *c, YYLTYPE *locp) void gen_load_cancel(Context *c, YYLTYPE *locp) { - OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_scalar_store_s1) {\n"); OUT(c, locp, "ctx->s1_store_processed = false;\n"); OUT(c, locp, "process_store(ctx, 1);\n"); OUT(c, locp, "}\n"); @@ -1750,7 +1750,7 @@ void gen_load(Context *c, YYLTYPE *locp, HexValue *width, /* Lookup the effective address EA */ find_variable(c, locp, ea, ea); - OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_scalar_store_s1) {\n"); OUT(c, locp, "probe_noshuf_load(", ea, ", ", width, ", ctx->mem_idx);\n"); OUT(c, locp, "process_store(ctx, 1);\n"); OUT(c, locp, "}\n"); diff --git a/target/hexagon/imported/encode_pp.def b/target/hexagon/imported/encode_pp.def index 0cd30a5e8575..41e4ab9e3a26 100644 --- a/target/hexagon/imported/encode_pp.def +++ b/target/hexagon/imported/encode_pp.def @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@ */ /* + * encode32.def * Encodings for 32 bit instructions * */ @@ -341,6 +342,8 @@ DEF_ENC32(L4_pload##TAG##tnew_abs,ICLASS_LD" 1 11 "OPC" iiiii PP110tti 1--ddd DEF_ENC32(L4_pload##TAG##fnew_abs,ICLASS_LD" 1 11 "OPC" iiiii PP111tti 1--ddddd") + + /* 0 000 misc: dealloc,loadw_locked,dcfetch */ STD_LD_ENC(bzw4,"0 101") STD_LD_ENC(bzw2,"0 011") @@ -375,6 +378,7 @@ DEF_ANTICLASS32(ICLASS_LD" 1110 000----- PP------ --------",LD_ADDR_POST_REG) DEF_ENC32(L2_deallocframe, ICLASS_LD" 000 0 000 sssss PP0----- ---ddddd") DEF_ENC32(L4_return, ICLASS_LD" 011 0 000 sssss PP0000-- ---ddddd") + DEF_ENC32(L4_return_t, ICLASS_LD" 011 0 000 sssss PP0100vv ---ddddd") DEF_ENC32(L4_return_f, ICLASS_LD" 011 0 000 sssss PP1100vv ---ddddd") DEF_ENC32(L4_return_tnew_pt, ICLASS_LD" 011 0 000 sssss PP0110vv ---ddddd") @@ -382,15 +386,19 @@ DEF_ENC32(L4_return_fnew_pt, ICLASS_LD" 011 0 000 sssss PP1110vv ---ddddd") 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,13 +406,11 @@ DEF_ENC32(S2_storew_rl_st_vi, ICLASS_ST" 000 01 01sssss PP-ttttt --1010dd") 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(Y2_dcfetchbo, ICLASS_LD" 010 0 000 sssss PP0--iii iiiiiiii") - - - +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 +494,17 @@ STD_PST_ENC(rinew, "1 101","10ttt") /* 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_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 +512,34 @@ DEF_ENC32(Y2_dccleana, ICLASS_ST" 000 00 00sssss PP------ --------") 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 +582,23 @@ DEF_ENC32(J2_jumprfnewpt, ICLASS_J" 0011 011sssss PP-11-uu --------") 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)") @@ -591,7 +636,6 @@ DEF_ENC32(J2_callf, ICLASS_J" 1101 ii1iiiii PPi-0-uu iiiiiii-") /*******************************/ -/* EJP: this has to match what we have in htmldocs.py... so I will call it CJ, we can change it */ DEF_CLASS32(ICLASS_CJ" 0--- -------- PP------ --------",CJ) DEF_FIELDROW_DESC32(ICLASS_CJ" 00-- -------- -------- --------","[#0-3] pd=cmp.xx(R,#u5) ; if ([!]p0.new) jump:[h] #s9:2 ") @@ -738,12 +782,19 @@ DEF_ENC32(J2_jumprltezpt,ICLASS_CR" 0001 11isssss PPi1iiii iiiiiii-") 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 +805,7 @@ DEF_ENC32(J2_loop1i, ICLASS_CR" 1001 001IIIII PP-iiiii IIIii-II") 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") @@ -776,8 +828,66 @@ DEF_ENC32(C4_and_orn, ICLASS_CR" 1011 1011--ss PP0---tt uu----dd") DEF_ENC32(C4_or_andn, ICLASS_CR" 1011 1101--ss PP0---tt uu----dd") DEF_ENC32(C4_or_orn, ICLASS_CR" 1011 1111--ss PP0---tt uu----dd") -DEF_ENC32(C4_fastcorner9, ICLASS_CR"1011 0000--ss PP1---tt 1--1--dd") -DEF_ENC32(C4_fastcorner9_not, ICLASS_CR"1011 0001--ss PP1---tt 1--1--dd") +DEF_ENC32(C4_fastcorner9, ICLASS_CR"1011 0000--ss PP1---tt 1--1--dd") +DEF_ENC32(C4_fastcorner9_not, ICLASS_CR"1011 0001--ss PP1---tt 1--1--dd") + + + +/* 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") + + + + + @@ -956,9 +1066,9 @@ MPY_ENC(F2_dfmin, "1000","ddddd","0","0","1","1","11") MPY_ENC(F2_dfmax, "1000","ddddd","0","1","0","0","11") MPY_ENC(F2_dfmpyll, "1000","ddddd","0","1","0","1","11") -MPY_ENC(M7_dcmpyrw, "1000","ddddd","0","0","0","1","10") +MPY_ENC(M7_dcmpyrw, "1000","ddddd","0","0","0","1","10") MPY_ENC(M7_dcmpyrwc, "1000","ddddd","0","0","1","1","10") -MPY_ENC(M7_dcmpyiw, "1000","ddddd","0","1","1","0","10") +MPY_ENC(M7_dcmpyiw, "1000","ddddd","0","1","1","0","10") MPY_ENC(M7_dcmpyiwc, "1000","ddddd","0","1","1","1","10") @@ -967,14 +1077,14 @@ DEF_FIELDROW_DESC32(ICLASS_M" 1001 -------- PP------ --------","[#9] Rd=(Rss,Rtt MPY_ENC(M2_vdmpyrs_s0, "1001","ddddd","0","0","0","0","00") MPY_ENC(M2_vdmpyrs_s1, "1001","ddddd","0","0","0","1","00") -MPY_ENC(M7_wcmpyrw, "1001","ddddd","0","0","1","0","00") +MPY_ENC(M7_wcmpyrw, "1001","ddddd","0","0","1","0","00") MPY_ENC(M7_wcmpyrw_rnd, "1001","ddddd","0","0","1","1","00") -MPY_ENC(M7_wcmpyiw, "1001","ddddd","0","1","0","0","00") +MPY_ENC(M7_wcmpyiw, "1001","ddddd","0","1","0","0","00") MPY_ENC(M7_wcmpyiw_rnd, "1001","ddddd","0","1","0","1","00") -MPY_ENC(M7_wcmpyrwc, "1001","ddddd","0","1","1","0","00") +MPY_ENC(M7_wcmpyrwc, "1001","ddddd","0","1","1","0","00") MPY_ENC(M7_wcmpyrwc_rnd, "1001","ddddd","0","1","1","1","00") -MPY_ENC(M7_wcmpyiwc, "1001","ddddd","1","0","0","0","00") +MPY_ENC(M7_wcmpyiwc, "1001","ddddd","1","0","0","0","00") MPY_ENC(M7_wcmpyiwc_rnd, "1001","ddddd","1","0","0","1","00") @@ -1030,10 +1140,10 @@ MPY_ENC(F2_dfmpylh, "1010","xxxxx","0","0","0","0","11") MPY_ENC(F2_dfmpyhh, "1010","xxxxx","0","0","0","1","11") -MPY_ENC(M7_dcmpyrw_acc, "1010","xxxxx","0","0","0","1","10") -MPY_ENC(M7_dcmpyrwc_acc, "1010","xxxxx","0","0","1","1","10") -MPY_ENC(M7_dcmpyiw_acc, "1010","xxxxx","0","1","1","0","10") -MPY_ENC(M7_dcmpyiwc_acc, "1010","xxxxx","1","0","1","0","10") +MPY_ENC(M7_dcmpyrw_acc, "1010","xxxxx","0","0","0","1","10") +MPY_ENC(M7_dcmpyrwc_acc, "1010","xxxxx","0","0","1","1","10") +MPY_ENC(M7_dcmpyiw_acc, "1010","xxxxx","0","1","1","0","10") +MPY_ENC(M7_dcmpyiwc_acc, "1010","xxxxx","1","0","1","0","10") @@ -1063,7 +1173,6 @@ SP_MPY(M2_mpy_sat_rnd, "1100","ddddd","1","1","0") SP_MPY(M2_mpyu, "1100","ddddd","0","0","1") DEF_FIELDROW_DESC32(ICLASS_M" 1101 -------- PP------ --------","[#13] Rd=(Rs,Rt)") -/* EJP: same as mpyi MPY_ENC(M2_mpyui, "1101","ddddd","0","0","1","0","00") */ MPY_ENC(M2_mpyi, "1101","ddddd","0","0","0","0","00") MPY_ENC(M2_mpy_up, "1101","ddddd","0","0","0","0","01") MPY_ENC(M2_mpyu_up, "1101","ddddd","0","0","1","0","01") @@ -1266,7 +1375,6 @@ DEF_ENC32(C2_cmovenewif,ICLASS_ALU2op" 1110 1uu0iiii PP1iiiii iiiddddd") DEF_ENC32(C2_cmoveit, ICLASS_ALU2op" 1110 0uu0iiii PP0iiiii iiiddddd") DEF_ENC32(C2_cmoveif, ICLASS_ALU2op" 1110 1uu0iiii PP0iiiii iiiddddd") - DEF_FIELDROW_DESC32( ICLASS_ALU2op" 1111 -------- PP------ --------","[#15] nop") DEF_ENC32(A2_nop, ICLASS_ALU2op" 1111 -------- PP------ --------") @@ -1408,9 +1516,6 @@ DEF_FIELDROW_DESC32(ICLASS_ALU3op" 1110 -------- PP------ --------","[#14] Rese - - - /*******************************/ /* */ /* */ @@ -1508,7 +1613,6 @@ SH_RRI6_ENC(S6_rol_i_##TAGEND,MAJ4,MIN3,SMOD1 "11",DSTCHARS) DEF_FIELDROW_DESC32(ICLASS_S2op" 0000 -------- PP------ --------","[#0] Rdd=(Rss,#u6)") -/* EJP: there is actually quite a bit of space here, look at the reserved bits */ I6SHIFTTYPES(p, "0000","000","0","ddddd") I5SHIFTTYPES_NOROL(vw, "0000","010","0","ddddd") I4SHIFTTYPES(vh, "0000","100","0","ddddd") @@ -1620,8 +1724,8 @@ SH2_RR_ENC(A2_roundsat, "1000","110","-","001","ddddd") SH_RRI5_ENC(S2_asr_i_svw_trun, "1000","110", "010","ddddd") SH_RRI5_ENC(A4_bitspliti, "1000","110", "100","ddddd") -SH_RRI5_ENC(A7_clip, "1000","110", "101","ddddd") -SH_RRI5_ENC(A7_vclip, "1000","110", "110","ddddd") +SH_RRI5_ENC(A7_clip, "1000","110", "101","ddddd") +SH_RRI5_ENC(A7_vclip, "1000","110", "110","ddddd") SH2_RR_ENC(S4_clbpnorm, "1000","011","-","000","ddddd") @@ -1743,10 +1847,11 @@ SH_RRR_ENC(S2_shuffob, "0001","00-","-","10-","ddddd") SH_RRR_ENC(S2_shuffeh, "0001","00-","-","11-","ddddd") SH_RRR_ENC(S2_shuffoh, "0001","10-","-","000","ddddd") +// 001 SH_RRR_ENC(S2_vtrunewh, "0001","10-","-","010","ddddd") -SH_RRR_ENC(S6_vtrunehb_ppp, "0001","10-","-","011","ddddd") +SH_RRR_ENC(S6_vtrunehb_ppp, "0001","10-","-","011","ddddd") SH_RRR_ENC(S2_vtrunowh, "0001","10-","-","100","ddddd") -SH_RRR_ENC(S6_vtrunohb_ppp, "0001","10-","-","101","ddddd") +SH_RRR_ENC(S6_vtrunohb_ppp, "0001","10-","-","101","ddddd") SH_RRR_ENC(S2_lfsp, "0001","10-","-","110","ddddd") SH_RRR_ENC(S4_vxaddsubw, "0001","01-","-","000","ddddd") @@ -1780,8 +1885,6 @@ SH_RRR_ENC(S4_vrcrotate, "0011","11-","i","11i","ddddd") DEF_FIELDROW_DESC32(ICLASS_S3op" 0100 -------- PP------ --------","[#4] Rd=(Rs,Rt,#u3)") DEF_ENC32(S2_addasl_rrri, ICLASS_S3op" 0100 000 sssss PP0ttttt iiiddddd") - - DEF_FIELDROW_DESC32(ICLASS_S3op" 0101 -------- PP------ --------","[#5] Rd=(Rss,Rt)") SH_RRR_ENC(S2_asr_r_svw_trun, "0101","---","-","010","ddddd") SH_RRR_ENC(M4_cmpyi_wh, "0101","---","-","100","ddddd") @@ -1841,6 +1944,7 @@ DEF_FIELDROW_DESC32(ICLASS_S3op" 1010 -------- PP------ --------","[#10] Rxx=(Rs SH_RRR_ENC(S2_insertp_rp, "1010","0--","0","---","xxxxx") SH_RRR_ENC(M4_xor_xacc, "1010","10-","0","000","xxxxx") + DEF_FIELDROW_DESC32(ICLASS_S3op" 1011 -------- PP------ --------","[#11] Rxx=(Rss,Rt)") RSHIFTTYPES(p_or, "1011","000","-","-","xxxxx") RSHIFTTYPES(p_and, "1011","010","-","-","xxxxx") @@ -1848,19 +1952,19 @@ RSHIFTTYPES(p_nac, "1011","100","-","-","xxxxx") RSHIFTTYPES(p_acc, "1011","110","-","-","xxxxx") RSHIFTTYPES(p_xor, "1011","011","-","-","xxxxx") -SH_RRR_ENCX(A4_vrmaxh, "1011","001","0","001","uuuuu") -SH_RRR_ENCX(A4_vrmaxuh, "1011","001","1","001","uuuuu") -SH_RRR_ENCX(A4_vrmaxw, "1011","001","0","010","uuuuu") -SH_RRR_ENCX(A4_vrmaxuw, "1011","001","1","010","uuuuu") +SH_RRR_ENCX(A4_vrmaxh, "1011","001","0","001","uuuuu") +SH_RRR_ENCX(A4_vrmaxuh, "1011","001","1","001","uuuuu") +SH_RRR_ENCX(A4_vrmaxw, "1011","001","0","010","uuuuu") +SH_RRR_ENCX(A4_vrmaxuw, "1011","001","1","010","uuuuu") -SH_RRR_ENCX(A4_vrminh, "1011","001","0","101","uuuuu") -SH_RRR_ENCX(A4_vrminuh, "1011","001","1","101","uuuuu") -SH_RRR_ENCX(A4_vrminw, "1011","001","0","110","uuuuu") -SH_RRR_ENCX(A4_vrminuw, "1011","001","1","110","uuuuu") +SH_RRR_ENCX(A4_vrminh, "1011","001","0","101","uuuuu") +SH_RRR_ENCX(A4_vrminuh, "1011","001","1","101","uuuuu") +SH_RRR_ENCX(A4_vrminw, "1011","001","0","110","uuuuu") +SH_RRR_ENCX(A4_vrminuw, "1011","001","1","110","uuuuu") -SH_RRR_ENC(S2_vrcnegh, "1011","001","1","111","xxxxx") +SH_RRR_ENC(S2_vrcnegh, "1011","001","1","111","xxxxx") -SH_RRR_ENC(S4_vrcrotate_acc, "1011","101","i","--i","xxxxx") +SH_RRR_ENC(S4_vrcrotate_acc, "1011","101","i","--i","xxxxx") DEF_FIELDROW_DESC32(ICLASS_S3op" 1100 -------- PP------ --------","[#12] Rx=(Rs,Rt)") @@ -1874,11 +1978,6 @@ DEF_FIELDROW_DESC32(ICLASS_S3op" 1101 -------- PP------ --------","[#13] Reserve DEF_FIELDROW_DESC32(ICLASS_S3op" 1110 -------- PP------ --------","[#14] Reserved") -DEF_FIELDROW_DESC32(ICLASS_S3op" 1111 -------- PP------ --------","[#14] User Instruction") - - - - @@ -2129,3 +2228,6 @@ OP_OPI_RI(lsr,"1") DEF_FIELDROW_DESC32(ICLASS_ALU64" 1111 -------- PP------ --------","[#15] Rd=(Rs,Ru,#u6:2)") DEF_ENC32(M4_mpyri_addr_u2, ICLASS_ALU64" 1111 0ii sssss PPiddddd iiiuuuuu") DEF_ENC32(M4_mpyri_addr, ICLASS_ALU64" 1111 1ii sssss PPiddddd iiiuuuuu") + + + diff --git a/target/hexagon/imported/ldst.idef b/target/hexagon/imported/ldst.idef index 53198176a994..4e1e5d5326dd 100644 --- a/target/hexagon/imported/ldst.idef +++ b/target/hexagon/imported/ldst.idef @@ -203,6 +203,9 @@ Q6INSN(S2_storew_locked,"memw_locked(Rs32,Pd4)=Rt32", ATTRIBS(A_REGWRSIZE_4B,A_M 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 4bbcfdd5e194..f24f89f36126 --- a/target/hexagon/imported/macros.def +++ b/target/hexagon/imported/macros.def @@ -353,6 +353,12 @@ DEF_MACRO( () ) +DEF_MACRO( + fREAD_SSR, /* read SSR register */ + (READ_RREG(REG_SSR)), /* behavior */ + () +) + DEF_MACRO( fWRITE_LR, /* write lr */ WRITE_RREG(REG_LR,A), /* behavior */ @@ -371,12 +377,36 @@ DEF_MACRO( (A_IMPLICIT_WRITES_SP) ) +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 @@ DEF_MACRO( 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 @@ DEF_MACRO( ((size8s_t)((size2s_t)(A))), /* optional attributes */ ) + DEF_MACRO( fCAST2_8u, /* macro name */ ((size8u_t)((size2u_t)(A))), @@ -1532,18 +1568,209 @@ DEF_MACRO(fECHO, /* OS interface and stop/wait */ /********************************************/ +DEF_MACRO(RUNNABLE_THREADS_MAX, + (thread->processor_ptr->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, + (((TH) >= (thread->processor_ptr->runnable_threads_max)) ? 0 : (thread->processor_ptr->thread[TH]->Regs[REG_IMASK])), + () +) +DEF_MACRO(WRITE_IMASK, + if ((TH) < (thread->processor_ptr->runnable_threads_max)) { thread->processor_ptr->thread[TH]->Regs[REG_IMASK]=(VAL & reg_mutability[REG_IMASK] ); }, + (A_IMPLICIT_WRITES_IMASK_ANYTHREAD) +) + + +DEF_MACRO(WRITE_PRIO, + { + if ((TH) < (thread->processor_ptr->runnable_threads_max)) { + size4u_t tid_reg = thread->processor_ptr->thread[TH]->Regs[REG_TID]; + fINSERT_BITS(tid_reg, reg_field_info[STID_PRIO].width, reg_field_info[STID_PRIO].offset, VAL); + LOG_OTHER_THREAD_REG_WRITE(thread,REG_TID,tid_reg,TH); + } + }, + (A_IMPLICIT_WRITES_STID_PRIO_ANYTHREAD) +) + + +DEF_MACRO(DO_IASSIGNW, + { + int i; + int intbitpos = ((REG>>16)&0xF); + for (i=0;iprocessor_ptr->arch_proc_options->thread_enable_mask>>i) & 0x1)) { + fINSERT_BITS(thread->processor_ptr->thread[i]->Regs[REG_IMASK],1, intbitpos, (REG>>i) & 1); + } + } + }, + (A_IMPLICIT_WRITES_IMASK_ANYTHREAD) +) + + + + +DEF_MACRO(fDO_NMI, + { + int i; + for (i=0;iprocessor_ptr->arch_proc_options->thread_enable_mask>>i) & 0x1) ) { + if (SREG & (1<processor_ptr->thread[i]); + } + } + } + }, +) + +DEF_MACRO(fDO_TRACE, + { + fHIDE(HEX_CALLBACK(thread->processor_ptr->options->trace_callback, + thread->system_ptr,thread->processor_ptr, + thread->threadId,SREG);) + }, +) + +DEF_MACRO(DO_IASSIGNR, + { + int i; + int result=0; + int intbitpos = ((SREG>>16)&0xF); + for (i=0;iprocessor_ptr->arch_proc_options->thread_enable_mask>>i) & 0x1)) { + result |= (((thread->processor_ptr->thread[i]->Regs[REG_IMASK]>>intbitpos)&1)<processor_ptr->options->swi_callback, + thread->system_ptr,thread->processor_ptr, + thread->threadId,REG)); + LOG_GLOBAL_REG_WRITE(REG_IPEND,(GLOBAL_REG_READ(REG_IPEND) | (REG & GLOBAL_REG_READ(REG_IEL)))); + }, + (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, + {isdb_brkpt_insn(thread->processor_ptr,thread->threadId);}, + () +) + 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(fINTERNAL_CLEAR_SAMEPAGE, + /* force re-xlate at next fetch, refresh of in_user_mode, etc */ + /* Permissions change too... */ + sys_utlb_invalidate(thread->processor_ptr,thread), + /* NOTHING */ +) + +DEF_MACRO(fCLEAR_RTE_EX, + { + fLOG_REG_FIELD(SSR,SSR_EX,0); + fINTERNAL_CLEAR_SAMEPAGE(); + }, + () +) + +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, + { + if (fK0_LOCK_AVAILABLE() && sys_k0lock_queue_ready(thread)) { + warn("k0lock: T%d: PC=0x%x: PCycle=%lld",thread->threadId,thread->Regs[REG_PC],thread->processor_ptr->pstats[pcycles]); + fLOG_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_K0LOCK,1); + } else { + warn("k0lock_waiting: T%d: PC=0x%x: PCycle=%lld",thread->threadId,thread->Regs[REG_PC],thread->processor_ptr->pstats[pcycles]); + sys_waiting_for_k0_lock(thread); + } + }, + () +) + +DEF_MACRO(fCLEAR_TLB_LOCK, + { + int i; + fLOG_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_TLBLOCK,0); + for (i = 0; i < RUNNABLE_THREADS_MAX; i++) { + if(( (thread->processor_ptr->arch_proc_options->thread_enable_mask>>i) & 0x1)) { + thread->processor_ptr->thread[i]->cu_tlb_lock_waiting = 0; + } + } + }, + () +) + +DEF_MACRO(fCLEAR_K0_LOCK, + do { + warn("k0unlock: T%d: PC=0x%x: Pcycle=%lld",thread->threadId,thread->Regs[REG_PC], thread->processor_ptr->pstats[pcycles]); + sys_initiate_clear_k0_lock(thread); + } while (0), + () +) + DEF_MACRO(fALIGN_REG_FIELD_VALUE, ((VAL)<processor_ptr->global_regs[REG_##REG], + reg_field_info[FIELD].width, + reg_field_info[FIELD].offset,VAL), +) + +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 +1808,13 @@ DEF_MACRO(fREAD_REG_FIELD, /* ATTRIBS */ ) +DEF_MACRO(fREAD_GLOBAL_REG_FIELD, + fEXTRACTU_BITS(thread->processor_ptr->global_regs[REG_##REG], + reg_field_info[FIELD].width, + reg_field_info[FIELD].offset), + /* ATTRIBS */ +) + DEF_MACRO(fGET_FIELD, fEXTRACTU_BITS(VAL, reg_field_info[FIELD].width, @@ -1576,6 +1830,185 @@ DEF_MACRO(fSET_FIELD, /* ATTRIBS */ ) +DEF_MACRO(fSET_RUN_MODE_NOW, + {thread->processor_ptr->global_regs[REG_MODECTL] |= (1<last_commit_cycle = thread->processor_ptr->pcycle_counter; + sys_recalc_num_running_threads(thread->processor_ptr);}, +) + +DEF_MACRO(fIN_DEBUG_MODE, + (thread->debug_mode || (fREAD_GLOBAL_REG_FIELD(ISDBST,ISDBST_DEBUGMODE) & 1<debug_mode), + () +) + + +DEF_MACRO(fIN_DEBUG_MODE_WARN, + { + if (fREAD_GLOBAL_REG_FIELD(ISDBST,ISDBST_DEBUGMODE) & 1<processor_ptr); + } while (0), + /* NOTHING */ +) + +DEF_MACRO(fGET_RUN_MODE, + ((thread->processor_ptr->global_regs[REG_MODECTL]>>TNUM)&0x1), +) + +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, + {thread->processor_ptr->global_regs[REG_MODECTL] &= ~(1<<(TNUM+16)); + thread->last_commit_cycle = thread->processor_ptr->pcycle_counter; + sys_recalc_num_running_threads(thread->processor_ptr);}, +) + +DEF_MACRO(fGET_WAIT_MODE, + ((thread->processor_ptr->global_regs[REG_MODECTL]>>(TNUM+16))&0x1), +) + + +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, + ((INDEX) & (fPOW2_ROUNDUP(fCAST4u(thread->processor_ptr->arch_proc_options->jtlb_size)) - 1)), + () +) + +DEF_MACRO(fTLB_NONPOW2WRAP, + (((INDEX) >= thread->processor_ptr->arch_proc_options->jtlb_size) ? ((INDEX) - thread->processor_ptr->arch_proc_options->jtlb_size) : (INDEX)), + /* ATTRIBS */ +) + +DEF_MACRO(fTLBW, + do {size4u_t __myidx = fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX)); + TLB_REG_WRITE(__myidx,VALUE); + fHIDE(HEX_CALLBACK(thread->processor_ptr->options->tlbw_callback,thread->system_ptr,thread->processor_ptr,thread->threadId,__myidx);) + fHIDE(sys_tlb_write(thread,__myidx,VALUE);)} while (0), + /* 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, + fLOG_GLOBAL_REG_FIELD(MODECTL,MODECTL_E, fREAD_GLOBAL_REG_FIELD(MODECTL,MODECTL_E) | (((REG & ((1<processor_ptr))), + () +) + +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,6 +2035,11 @@ DEF_MACRO(fISYNC, ) +DEF_MACRO(fICFETCH, + , + () +) + DEF_MACRO(fDCFETCH, sys_dcfetch(thread, (REG), insn->slot), (A_MEMLIKE) @@ -1615,6 +2053,34 @@ DEF_MACRO(fICINVA, (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 +2101,12 @@ DEF_MACRO(fDCZEROA, (A_MEMLIKE) ) +DEF_MACRO(fDCINVA, + sys_dcinva(thread, (REG)), + (A_MEMLIKE) +) + + DEF_MACRO(fCHECKFORPRIV, {sys_check_privs(thread); if (EXCEPTION_DETECTED) return; }, () @@ -1645,6 +2117,16 @@ DEF_MACRO(fCHECKFORGUEST, () ) +DEF_MACRO(fTAKEN_INTERRUPT_EDGECLEAR, + { proc->global_regs[REG_IPEND] &= ~(INT_NUMTOMASK(intnum) & proc->global_regs[REG_IEL]); }, + () +) + +DEF_MACRO(fSET_IAD, + { sys_siad(thread,INT_NUMTOMASK(intnum)); thread->processor_ptr->global_regs[REG_IAD] |= INT_NUMTOMASK(intnum); }, + () +) + DEF_MACRO(fBRANCH_SPECULATE_STALL, { sys_speculate_branch_stall(thread, insn->slot, JUMP_COND(JUMP_PRED_SET), @@ -1664,3 +2146,79 @@ DEF_MACRO(IV1DEAD, , () ) + +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 7c6568e75e42..fd7ef18b3e34 100644 --- a/target/hexagon/imported/system.idef +++ b/target/hexagon/imported/system.idef @@ -25,44 +25,262 @@ /* 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); + 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(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);}) + + +/********************************************/ +/* 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_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); }) + + + +/********************************************/ +/* 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_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(Y2_dcfetchbo,"dcfetch(Rs32+#u11:3)",ATTRIBS(A_RESTRICT_PREFERSLOT0,A_DCFETCH),"Data Cache Prefetch",{fEA_RI(RsV,uiV); fDCFETCH(EA);}) +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); }) -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(Y4_l2fetch,"l2fetch(Rs32,Rt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY),"L2 Cache Prefetch", +/********************************************/ +/* 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*/ - ((RtV>>16)&0xffff), /*stride*/ - 0); /*extra attrib flags*/ + (RtV&0xff), /*height*/ + ((RtV>>8)&0xff), /*width*/ + ((RtV>>16)&0xffff), /*stride*/ + 0); /*extra attrib flags*/ }) -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*/ + 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/insn.h b/target/hexagon/insn.h index 24dcf7fe9f38..5d59430da9e1 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -66,8 +66,8 @@ struct Packet { bool pkt_has_dczeroa; - bool pkt_has_store_s0; - bool pkt_has_store_s1; + bool pkt_has_scalar_store_s0; + bool pkt_has_scalar_store_s1; bool pkt_has_hvx; Insn *vhist_insn; diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h index 32e96f00d97a..ecd99e5a4dd0 100644 --- a/target/hexagon/internal.h +++ b/target/hexagon/internal.h @@ -22,6 +22,10 @@ int hexagon_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hexagon_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +#ifndef CONFIG_USER_ONLY +int hexagon_sys_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n); +int hexagon_sys_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n); +#endif int hexagon_hvx_gdb_read_register(CPUState *env, GByteArray *mem_buf, int n); int hexagon_hvx_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n); @@ -30,5 +34,20 @@ void hexagon_debug_qreg(CPUHexagonState *env, int regnum); void hexagon_debug(CPUHexagonState *env); extern const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS]; +extern const char * const hexagon_sregnames[]; +extern const char * const hexagon_gregnames[]; + +void G_NORETURN do_raise_exception(CPUHexagonState *env, + uint32_t exception, + target_ulong PC, + uintptr_t retaddr); + +#define hexagon_cpu_mmu_enabled(env) \ + GET_SYSCFG_FIELD(SYSCFG_MMUEN, ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG)) + +#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 000000000000..bcad5bb241e5 --- /dev/null +++ b/target/hexagon/machine.c @@ -0,0 +1,87 @@ +/* + * Copyright(c) 2023-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "migration/cpu.h" +#include "cpu.h" +#include "hex_mmu.h" + +static int get_u64_ptr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + uint64_t *p = pv; + *p = qemu_get_be64(f); + return 0; +} + +static int put_u64_ptr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + qemu_put_be64(f, *((uint64_t *)pv)); + return 0; +} + +const VMStateInfo vmstate_info_uint64_ptr = { + .name = "uint64_t_pointer", + .get = get_u64_ptr, + .put = put_u64_ptr, +}; + +static int get_hex_tlb_ptr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + CPUHexagonTLBContext *tlb = pv; + for (int i = 0; i < ARRAY_SIZE(tlb->entries); i++) { + tlb->entries[i] = qemu_get_be64(f); + } + return 0; +} + +static int put_hex_tlb_ptr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + CPUHexagonTLBContext *tlb = pv; + for (int i = 0; i < ARRAY_SIZE(tlb->entries); i++) { + qemu_put_be64(f, tlb->entries[i]); + } + return 0; +} + +const VMStateInfo vmstate_info_hex_tlb_ptr = { + .name = "hex_tlb_pointer", + .get = get_hex_tlb_ptr, + .put = put_hex_tlb_ptr, +}; + +const VMStateDescription vmstate_hexagon_cpu = { + .name = "cpu", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_CPU(), + VMSTATE_UINTTL_ARRAY(env.gpr, HexagonCPU, TOTAL_PER_THREAD_REGS), + VMSTATE_UINTTL_ARRAY(env.pred, HexagonCPU, NUM_PREGS), + VMSTATE_UINTTL_ARRAY(env.t_sreg, HexagonCPU, NUM_SREGS), + VMSTATE_UINTTL_ARRAY(env.t_sreg_written, HexagonCPU, NUM_SREGS), + VMSTATE_UINTTL_ARRAY(env.greg, HexagonCPU, NUM_GREGS), + VMSTATE_UINTTL_ARRAY(env.greg_written, HexagonCPU, NUM_GREGS), + VMSTATE_UINTTL(env.next_PC, HexagonCPU), + VMSTATE_UINTTL(env.tlb_lock_state, HexagonCPU), + VMSTATE_UINTTL(env.k0_lock_state, HexagonCPU), + VMSTATE_UINTTL(env.tlb_lock_count, HexagonCPU), + VMSTATE_UINTTL(env.k0_lock_count, HexagonCPU), + VMSTATE_UINTTL(env.threadId, HexagonCPU), + VMSTATE_UINTTL(env.cause_code, HexagonCPU), + VMSTATE_UINTTL(env.wait_next_pc, HexagonCPU), + VMSTATE_POINTER(env.hex_tlb, HexagonCPU, 0, + vmstate_info_hex_tlb_ptr, CPUHexagonTLBContext *), + VMSTATE_UINT64(env.t_cycle_count, HexagonCPU), + VMSTATE_POINTER(env.g_pcycle_base, HexagonCPU, 0, + vmstate_info_uint64_ptr, uint64_t *), + VMSTATE_END_OF_LIST() + }, +}; + diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index ee3d4c88e7bd..06c1dd2f407e 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -82,7 +82,7 @@ */ #define CHECK_NOSHUF(VA, SIZE) \ do { \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ probe_noshuf_load(VA, SIZE, ctx->mem_idx); \ process_store(ctx, 1); \ } \ @@ -93,11 +93,11 @@ TCGLabel *noshuf_label = gen_new_label(); \ tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, noshuf_label); \ GET_EA; \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ probe_noshuf_load(EA, SIZE, ctx->mem_idx); \ } \ gen_set_label(noshuf_label); \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ process_store(ctx, 1); \ } \ } while (0) @@ -524,7 +524,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fLOAD(NUM, SIZE, SIGN, EA, DST) \ do { \ - check_noshuf(env, pkt_has_store_s1, slot, EA, SIZE, GETPC()); \ + check_noshuf(env, pkt_has_scalar_store_s1, slot, EA, SIZE, GETPC()); \ DST = (size##SIZE##SIGN##_t)MEM_LOAD##SIZE(env, EA, GETPC()); \ } while (0) #endif @@ -537,9 +537,6 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #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 @@ -630,8 +627,18 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #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) @@ -642,16 +649,41 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) 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 { \ ctx->dczero_addr = tcg_temp_new(); \ tcg_gen_mov_tl(ctx->dczero_addr, (REG)); \ } while (0) +#else +#define fDCZEROA(REG) ((void) REG) #endif #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 #endif + +#define fPREDUSE_TIMING() diff --git a/target/hexagon/max.h b/target/hexagon/max.h new file mode 100644 index 000000000000..fd315f09b6f7 --- /dev/null +++ b/target/hexagon/max.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _MAX_H +#define _MAX_H + +#define MAX_TLB_ENTRIES 320 +#define DTLB_ENTRIES 16 +#define ITLB_ENTRIES 16 + +#endif diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index bb4ebaae816e..d2b56b9e65e5 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -20,6 +20,7 @@ hexagon_ss = ss.source_set() 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' # @@ -244,11 +245,13 @@ decodetree_trans_funcs_generated = custom_target( 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', 'translate.c', 'op_helper.c', + 'cpu_helper.c', 'gdbstub.c', 'genptr.c', 'reg_fields.c', @@ -262,6 +265,13 @@ hexagon_ss.add(files( 'mmvec/system_ext_mmvec.c', )) +hexagon_softmmu_ss.add(files( + '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 @@ -271,7 +281,8 @@ hexagon_ss.add(files( # 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', @@ -346,12 +357,12 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs # 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 # @@ -365,7 +376,7 @@ helper_protos_generated = custom_target( '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) @@ -374,7 +385,7 @@ helper_funcs_generated = custom_target( '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) @@ -383,7 +394,7 @@ tcg_funcs_generated = custom_target( '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) @@ -392,9 +403,10 @@ analyze_funcs_generated = custom_target( '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 6da8db8ea5c5..e818a86a8c03 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/main-loop.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" @@ -24,31 +25,57 @@ #include "cpu.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 "hex_mmu.h" +#include "hw/intc/l2vic.h" +#include "hw/timer/qct-qtimer.h" +#include "hex_interrupts.h" +#endif #define SF_BIAS 127 #define SF_MANTBITS 23 /* Exceptions processing helpers */ +G_NORETURN +void do_raise_exception(CPUHexagonState *env, uint32_t exception, + target_ulong PC, uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); +#ifdef CONFIG_USER_ONLY + qemu_log_mask(CPU_LOG_INT, "%s: 0x%08x\n", __func__, exception); +#else + qemu_log_mask(CPU_LOG_INT, "%s: 0x%08x, @ %08" PRIx32 "\n", + __func__, exception, PC); + + ASSERT_DIRECT_TO_GUEST_UNSET(env, exception); +#endif + + env->gpr[HEX_REG_PC] = PC; + cs->exception_index = exception; + cpu_loop_exit_restore(cs, retaddr); + cs->halted = false; +} + 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, + target_ulong PC) { - hexagon_raise_exception_err(env, excp, 0); + hexagon_raise_exception_err(env, excp, PC); } void log_store32(CPUHexagonState *env, target_ulong addr, @@ -463,11 +490,11 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) * If the load is in slot 0 and there is a store in slot1 (that * wasn't cancelled), we have to do the store first. */ -static void check_noshuf(CPUHexagonState *env, bool pkt_has_store_s1, +static void check_noshuf(CPUHexagonState *env, bool pkt_has_scalar_store_s1, uint32_t slot, target_ulong vaddr, int size, uintptr_t ra) { - if (slot == 0 && pkt_has_store_s1 && + if (slot == 0 && pkt_has_scalar_store_s1 && ((env->slot_cancelled & (1 << 1)) == 0)) { probe_read(env, vaddr, size, MMU_USER_IDX, ra); commit_store(env, 1, ra); @@ -1149,6 +1176,120 @@ float64 HELPER(dfmpyhh)(CPUHexagonState *env, float64 RxxV, 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) +{ + BQL_LOCK_GUARD(); + g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1)); + + uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + 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%x, thread may hang\n", + env->next_PC); + env->next_PC += 4; + CPUState *cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + return; + } + env->k0_lock_state = HEX_LOCK_WAITING; + CPUState *cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + } 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) +{ + 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 */ + uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + if ((GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg) == 0) || + (env->k0_lock_state != HEX_LOCK_OWNER)) { + qemu_log_mask(LOG_GUEST_ERROR, + "thread %d attempted to unlock k0 without having the " + "lock, k0_lock state = %d, syscfg:k0 = %d\n", + env->threadId, env->k0_lock_state, + 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 */ + unsigned int this_threadId = env->threadId; + CPUHexagonState *unlock_thread = NULL; + CPUState *cs; + CPU_FOREACH(cs) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *thread = &cpu->env; + + /* + * 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) @@ -1314,6 +1455,491 @@ void HELPER(vwhist128qm)(CPUHexagonState *env, int32_t uiV) } } +#ifndef CONFIG_USER_ONLY +static void hexagon_set_vid(CPUHexagonState *env, uint32_t offset, int val) +{ + g_assert((offset == L2VIC_VID_0) || (offset == L2VIC_VID_1)); + CPUState *cs = env_cpu(env); + HexagonCPU *cpu = HEXAGON_CPU(cs); + const hwaddr pend_mem = cpu->l2vic_base_addr + offset; + cpu_physical_memory_write(pend_mem, &val, sizeof(val)); +} + +static void hexagon_clear_last_irq(CPUHexagonState *env, uint32_t offset) +{ + /* + * currently only l2vic is the only attached it uses vid0, remove + * the assert below if anther is added + */ + hexagon_set_vid(env, offset, L2VIC_CIAD_INSTRUCTION); +} + +void HELPER(ciad)(CPUHexagonState *env, uint32_t mask) +{ + uint32_t ipendad; + uint32_t iad; + + BQL_LOCK_GUARD(); + ipendad = READ_SREG(HEX_SREG_IPENDAD); + iad = fGET_FIELD(ipendad, IPENDAD_IAD); + fSET_FIELD(ipendad, IPENDAD_IAD, iad & ~(mask)); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_IPENDAD, ipendad); + hexagon_clear_last_irq(env, L2VIC_VID_0); + hex_interrupt_update(env); +} + +void HELPER(siad)(CPUHexagonState *env, uint32_t mask) +{ + uint32_t ipendad; + uint32_t iad; + + BQL_LOCK_GUARD(); + ipendad = READ_SREG(HEX_SREG_IPENDAD); + iad = fGET_FIELD(ipendad, IPENDAD_IAD); + fSET_FIELD(ipendad, IPENDAD_IAD, iad | mask); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_IPENDAD, ipendad); + 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; + + BQL_LOCK_GUARD(); + modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + 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 = ARCH_GET_SYSTEM_REG(thread_env, HEX_SREG_IMASK); + uint32_t intbitpos = (src >> 16) & 0xF; + uint32_t val = (src >> thread_env->threadId) & 0x1; + imask = deposit32(imask, intbitpos, 1, val); + ARCH_SET_SYSTEM_REG(thread_env, HEX_SREG_IMASK, imask); + + qemu_log_mask(CPU_LOG_INT, "%s: thread %d, new imask 0x%x\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; + + BQL_LOCK_GUARD(); + modectl = ARCH_GET_SYSTEM_REG(env, HEX_SREG_MODECTL); + 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 = ARCH_GET_SYSTEM_REG(thread_env, 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 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; + + BQL_LOCK_GUARD(); + qemu_log_mask(CPU_LOG_INT, "%s: check resched\n", __func__); + schedcfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SCHEDCFG); + 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, ARCH_GET_SYSTEM_REG(thread_env, 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 = ARCH_GET_SYSTEM_REG(env, HEX_SREG_BESTWAIT); + 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 %d, cur PC 0x%08x\n", __func__, + int_number, ARCH_GET_THREAD_REG(env, HEX_REG_PC)); + SET_SYSTEM_FIELD(env, HEX_SREG_BESTWAIT, BESTWAIT_PRIO, 0x1ff); + hex_raise_interrupts(env, 1 << int_number, CPU_INTERRUPT_SWI); + } +} + +void HELPER(resched)(CPUHexagonState *env) +{ + resched(env); +} + +void HELPER(wait)(CPUHexagonState *env, target_ulong 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; + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + if (found_env->threadId == tid) { + target_ulong imask = ARCH_GET_SYSTEM_REG(found_env, HEX_SREG_IMASK); + qemu_log_mask(CPU_LOG_INT, "%s: tid %d imask = 0x%x\n", + __func__, env->threadId, + (unsigned)GET_FIELD(IMASK_MASK, imask)); + return GET_FIELD(IMASK_MASK, imask); + } + } + return 0; +} + +void HELPER(setimask)(CPUHexagonState *env, uint32_t pred, uint32_t imask) +{ + CPUState *cs; + + BQL_LOCK_GUARD(); + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + + if (pred == found_env->threadId) { + SET_SYSTEM_FIELD(found_env, HEX_SREG_IMASK, IMASK_MASK, imask); + qemu_log_mask(CPU_LOG_INT, "%s: tid %d imask 0x%x\n", + __func__, found_env->threadId, imask); + hex_interrupt_update(env); + return; + } + } + hex_interrupt_update(env); +} + +static bool handle_pmu_sreg_write(CPUHexagonState *env, uint32_t reg, + uint32_t val) +{ + if (reg == HEX_SREG_PMUSTID0 || reg == HEX_SREG_PMUSTID1 + || reg == HEX_SREG_PMUCFG || reg == HEX_SREG_PMUEVTCFG + || reg == HEX_SREG_PMUEVTCFG1 + || (reg >= HEX_SREG_PMUCNT4 && reg <= HEX_SREG_PMUCNT3)) { + qemu_log_mask(LOG_UNIMP, "PMU registers not yet implemented"); + return true; + } + return false; +} + +static void modify_syscfg(CPUHexagonState *env, uint32_t val) +{ + g_assert(bql_locked()); + + uint32_t old; + uint32_t syscfg_read_only_mask = 0x80001c00; + uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + + /* clear read-only bits if they are set in the new value. */ + val &= ~syscfg_read_only_mask; + /* if read-only are currently set in syscfg keep them set. */ + val |= (syscfg & syscfg_read_only_mask); + + uint32_t tmp = val; + old = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_SYSCFG, tmp); + + /* Check for change in MMU enable */ + target_ulong old_mmu_enable = GET_SYSCFG_FIELD(SYSCFG_MMUEN, old); + uint8_t old_en = GET_SYSCFG_FIELD(SYSCFG_PCYCLEEN, old); + uint8_t old_gie = GET_SYSCFG_FIELD(SYSCFG_GIE, old); + target_ulong new_mmu_enable = + GET_SYSCFG_FIELD(SYSCFG_MMUEN, val); + if (new_mmu_enable && !old_mmu_enable) { + hex_mmu_on(env); + } else if (!new_mmu_enable && old_mmu_enable) { + hex_mmu_off(env); + } + + /* Changing pcycle enable from 0 to 1 resets the counters */ + uint8_t new_en = GET_SYSCFG_FIELD(SYSCFG_PCYCLEEN, val); + CPUState *cs; + if (old_en == 0 && new_en == 1) { + CPU_FOREACH(cs) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *_env = &cpu->env; + _env->t_cycle_count = 0; + } + } + + /* See if global interrupts are turned on */ + uint8_t new_gie = GET_SYSCFG_FIELD(SYSCFG_GIE, val); + if (!old_gie && new_gie) { + qemu_log_mask(CPU_LOG_INT, "%s: global interrupts enabled\n", __func__); + hex_interrupt_update(env); + } + + if (qemu_loglevel_mask(LOG_UNIMP)) { + int new_v2x = GET_SYSCFG_FIELD(SYSCFG_V2X, val); + if (!new_v2x) { + qemu_log("HVX: 64 byte vector length is unsupported\n"); + } + } +} + +static uint32_t hexagon_find_last_irq(CPUHexagonState *env, uint32_t vid) +{ + int offset = (vid == HEX_SREG_VID) ? L2VIC_VID_0 : L2VIC_VID_1; + CPUState *cs = env_cpu(env); + HexagonCPU *cpu = HEXAGON_CPU(cs); + const hwaddr pend_mem = cpu->l2vic_base_addr + offset; + uint32_t irq; + cpu_physical_memory_read(pend_mem, &irq, sizeof(irq)); + return irq; +} + +static void hexagon_read_timer(CPUHexagonState *env, uint32_t *low, + uint32_t *high) +{ + CPUState *cs = env_cpu(env); + HexagonCPU *cpu = HEXAGON_CPU(cs); + const hwaddr low_addr = cpu->qtimer_base_addr + QCT_QTIMER_CNTPCT_LO; + const hwaddr high_addr = cpu->qtimer_base_addr + QCT_QTIMER_CNTPCT_HI; + + cpu_physical_memory_read(low_addr, low, sizeof(*low)); + cpu_physical_memory_read(high_addr, high, sizeof(*high)); +} + +static inline QEMU_ALWAYS_INLINE void sreg_write(CPUHexagonState *env, + uint32_t reg, uint32_t val) + +{ + g_assert(bql_locked()); + if ((reg == HEX_SREG_VID) || (reg == HEX_SREG_VID1)) { + hexagon_set_vid(env, (reg == HEX_SREG_VID) ? L2VIC_VID_0 : L2VIC_VID_1, + val); + ARCH_SET_SYSTEM_REG(env, reg, val); + } else if (reg == HEX_SREG_SYSCFG) { + modify_syscfg(env, val); + } else if (reg == HEX_SREG_IMASK) { + val = GET_FIELD(IMASK_MASK, val); + ARCH_SET_SYSTEM_REG(env, reg, val); + } else if (reg == HEX_SREG_PCYCLELO) { + hexagon_set_sys_pcycle_count_low(env, val); + } else if (reg == HEX_SREG_PCYCLEHI) { + hexagon_set_sys_pcycle_count_high(env, val); + } else if (!handle_pmu_sreg_write(env, reg, val)) { + if (reg >= HEX_SREG_GLB_START) { + ARCH_SET_SYSTEM_REG(env, reg, val); + } else { + ARCH_SET_SYSTEM_REG(env, reg, val); + } + } +} + +void HELPER(sreg_write)(CPUHexagonState *env, uint32_t reg, uint32_t val) +{ + BQL_LOCK_GUARD(); + sreg_write(env, reg, val); +} + +void hexagon_gdb_sreg_write(CPUHexagonState *env, uint32_t reg, uint32_t val) +{ + BQL_LOCK_GUARD(); + sreg_write(env, reg, val); + /* + * The above is needed to run special logic for regs like syscfg, but it + * won't set read-only bits. This will: + */ + ARCH_SET_SYSTEM_REG(env, reg, val); +} + +void HELPER(sreg_write_pair)(CPUHexagonState *env, uint32_t reg, uint64_t val) +{ + BQL_LOCK_GUARD(); + sreg_write(env, reg, val & 0xFFFFFFFF); + sreg_write(env, reg + 1, val >> 32); +} + +static inline QEMU_ALWAYS_INLINE uint32_t sreg_read(CPUHexagonState *env, + uint32_t reg) +{ + g_assert(bql_locked()); + if (reg == HEX_SREG_PMUSTID0 || reg == HEX_SREG_PMUSTID1 + || reg == HEX_SREG_PMUCFG || reg == HEX_SREG_PMUEVTCFG + || reg == HEX_SREG_PMUEVTCFG1 + || (reg >= HEX_SREG_PMUCNT4 && reg <= HEX_SREG_PMUCNT3)) { + qemu_log_mask(LOG_UNIMP, "PMU registers not yet implemented"); + return 0; + } + if ((reg == HEX_SREG_VID) || (reg == HEX_SREG_VID1)) { + const uint32_t vid = hexagon_find_last_irq(env, reg); + ARCH_SET_SYSTEM_REG(env, reg, vid); + } else if ((reg == HEX_SREG_TIMERLO) || (reg == HEX_SREG_TIMERHI)) { + uint32_t low = 0; + uint32_t high = 0; + hexagon_read_timer(env, &low, &high); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_TIMERLO, low); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_TIMERHI, high); + } else if (reg == HEX_SREG_BADVA) { + target_ulong ssr = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + if (GET_SSR_FIELD(SSR_BVS, ssr)) { + return ARCH_GET_SYSTEM_REG(env, HEX_SREG_BADVA1); + } + return ARCH_GET_SYSTEM_REG(env, HEX_SREG_BADVA0); + } + return ARCH_GET_SYSTEM_REG(env, reg); +} + +uint32_t HELPER(sreg_read)(CPUHexagonState *env, uint32_t reg) +{ + BQL_LOCK_GUARD(); + return sreg_read(env, reg); +} + +uint32_t hexagon_sreg_read(CPUHexagonState *env, uint32_t reg) +{ + return sreg_read(env, reg); +} + +uint64_t HELPER(sreg_read_pair)(CPUHexagonState *env, uint32_t reg) +{ + BQL_LOCK_GUARD(); + if (reg == HEX_SREG_TIMERLO) { + uint32_t low = 0; + uint32_t high = 0; + hexagon_read_timer(env, &low, &high); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_TIMERLO, low); + ARCH_SET_SYSTEM_REG(env, HEX_SREG_TIMERHI, high); + } else if (reg == HEX_SREG_PCYCLELO) { + return hexagon_get_sys_pcycle_count(env); + } + return (uint64_t)sreg_read(env, reg) | + (((uint64_t)sreg_read(env, reg + 1)) << 32); +} + +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: { + target_ulong ssr = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SSR); + int ssr_ce = GET_SSR_FIELD(SSR_CE, ssr); + return ssr_ce ? hexagon_get_sys_pcycle_count(env) : 0; + } + default: + return (uint64_t)hexagon_greg_read(env, reg) | + ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32); + } +} + +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 %d prio = 0x%x\n", + __func__, found_env->threadId, prio); + resched(env); + return; + } + } + g_assert_not_reached(); +} + +void HELPER(nmi)(CPUHexagonState *env, uint32_t thread_mask) +{ + 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 f2a58d486c55..50b8c26f8bfa 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_FPDBZE, 26, 1) 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, 10) + + +/* 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 000000000000..186afc9a6fe8 --- /dev/null +++ b/target/hexagon/sys_macros.h @@ -0,0 +1,238 @@ +/* + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-clause + */ + +#ifndef HEXAGON_SYS_MACROS_H +#define HEXAGON_SYS_MACROS_H + +/* + * Macro definitions for Hexagon system mode + */ + +#ifndef CONFIG_USER_ONLY + +#define READ_SREG(NUM) ARCH_GET_SYSTEM_REG(env, NUM) +#define READ_SGP0() ARCH_GET_SYSTEM_REG(env, HEX_SREG_SGP0) +#define READ_SGP1() ARCH_GET_SYSTEM_REG(env, HEX_SREG_SGP1) +#define READ_SGP10() ((uint64_t)ARCH_GET_SYSTEM_REG(env, HEX_SREG_SGP0) | \ + ((uint64_t)ARCH_GET_SYSTEM_REG(env, HEX_SREG_SGP1) << 32)) + +#define WRITE_SREG(NUM, VAL) log_sreg_write(env, NUM, VAL, slot) +#define WRITE_SGP0(VAL) log_sreg_write(env, HEX_SREG_SGP0, VAL, slot) +#define WRITE_SGP1(VAL) log_sreg_write(env, HEX_SREG_SGP1, VAL, slot) +#define WRITE_SGP10(VAL) \ + do { \ + log_sreg_write(env, HEX_SREG_SGP0, (VAL) & 0xFFFFFFFF, slot); \ + log_sreg_write(env, HEX_SREG_SGP1, (VAL) >> 32, slot); \ + } while (0) + +#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 { \ + uint32_t regval = ARCH_GET_SYSTEM_REG(ENV, REG); \ + fINSERT_BITS(regval, reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset, (VAL)); \ + ARCH_SET_SYSTEM_REG(ENV, REG, regval); \ + } 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, ARCH_GET_SYSTEM_REG(ENV, 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() (READ_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; \ + cpu_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 fFRAMECHECK(ADDR, EA) g_assert_not_reached(); + +#define fTRAP(TRAPTYPE, IMM) \ + register_trap_exception(env, TRAPTYPE, IMM, PC) + +#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, READ_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 = 0; \ + tmp = ARCH_GET_SYSTEM_REG(env, 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(env_archcpu(env)->num_tlbs)) - 1)) + +#define fTLB_NONPOW2WRAP(INDEX) \ + (((INDEX) >= env_archcpu(env)->num_tlbs) ? \ + ((INDEX) - env_archcpu(env)->num_tlbs) : \ + (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) \ + (env->hex_tlb->entries[fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX))]) +#define fTLBR_EXTENDED(INDEX) \ + (env->hex_tlb->entries[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) \ + ((GET_FIELD(ISDBST_DEBUGMODE, ARCH_GET_SYSTEM_REG(env, HEX_SREG_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 = HEX_DC_STATE_INVALID | 0x00; \ + } while (0) +#endif + +#endif + +#define NUM_TLB_REGS(x) (env_archcpu(env)->num_tlbs) + +#endif diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index fe7858703c8c..5ab327e43670 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -49,6 +49,7 @@ static const AnalyzeInsn opcode_analyze[XX_LAST_OPCODE] = { TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; TCGv hex_pred[NUM_PREGS]; TCGv hex_slot_cancelled; +TCGv hex_next_PC; TCGv hex_new_value_usr; TCGv hex_store_addr[STORES_MAX]; TCGv hex_store_width[STORES_MAX]; @@ -57,9 +58,19 @@ TCGv_i64 hex_store_val64[STORES_MAX]; TCGv hex_llsc_addr; TCGv hex_llsc_val; TCGv_i64 hex_llsc_val_i64; +TCGv_i64 hex_cycle_count; TCGv hex_vstore_addr[VSTORES_MAX]; TCGv hex_vstore_size[VSTORES_MAX]; TCGv hex_vstore_pending[VSTORES_MAX]; +static bool need_next_PC(DisasContext *ctx); + +#ifndef CONFIG_USER_ONLY +TCGv hex_greg[NUM_GREGS]; +TCGv hex_t_sreg[NUM_SREGS]; +TCGv_ptr hex_g_sreg_ptr; +TCGv hex_g_sreg[NUM_SREGS]; +TCGv hex_cause_code; +#endif static const char * const hexagon_prednames[] = { "p0", "p1", "p2", "p3" @@ -113,11 +124,28 @@ intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum, return offset; } -static void gen_exception_raw(int excp) +static void gen_exception(int excp, target_ulong PC) +{ + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp), + tcg_constant_tl(PC)); +} + +#ifndef CONFIG_USER_ONLY +static inline void gen_precise_exception(int excp, target_ulong PC) { - gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); + tcg_gen_movi_tl(hex_cause_code, excp); + gen_exception(HEX_EVENT_PRECISE, PC); } +static inline void gen_pcycle_counters(DisasContext *ctx) +{ + if (ctx->pcycle_enabled) { + tcg_gen_addi_i64(hex_cycle_count, hex_cycle_count, ctx->num_cycles); + ctx->num_cycles = 0; + } +} +#endif + static void gen_exec_counters(DisasContext *ctx) { tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_PKT_CNT], @@ -126,6 +154,10 @@ static void gen_exec_counters(DisasContext *ctx) 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) @@ -156,6 +188,9 @@ static void gen_end_tb(DisasContext *ctx) gen_exec_counters(ctx); + if (ctx->need_next_pc) { + tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC); + } if (ctx->branch_cond != TCG_COND_NEVER) { if (ctx->branch_cond != TCG_COND_ALWAYS) { TCGLabel *skip = gen_new_label(); @@ -185,13 +220,14 @@ static void gen_end_tb(DisasContext *ctx) 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; - } static int read_packet_words(CPUHexagonState *env, DisasContext *ctx, @@ -232,34 +268,135 @@ static bool check_for_attrib(Packet *pkt, int attrib) return false; } +#ifndef CONFIG_USER_ONLY +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; +} +#endif + static bool need_slot_cancelled(Packet *pkt) { /* We only need slot_cancelled for conditional store instructions */ for (int i = 0; i < pkt->num_insns; i++) { uint16_t opcode = pkt->insn[i].opcode; - if (GET_ATTRIB(opcode, A_CONDEXEC) && - GET_ATTRIB(opcode, A_SCALAR_STORE)) { + if (GET_ATTRIB(opcode, A_CONDEXEC)) { return true; } } return false; } -static bool need_next_PC(DisasContext *ctx) +#ifndef CONFIG_USER_ONLY +static bool sreg_write_to_global(int reg_num) { - Packet *pkt = ctx->pkt; + 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; +} - /* Check for conditional control flow or HW loop end */ +static bool has_sreg_write_to_global(Packet const *pkt) +{ for (int i = 0; i < pkt->num_insns; i++) { - uint16_t opcode = pkt->insn[i].opcode; - if (GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) { - return true; + 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_to_global(reg_num)) { + return true; + } + } else if (opcode == Y4_tfrspcp) { + /* Write to a sreg pair */ + int reg_num = insn->regno[0]; + if (sreg_write_to_global(reg_num)) { + return true; + } + if (sreg_write_to_global(reg_num + 1)) { + return true; + } } - if (GET_ATTRIB(opcode, A_HWLOOP0_END) || - GET_ATTRIB(opcode, A_HWLOOP1_END)) { - 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_to_global(pkt)) { + return true; + } +#endif + return false; +} + + +static bool need_next_PC(DisasContext *ctx) +{ + Packet *pkt = ctx->pkt; + if (pkt->pkt_has_cof || ctx->pkt_ends_tb) { + for (int i = 0; i < pkt->num_insns; i++) { + uint16_t opcode = pkt->insn[i].opcode; + if ((GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) || + GET_ATTRIB(opcode, A_HWLOOP0_END) || + GET_ATTRIB(opcode, A_HWLOOP1_END)) { + return true; + } } } + /* + * We end the TB on some instructions that do not change the flow (for + * other reasons). In these cases, we must set pc too, as the insn won't + * do it themselves. + */ + if (ctx->pkt_ends_tb && !check_for_attrib(pkt, A_COF)) { + return true; + } return false; } @@ -291,6 +428,16 @@ static void mark_implicit_reg_write(DisasContext *ctx, int attrib, int rnum) } } +#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); @@ -302,6 +449,12 @@ static void mark_implicit_reg_writes(DisasContext *ctx) mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1); mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_USR, HEX_REG_USR); mark_implicit_reg_write(ctx, A_FPOP, HEX_REG_USR); + +#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) @@ -400,7 +553,14 @@ static void analyze_packet(DisasContext *ctx) static void gen_start_packet(DisasContext *ctx) { Packet *pkt = ctx->pkt; +#ifndef CONFIG_USER_ONLY + 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; +#else target_ulong next_PC = ctx->base.pc_next + pkt->encod_pkt_size_in_bytes; +#endif int i; /* Clear out the disassembly context */ @@ -408,6 +568,10 @@ static void gen_start_packet(DisasContext *ctx) 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; @@ -440,6 +604,25 @@ static void gen_start_packet(DisasContext *ctx) * gen phase, so clear it again. */ bitmap_zero(ctx->pregs_written, NUM_PREGS); +#ifndef CONFIG_USER_ONLY + for (i = 0; i < NUM_SREGS; 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->t_sreg_new_value[reg_num] = tcg_temp_new(); + tcg_gen_mov_tl(ctx->t_sreg_new_value[reg_num], hex_t_sreg[reg_num]); + } + } + 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(pkt)) { @@ -448,12 +631,14 @@ static void gen_start_packet(DisasContext *ctx) ctx->branch_taken = NULL; if (pkt->pkt_has_cof) { ctx->branch_taken = tcg_temp_new(); - if (pkt->pkt_has_multi_cof) { - tcg_gen_movi_tl(ctx->branch_taken, 0); - } - if (need_next_PC(ctx)) { - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC); - } + } + if (pkt->pkt_has_multi_cof) { + tcg_gen_movi_tl(ctx->branch_taken, 0); + } + ctx->pkt_ends_tb = pkt_ends_tb(pkt); + ctx->need_next_pc = need_next_PC(ctx); + if (ctx->need_next_pc) { + tcg_gen_movi_tl(hex_next_PC, next_PC); } /* Preload the predicated registers into get_result_gpr(ctx, i) */ @@ -558,7 +743,7 @@ static void gen_insn(DisasContext *ctx) 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); } } @@ -589,6 +774,59 @@ static void gen_reg_writes(DisasContext *ctx) } } +#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 old_reg = tcg_temp_new(); + 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); + /* This can change processor state, so end the TB */ + ctx->base.is_jmp = DISAS_NORETURN; + } else if ((reg_num == HEX_SREG_STID) || + (reg_num == HEX_SREG_IMASK) || + (reg_num == HEX_SREG_IPENDAD)) { + if (reg_num < HEX_SREG_GLB_START) { + 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]); + } + /* This can change the interrupt state, so end the TB */ + gen_helper_pending_interrupt(tcg_env); + ctx->base.is_jmp = DISAS_NORETURN; + } else if ((reg_num == HEX_SREG_BESTWAIT) || + (reg_num == HEX_SREG_SCHEDCFG)) { + /* This can trigger resched interrupt, so end the TB */ + gen_helper_resched(tcg_env); + ctx->base.is_jmp = DISAS_NORETURN; + } + + if (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 */ @@ -693,11 +931,11 @@ static void process_store_log(DisasContext *ctx) * the memory accesses overlap. */ Packet *pkt = ctx->pkt; - if (pkt->pkt_has_store_s1) { + if (pkt->pkt_has_scalar_store_s1) { g_assert(!pkt->pkt_has_dczeroa); process_store(ctx, 1); } - if (pkt->pkt_has_store_s0) { + if (pkt->pkt_has_scalar_store_s0) { g_assert(!pkt->pkt_has_dczeroa); process_store(ctx, 0); } @@ -779,6 +1017,7 @@ static void gen_commit_hvx(DisasContext *ctx) } } +static const int PCYCLES_PER_PACKET = 3; static void update_exec_counters(DisasContext *ctx) { Packet *pkt = ctx->pkt; @@ -798,6 +1037,7 @@ static void update_exec_counters(DisasContext *ctx) } ctx->num_packets++; + ctx->num_cycles += PCYCLES_PER_PACKET; ctx->num_insns += num_real_insns; ctx->num_hvx_insns += num_hvx_insns; } @@ -822,8 +1062,9 @@ static void gen_commit_packet(DisasContext *ctx) * involved in committing the packet. */ Packet *pkt = ctx->pkt; - bool has_store_s0 = pkt->pkt_has_store_s0; - bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed); + bool has_store_s0 = pkt->pkt_has_scalar_store_s0; + bool has_store_s1 = + (pkt->pkt_has_scalar_store_s1 && !ctx->s1_store_processed); bool has_hvx_store = pkt_has_hvx_store(pkt); if (pkt->pkt_has_dczeroa) { /* @@ -886,6 +1127,10 @@ static void gen_commit_packet(DisasContext *ctx) process_store_log(ctx); gen_reg_writes(ctx); +#if !defined(CONFIG_USER_ONLY) + gen_greg_writes(ctx); + gen_sreg_writes(ctx); +#endif gen_pred_writes(ctx); if (pkt->pkt_has_hvx) { gen_commit_hvx(ctx); @@ -898,7 +1143,7 @@ static void gen_commit_packet(DisasContext *ctx) pkt->vhist_insn->generate(ctx); } - if (pkt->pkt_has_cof) { + if (ctx->pkt_ends_tb || ctx->base.is_jmp == DISAS_NORETURN) { gen_end_tb(ctx); } } @@ -912,7 +1157,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) nwords = read_packet_words(env, ctx, words); if (!nwords) { - gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); + hex_gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); return; } @@ -927,7 +1172,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) gen_commit_packet(ctx); ctx->base.pc_next += pkt.encod_pkt_size_in_bytes; } else { - gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); + hex_gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); } } @@ -938,13 +1183,16 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, 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_cycles = 0; ctx->num_insns = 0; ctx->num_hvx_insns = 0; ctx->branch_cond = TCG_COND_NEVER; ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP); ctx->short_circuit = hex_cpu->short_circuit; + ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); + ctx->need_next_pc = false; } static void hexagon_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -1050,6 +1298,26 @@ void hexagon_translate_init(void) opcode_init(); +#ifndef CONFIG_USER_ONLY + for (i = 0; i < NUM_GREGS; i++) { + hex_greg[i] = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, greg[i]), + hexagon_gregnames[i]); + } + hex_g_sreg_ptr = tcg_global_mem_new_ptr(tcg_env, + offsetof(CPUHexagonState, g_sreg), "hex_g_sreg_ptr"); + for (i = 0; i < NUM_SREGS; i++) { + if (i < HEX_SREG_GLB_START) { + hex_t_sreg[i] = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, t_sreg[i]), + hexagon_sregnames[i]); + } else { + hex_g_sreg[i] = tcg_global_mem_new(hex_g_sreg_ptr, + i * sizeof(target_ulong), + 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]), @@ -1071,6 +1339,15 @@ void hexagon_translate_init(void) 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"); + hex_cycle_count = tcg_global_mem_new_i64(tcg_env, + offsetof(CPUHexagonState, t_cycle_count), "t_cycle_count"); +#ifndef CONFIG_USER_ONLY + hex_cause_code = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, cause_code), "cause_code"); +#endif + hex_next_PC = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, next_PC), "next_PC"); + 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 d251e2233fda..ad1a2f404534 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -39,6 +39,14 @@ typedef struct DisasContext { int reg_log_idx; DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); +#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 t_sreg_new_value[NUM_SREGS]; + TCGv greg_new_value[NUM_GREGS]; +#endif int preg_log[PRED_WRITES_MAX]; int preg_log_idx; DECLARE_BITMAP(pregs_written, NUM_PREGS); @@ -75,10 +83,42 @@ typedef struct DisasContext { TCGv new_pred_value[NUM_PREGS]; TCGv branch_taken; TCGv dczero_addr; + bool pcycle_enabled; + bool pkt_ends_tb; + bool need_next_pc; + 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) +{ + if (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) +{ + 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)) { @@ -267,6 +307,7 @@ static inline void ctx_log_qreg_read(DisasContext *ctx, } extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; +extern TCGv hex_next_PC; extern TCGv hex_pred[NUM_PREGS]; extern TCGv hex_slot_cancelled; extern TCGv hex_new_value_usr; @@ -280,6 +321,15 @@ extern TCGv_i64 hex_llsc_val_i64; 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 hex_greg[NUM_GREGS]; +extern TCGv hex_t_sreg[NUM_SREGS]; +extern TCGv_ptr hex_g_sreg_ptr; +extern TCGv hex_g_sreg[NUM_SREGS]; +#endif + + +void hex_gen_exception_end_tb(DisasContext *ctx, int excp); void process_store(DisasContext *ctx, int slot_num); diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 24f7f8f2f1ca..3a62d937668b 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -104,6 +104,14 @@ tests_avr_system_thorough = [ 'avr_mega2560', ] +test_timeouts += { + 'hexagon_minivm': 180, +} + +tests_hexagon_system_quick = [ + 'hexagon_minivm', +] + tests_i386_system_thorough = [ 'i386_tuxrun', ] diff --git a/tests/functional/test_hexagon_minivm.py b/tests/functional/test_hexagon_minivm.py new file mode 100755 index 000000000000..8e09774e092f --- /dev/null +++ b/tests/functional/test_hexagon_minivm.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# +# Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +import os +from glob import glob +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + +class MiniVMTest(QemuSystemTest): + + timeout = 180 + GUEST_ENTRY = 0xc0000000 + + REPO = 'https://artifacts.codelinaro.org/artifactory' + ASSET_TARBALL = \ + Asset(f'{REPO}/codelinaro-toolchain-for-hexagon/' + '19.1.5/hexagon_minivm_2024_Dec_15.tar.gz', + 'd7920b5ff14bed5a10b23ada7d4eb927ede08635281f25067e0d5711feee2c2a') + + def test_minivm(self): + self.set_machine('virt') + self.archive_extract(self.ASSET_TARBALL) + rootfs_path = f'{self.workdir}/hexagon-unknown-linux-musl-rootfs' + kernel_path = f'{rootfs_path}/boot/minivm' + + assert(os.path.exists(kernel_path)) + for test_bin_path in glob(f'{rootfs_path}/boot/test_*'): + print(f'# Testing "{os.path.basename(test_bin_path)}"') + + vm = self.get_vm() + vm.add_args('-kernel', kernel_path, + '-device', + f'loader,addr={hex(self.GUEST_ENTRY)},file={test_bin_path}') + vm.launch() + vm.wait() + self.assertEqual(vm.exitcode(), 0) + +if __name__ == '__main__': + QemuSystemTest.main()