mirror of https://github.com/xemu-project/xemu.git
Initial LoongArch support.
-----BEGIN PGP SIGNATURE----- iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmKeiRYdHHJpY2hhcmQu aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV85Kgf/buBy5+0y51NKOpTI zwFzvuQoWyMwb1nz1ag3i0Sk5fk72zmQI13fwfCgyUZckT165qISa2hohnzl4zVZ CO0uZl44makET+uqJn5h2VXSM7Wf+jv0UzbCElVQuEFt0t1bIPbco0pTx/TojBb+ +YKN4jobvJiLVhD1wDVJqp/2r9gcnX11EWZk+ZC+pIiEqYZpWRcQdEGVh4Ymhig8 0LK/8HRSyw0AecX/01hcGWvYCC0ldFicwN69AD42BqM+7WD+3jnV8FJL8qqq766G xuCNHz0eDcVgfw9bCEyhFmhgiBFvOXNCtyDOV0qVn7eee9nIrFZcsGyBqeI/T1el e7uz8Q== =l8TD -----END PGP SIGNATURE----- Merge tag 'pull-la-20220606' of https://gitlab.com/rth7680/qemu into staging Initial LoongArch support. # -----BEGIN PGP SIGNATURE----- # # iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmKeiRYdHHJpY2hhcmQu # aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV85Kgf/buBy5+0y51NKOpTI # zwFzvuQoWyMwb1nz1ag3i0Sk5fk72zmQI13fwfCgyUZckT165qISa2hohnzl4zVZ # CO0uZl44makET+uqJn5h2VXSM7Wf+jv0UzbCElVQuEFt0t1bIPbco0pTx/TojBb+ # +YKN4jobvJiLVhD1wDVJqp/2r9gcnX11EWZk+ZC+pIiEqYZpWRcQdEGVh4Ymhig8 # 0LK/8HRSyw0AecX/01hcGWvYCC0ldFicwN69AD42BqM+7WD+3jnV8FJL8qqq766G # xuCNHz0eDcVgfw9bCEyhFmhgiBFvOXNCtyDOV0qVn7eee9nIrFZcsGyBqeI/T1el # e7uz8Q== # =l8TD # -----END PGP SIGNATURE----- # gpg: Signature made Mon 06 Jun 2022 04:09:10 PM PDT # gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F # gpg: issuer "richard.henderson@linaro.org" # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [ultimate] * tag 'pull-la-20220606' of https://gitlab.com/rth7680/qemu: (43 commits) target/loongarch: 'make check-tcg' support tests/tcg/loongarch64: Add hello/memory test in loongarch64 system target/loongarch: Add gdb support. hw/loongarch: Add LoongArch virt power manager support. hw/loongarch: Add LoongArch load elf function. hw/loongarch: Add LoongArch ls7a rtc device support hw/loongarch: Add some devices support for 3A5000. Enable common virtio pci support for LoongArch hw/loongarch: Add irq hierarchy for the system hw/intc: Add LoongArch extioi interrupt controller(EIOINTC) hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI) hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC) hw/loongarch: Add LoongArch ipi interrupt support(IPI) hw/loongarch: Add support loongson3 virt machine type. target/loongarch: Add timer related instructions support. target/loongarch: Add other core instructions support target/loongarch: Add TLB instruction support target/loongarch: Add LoongArch IOCSR instruction target/loongarch: Add LoongArch CSR instruction target/loongarch: Add constant timer support ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
9b1f588549
24
MAINTAINERS
24
MAINTAINERS
|
@ -212,6 +212,13 @@ S: Maintained
|
|||
F: target/hppa/
|
||||
F: disas/hppa.c
|
||||
|
||||
LoongArch TCG CPUs
|
||||
M: Song Gao <gaosong@loongson.cn>
|
||||
M: Xiaojuan Yang <yangxiaojuan@loongson.cn>
|
||||
S: Maintained
|
||||
F: target/loongarch/
|
||||
F: tests/tcg/loongarch64/
|
||||
|
||||
M68K TCG CPUs
|
||||
M: Laurent Vivier <laurent@vivier.eu>
|
||||
S: Maintained
|
||||
|
@ -1116,6 +1123,23 @@ F: include/hw/net/lasi_82596.h
|
|||
F: include/hw/pci-host/dino.h
|
||||
F: pc-bios/hppa-firmware.img
|
||||
|
||||
LoongArch Machines
|
||||
------------------
|
||||
Virt
|
||||
M: Xiaojuan Yang <yangxiaojuan@loongson.cn>
|
||||
M: Song Gao <gaosong@loongson.cn>
|
||||
S: Maintained
|
||||
F: docs/system/loongarch/loongson3.rst
|
||||
F: configs/targets/loongarch64-softmmu.mak
|
||||
F: configs/devices/loongarch64-softmmu/default.mak
|
||||
F: hw/loongarch/
|
||||
F: include/hw/loongarch/virt.h
|
||||
F: include/hw/intc/loongarch_*.h
|
||||
F: hw/intc/loongarch_*.c
|
||||
F: include/hw/pci-host/ls7a.h
|
||||
F: hw/rtc/ls7a_rtc.c
|
||||
F: gdb-xml/loongarch*.xml
|
||||
|
||||
M68K Machines
|
||||
-------------
|
||||
an5206
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Default configuration for loongarch64-softmmu
|
||||
|
||||
CONFIG_LOONGARCH_VIRT=y
|
|
@ -0,0 +1,4 @@
|
|||
TARGET_ARCH=loongarch64
|
||||
TARGET_BASE_ARCH=loongarch
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_XML_FILES= gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu64.xml
|
|
@ -1831,6 +1831,7 @@ fi
|
|||
: ${cross_prefix_arm="arm-linux-gnueabihf-"}
|
||||
: ${cross_prefix_armeb="$cross_prefix_arm"}
|
||||
: ${cross_prefix_hexagon="hexagon-unknown-linux-musl-"}
|
||||
: ${cross_prefix_loongarch64="loongarch64-unknown-linux-gnu-"}
|
||||
: ${cross_prefix_hppa="hppa-linux-gnu-"}
|
||||
: ${cross_prefix_i386="i686-linux-gnu-"}
|
||||
: ${cross_prefix_m68k="m68k-linux-gnu-"}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
:orphan:
|
||||
|
||||
==========================================
|
||||
loongson3 virt generic platform (``virt``)
|
||||
==========================================
|
||||
|
||||
The ``virt`` machine use gpex host bridge, and there are some
|
||||
emulated devices on virt board, such as loongson7a RTC device,
|
||||
IOAPIC device, ACPI device and so on.
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
|
||||
The ``virt`` machine supports:
|
||||
- Gpex host bridge
|
||||
- Ls7a RTC device
|
||||
- Ls7a IOAPIC device
|
||||
- Ls7a ACPI device
|
||||
- Fw_cfg device
|
||||
- PCI/PCIe devices
|
||||
- Memory device
|
||||
- CPU device. Type: Loongson-3A5000.
|
||||
|
||||
CPU and machine Type
|
||||
--------------------
|
||||
|
||||
The ``qemu-system-loongarch64`` provides emulation for virt
|
||||
machine. You can specify the machine type ``virt`` and
|
||||
cpu type ``Loongson-3A5000``.
|
||||
|
||||
Boot options
|
||||
------------
|
||||
|
||||
Now the ``virt`` machine can run test program in ELF format and the
|
||||
method of compiling is in target/loongarch/README.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-loongarch64 -machine virt -m 4G -cpu Loongson-3A5000 \
|
||||
-smp 1 -kernel hello -monitor none -display none \
|
||||
-chardev file,path=hello.out,id=output -serial chardev:output
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||
<feature name="org.gnu.gdb.loongarch.base">
|
||||
<reg name="r0" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r1" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r2" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r3" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r4" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r5" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r6" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r7" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r8" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r9" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r10" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r11" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r12" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r13" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r14" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r15" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r16" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r17" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r18" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r19" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r20" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r21" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r22" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r23" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r24" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r25" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r26" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r27" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r28" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r29" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r30" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="r31" bitsize="64" type="uint64" group="general"/>
|
||||
<reg name="pc" bitsize="64" type="code_ptr" group="general"/>
|
||||
<reg name="badvaddr" bitsize="64" type="code_ptr" group="general"/>
|
||||
</feature>
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||
<feature name="org.gnu.gdb.loongarch.fpu">
|
||||
|
||||
<union id="fpu64type">
|
||||
<field name="f" type="ieee_single"/>
|
||||
<field name="d" type="ieee_double"/>
|
||||
</union>
|
||||
|
||||
<reg name="f0" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f1" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f2" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f3" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f4" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f5" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f6" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f7" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f8" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f9" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f10" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f11" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f12" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f13" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f14" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f15" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f16" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f17" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f18" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f19" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f20" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f21" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f22" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f23" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f24" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f25" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f26" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f27" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f28" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f29" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f30" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="f31" bitsize="64" type="fpu64type" group="float"/>
|
||||
<reg name="fcc0" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcc1" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcc2" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcc3" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcc4" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcc5" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcc6" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcc7" bitsize="8" type="uint8" group="float"/>
|
||||
<reg name="fcsr" bitsize="32" type="uint32" group="float"/>
|
||||
</feature>
|
|
@ -50,6 +50,7 @@ source avr/Kconfig
|
|||
source cris/Kconfig
|
||||
source hppa/Kconfig
|
||||
source i386/Kconfig
|
||||
source loongarch/Kconfig
|
||||
source m68k/Kconfig
|
||||
source microblaze/Kconfig
|
||||
source mips/Kconfig
|
||||
|
|
|
@ -87,3 +87,18 @@ config M68K_IRQC
|
|||
|
||||
config NIOS2_VIC
|
||||
bool
|
||||
|
||||
config LOONGARCH_IPI
|
||||
bool
|
||||
|
||||
config LOONGARCH_PCH_PIC
|
||||
bool
|
||||
select UNIMP
|
||||
|
||||
config LOONGARCH_PCH_MSI
|
||||
select MSI_NONBROKEN
|
||||
bool
|
||||
select UNIMP
|
||||
|
||||
config LOONGARCH_EXTIOI
|
||||
bool
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Loongson 3A5000 ext interrupt controller emulation
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/loongarch/virt.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/intc/loongarch_extioi.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level)
|
||||
{
|
||||
int ipnum, cpu, found, irq_index, irq_mask;
|
||||
|
||||
ipnum = s->sw_ipmap[irq / 32];
|
||||
cpu = s->sw_coremap[irq];
|
||||
irq_index = irq / 32;
|
||||
irq_mask = 1 << (irq & 0x1f);
|
||||
|
||||
if (level) {
|
||||
/* if not enable return false */
|
||||
if (((s->enable[irq_index]) & irq_mask) == 0) {
|
||||
return;
|
||||
}
|
||||
s->coreisr[cpu][irq_index] |= irq_mask;
|
||||
found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS);
|
||||
set_bit(irq, s->sw_isr[cpu][ipnum]);
|
||||
if (found < EXTIOI_IRQS) {
|
||||
/* other irq is handling, need not update parent irq level */
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
s->coreisr[cpu][irq_index] &= ~irq_mask;
|
||||
clear_bit(irq, s->sw_isr[cpu][ipnum]);
|
||||
found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS);
|
||||
if (found < EXTIOI_IRQS) {
|
||||
/* other irq is handling, need not update parent irq level */
|
||||
return;
|
||||
}
|
||||
}
|
||||
qemu_set_irq(s->parent_irq[cpu][ipnum], level);
|
||||
}
|
||||
|
||||
static void extioi_setirq(void *opaque, int irq, int level)
|
||||
{
|
||||
LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
|
||||
trace_loongarch_extioi_setirq(irq, level);
|
||||
if (level) {
|
||||
/*
|
||||
* s->isr should be used in vmstate structure,
|
||||
* but it not support 'unsigned long',
|
||||
* so we have to switch it.
|
||||
*/
|
||||
set_bit(irq, (unsigned long *)s->isr);
|
||||
} else {
|
||||
clear_bit(irq, (unsigned long *)s->isr);
|
||||
}
|
||||
extioi_update_irq(s, irq, level);
|
||||
}
|
||||
|
||||
static uint64_t extioi_readw(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
|
||||
unsigned long offset = addr & 0xffff;
|
||||
uint32_t index, cpu, ret = 0;
|
||||
|
||||
switch (offset) {
|
||||
case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1:
|
||||
index = (offset - EXTIOI_NODETYPE_START) >> 2;
|
||||
ret = s->nodetype[index];
|
||||
break;
|
||||
case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1:
|
||||
index = (offset - EXTIOI_IPMAP_START) >> 2;
|
||||
ret = s->ipmap[index];
|
||||
break;
|
||||
case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1:
|
||||
index = (offset - EXTIOI_ENABLE_START) >> 2;
|
||||
ret = s->enable[index];
|
||||
break;
|
||||
case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1:
|
||||
index = (offset - EXTIOI_BOUNCE_START) >> 2;
|
||||
ret = s->bounce[index];
|
||||
break;
|
||||
case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1:
|
||||
index = ((offset - EXTIOI_COREISR_START) & 0x1f) >> 2;
|
||||
cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
|
||||
ret = s->coreisr[cpu][index];
|
||||
break;
|
||||
case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1:
|
||||
index = (offset - EXTIOI_COREMAP_START) >> 2;
|
||||
ret = s->coremap[index];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
trace_loongarch_extioi_readw(addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\
|
||||
uint32_t mask, int level)
|
||||
{
|
||||
uint32_t val;
|
||||
int irq;
|
||||
|
||||
val = mask & s->isr[index];
|
||||
irq = ctz32(val);
|
||||
while (irq != 32) {
|
||||
/*
|
||||
* enable bit change from 0 to 1,
|
||||
* need to update irq by pending bits
|
||||
*/
|
||||
extioi_update_irq(s, irq + index * 32, level);
|
||||
val &= ~(1 << irq);
|
||||
irq = ctz32(val);
|
||||
}
|
||||
}
|
||||
|
||||
static void extioi_writew(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
|
||||
int i, cpu, index, old_data, irq;
|
||||
uint32_t offset;
|
||||
|
||||
trace_loongarch_extioi_writew(addr, val);
|
||||
offset = addr & 0xffff;
|
||||
|
||||
switch (offset) {
|
||||
case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1:
|
||||
index = (offset - EXTIOI_NODETYPE_START) >> 2;
|
||||
s->nodetype[index] = val;
|
||||
break;
|
||||
case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1:
|
||||
/*
|
||||
* ipmap cannot be set at runtime, can be set only at the beginning
|
||||
* of intr driver, need not update upper irq level
|
||||
*/
|
||||
index = (offset - EXTIOI_IPMAP_START) >> 2;
|
||||
s->ipmap[index] = val;
|
||||
/*
|
||||
* loongarch only support little endian,
|
||||
* so we paresd the value with little endian.
|
||||
*/
|
||||
val = cpu_to_le64(val);
|
||||
for (i = 0; i < 4; i++) {
|
||||
uint8_t ipnum;
|
||||
ipnum = val & 0xff;
|
||||
ipnum = ctz32(ipnum);
|
||||
ipnum = (ipnum >= 4) ? 0 : ipnum;
|
||||
s->sw_ipmap[index * 4 + i] = ipnum;
|
||||
val = val >> 8;
|
||||
}
|
||||
|
||||
break;
|
||||
case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1:
|
||||
index = (offset - EXTIOI_ENABLE_START) >> 2;
|
||||
old_data = s->enable[index];
|
||||
s->enable[index] = val;
|
||||
|
||||
/* unmask irq */
|
||||
val = s->enable[index] & ~old_data;
|
||||
extioi_enable_irq(s, index, val, 1);
|
||||
|
||||
/* mask irq */
|
||||
val = ~s->enable[index] & old_data;
|
||||
extioi_enable_irq(s, index, val, 0);
|
||||
break;
|
||||
case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1:
|
||||
/* do not emulate hw bounced irq routing */
|
||||
index = (offset - EXTIOI_BOUNCE_START) >> 2;
|
||||
s->bounce[index] = val;
|
||||
break;
|
||||
case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1:
|
||||
index = ((offset - EXTIOI_COREISR_START) & 0x1f) >> 2;
|
||||
cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
|
||||
old_data = s->coreisr[cpu][index];
|
||||
s->coreisr[cpu][index] = old_data & ~val;
|
||||
/* write 1 to clear interrrupt */
|
||||
old_data &= val;
|
||||
irq = ctz32(old_data);
|
||||
while (irq != 32) {
|
||||
extioi_update_irq(s, irq + index * 32, 0);
|
||||
old_data &= ~(1 << irq);
|
||||
irq = ctz32(old_data);
|
||||
}
|
||||
break;
|
||||
case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1:
|
||||
irq = offset - EXTIOI_COREMAP_START;
|
||||
index = irq / 4;
|
||||
s->coremap[index] = val;
|
||||
/*
|
||||
* loongarch only support little endian,
|
||||
* so we paresd the value with little endian.
|
||||
*/
|
||||
val = cpu_to_le64(val);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
cpu = val & 0xff;
|
||||
cpu = ctz32(cpu);
|
||||
cpu = (cpu >= 4) ? 0 : cpu;
|
||||
val = val >> 8;
|
||||
|
||||
if (s->sw_coremap[irq + i] == cpu) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (test_bit(irq, (unsigned long *)s->isr)) {
|
||||
/*
|
||||
* lower irq at old cpu and raise irq at new cpu
|
||||
*/
|
||||
extioi_update_irq(s, irq + i, 0);
|
||||
s->sw_coremap[irq + i] = cpu;
|
||||
extioi_update_irq(s, irq + i, 1);
|
||||
} else {
|
||||
s->sw_coremap[irq + i] = cpu;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps extioi_ops = {
|
||||
.read = extioi_readw,
|
||||
.write = extioi_writew,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 8,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_loongarch_extioi = {
|
||||
.name = TYPE_LOONGARCH_EXTIOI,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT),
|
||||
VMSTATE_UINT32_2DARRAY(coreisr, LoongArchExtIOI, LOONGARCH_MAX_VCPUS,
|
||||
EXTIOI_IRQS_GROUP_COUNT),
|
||||
VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI,
|
||||
EXTIOI_IRQS_NODETYPE_COUNT / 2),
|
||||
VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOI, EXTIOI_IRQS / 32),
|
||||
VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOI, EXTIOI_IRQS / 32),
|
||||
VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE / 4),
|
||||
VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOI, EXTIOI_IRQS / 4),
|
||||
VMSTATE_UINT8_ARRAY(sw_ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE),
|
||||
VMSTATE_UINT8_ARRAY(sw_coremap, LoongArchExtIOI, EXTIOI_IRQS),
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void loongarch_extioi_instance_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj);
|
||||
int i, cpu, pin;
|
||||
|
||||
for (i = 0; i < EXTIOI_IRQS; i++) {
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
|
||||
}
|
||||
|
||||
qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS);
|
||||
|
||||
for (cpu = 0; cpu < LOONGARCH_MAX_VCPUS; cpu++) {
|
||||
memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops,
|
||||
s, "extioi_iocsr", 0x900);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_iocsr_mem[cpu]);
|
||||
for (pin = 0; pin < LS3A_INTC_IP; pin++) {
|
||||
qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1);
|
||||
}
|
||||
}
|
||||
memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops,
|
||||
s, "extioi_system_mem", 0x900);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_system_mem);
|
||||
}
|
||||
|
||||
static void loongarch_extioi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_loongarch_extioi;
|
||||
}
|
||||
|
||||
static const TypeInfo loongarch_extioi_info = {
|
||||
.name = TYPE_LOONGARCH_EXTIOI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_init = loongarch_extioi_instance_init,
|
||||
.instance_size = sizeof(struct LoongArchExtIOI),
|
||||
.class_init = loongarch_extioi_class_init,
|
||||
};
|
||||
|
||||
static void loongarch_extioi_register_types(void)
|
||||
{
|
||||
type_register_static(&loongarch_extioi_info);
|
||||
}
|
||||
|
||||
type_init(loongarch_extioi_register_types)
|
|
@ -0,0 +1,242 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch ipi interrupt support
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/intc/loongarch_ipi.h"
|
||||
#include "hw/irq.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/loongarch/virt.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "target/loongarch/internals.h"
|
||||
#include "trace.h"
|
||||
|
||||
static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPICore *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
int index = 0;
|
||||
|
||||
addr &= 0xff;
|
||||
switch (addr) {
|
||||
case CORE_STATUS_OFF:
|
||||
ret = s->status;
|
||||
break;
|
||||
case CORE_EN_OFF:
|
||||
ret = s->en;
|
||||
break;
|
||||
case CORE_SET_OFF:
|
||||
ret = 0;
|
||||
break;
|
||||
case CORE_CLEAR_OFF:
|
||||
ret = 0;
|
||||
break;
|
||||
case CORE_BUF_20 ... CORE_BUF_38 + 4:
|
||||
index = (addr - CORE_BUF_20) >> 2;
|
||||
ret = s->buf[index];
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
|
||||
break;
|
||||
}
|
||||
|
||||
trace_loongarch_ipi_read(size, (uint64_t)addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_ipi_data(target_ulong val)
|
||||
{
|
||||
int i, mask, data;
|
||||
|
||||
data = val >> 32;
|
||||
mask = (val >> 27) & 0xf;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if ((mask >> i) & 1) {
|
||||
data &= ~(0xff << (i * 8));
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static void ipi_send(uint64_t val)
|
||||
{
|
||||
int cpuid, data;
|
||||
CPULoongArchState *env;
|
||||
|
||||
cpuid = (val >> 16) & 0x3ff;
|
||||
/* IPI status vector */
|
||||
data = 1 << (val & 0x1f);
|
||||
qemu_mutex_lock_iothread();
|
||||
CPUState *cs = qemu_get_cpu(cpuid);
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
env = &cpu->env;
|
||||
loongarch_cpu_set_irq(cpu, IRQ_IPI, 1);
|
||||
qemu_mutex_unlock_iothread();
|
||||
address_space_stl(&env->address_space_iocsr, 0x1008,
|
||||
data, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
|
||||
}
|
||||
|
||||
static void mail_send(uint64_t val)
|
||||
{
|
||||
int cpuid, data;
|
||||
hwaddr addr;
|
||||
CPULoongArchState *env;
|
||||
|
||||
cpuid = (val >> 16) & 0x3ff;
|
||||
addr = 0x1020 + (val & 0x1c);
|
||||
CPUState *cs = qemu_get_cpu(cpuid);
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
env = &cpu->env;
|
||||
data = get_ipi_data(val);
|
||||
address_space_stl(&env->address_space_iocsr, addr,
|
||||
data, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
static void any_send(uint64_t val)
|
||||
{
|
||||
int cpuid, data;
|
||||
hwaddr addr;
|
||||
CPULoongArchState *env;
|
||||
|
||||
cpuid = (val >> 16) & 0x3ff;
|
||||
addr = val & 0xffff;
|
||||
CPUState *cs = qemu_get_cpu(cpuid);
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
env = &cpu->env;
|
||||
data = get_ipi_data(val);
|
||||
address_space_stl(&env->address_space_iocsr, addr,
|
||||
data, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
IPICore *s = opaque;
|
||||
int index = 0;
|
||||
|
||||
addr &= 0xff;
|
||||
trace_loongarch_ipi_write(size, (uint64_t)addr, val);
|
||||
switch (addr) {
|
||||
case CORE_STATUS_OFF:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
|
||||
break;
|
||||
case CORE_EN_OFF:
|
||||
s->en = val;
|
||||
break;
|
||||
case CORE_SET_OFF:
|
||||
s->status |= val;
|
||||
if (s->status != 0 && (s->status & s->en) != 0) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
break;
|
||||
case CORE_CLEAR_OFF:
|
||||
s->status &= ~val;
|
||||
if (s->status == 0 && s->en != 0) {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
break;
|
||||
case CORE_BUF_20 ... CORE_BUF_38 + 4:
|
||||
index = (addr - CORE_BUF_20) >> 2;
|
||||
s->buf[index] = val;
|
||||
break;
|
||||
case IOCSR_IPI_SEND:
|
||||
ipi_send(val);
|
||||
break;
|
||||
case IOCSR_MAIL_SEND:
|
||||
mail_send(val);
|
||||
break;
|
||||
case IOCSR_ANY_SEND:
|
||||
any_send(val);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps loongarch_ipi_ops = {
|
||||
.read = loongarch_ipi_readl,
|
||||
.write = loongarch_ipi_writel,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 8,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void loongarch_ipi_init(Object *obj)
|
||||
{
|
||||
int cpu;
|
||||
LoongArchMachineState *lams;
|
||||
LoongArchIPI *s = LOONGARCH_IPI(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
Object *machine = qdev_get_machine();
|
||||
ObjectClass *mc = object_get_class(machine);
|
||||
/* 'lams' should be initialized */
|
||||
if (!strcmp(MACHINE_CLASS(mc)->name, "none")) {
|
||||
return;
|
||||
}
|
||||
lams = LOONGARCH_MACHINE(machine);
|
||||
for (cpu = 0; cpu < MAX_IPI_CORE_NUM; cpu++) {
|
||||
memory_region_init_io(&s->ipi_iocsr_mem[cpu], obj, &loongarch_ipi_ops,
|
||||
&lams->ipi_core[cpu], "loongarch_ipi_iocsr", 0x100);
|
||||
sysbus_init_mmio(sbd, &s->ipi_iocsr_mem[cpu]);
|
||||
qdev_init_gpio_out(DEVICE(obj), &lams->ipi_core[cpu].irq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ipi_core = {
|
||||
.name = "ipi-single",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(status, IPICore),
|
||||
VMSTATE_UINT32(en, IPICore),
|
||||
VMSTATE_UINT32(set, IPICore),
|
||||
VMSTATE_UINT32(clear, IPICore),
|
||||
VMSTATE_UINT32_ARRAY(buf, IPICore, MAX_IPI_MBX_NUM * 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_loongarch_ipi = {
|
||||
.name = TYPE_LOONGARCH_IPI,
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT_ARRAY(ipi_core, LoongArchMachineState,
|
||||
MAX_IPI_CORE_NUM, 0,
|
||||
vmstate_ipi_core, IPICore),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void loongarch_ipi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_loongarch_ipi;
|
||||
}
|
||||
|
||||
static const TypeInfo loongarch_ipi_info = {
|
||||
.name = TYPE_LOONGARCH_IPI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(LoongArchIPI),
|
||||
.instance_init = loongarch_ipi_init,
|
||||
.class_init = loongarch_ipi_class_init,
|
||||
};
|
||||
|
||||
static void loongarch_ipi_register_types(void)
|
||||
{
|
||||
type_register_static(&loongarch_ipi_info);
|
||||
}
|
||||
|
||||
type_init(loongarch_ipi_register_types)
|
|
@ -0,0 +1,73 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU Loongson 7A1000 msi interrupt controller.
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/intc/loongarch_pch_msi.h"
|
||||
#include "hw/intc/loongarch_pch_pic.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void loongarch_msi_mem_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque);
|
||||
int irq_num = val & 0xff;
|
||||
|
||||
trace_loongarch_msi_set_irq(irq_num);
|
||||
assert(irq_num < PCH_MSI_IRQ_NUM);
|
||||
qemu_set_irq(s->pch_msi_irq[irq_num], 1);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps loongarch_pch_msi_ops = {
|
||||
.read = loongarch_msi_mem_read,
|
||||
.write = loongarch_msi_mem_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void pch_msi_irq_handler(void *opaque, int irq, int level)
|
||||
{
|
||||
LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque);
|
||||
|
||||
qemu_set_irq(s->pch_msi_irq[irq], level);
|
||||
}
|
||||
|
||||
static void loongarch_pch_msi_init(Object *obj)
|
||||
{
|
||||
LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->msi_mmio, obj, &loongarch_pch_msi_ops,
|
||||
s, TYPE_LOONGARCH_PCH_MSI, 0x8);
|
||||
sysbus_init_mmio(sbd, &s->msi_mmio);
|
||||
msi_nonbroken = true;
|
||||
|
||||
qdev_init_gpio_out(DEVICE(obj), s->pch_msi_irq, PCH_MSI_IRQ_NUM);
|
||||
qdev_init_gpio_in(DEVICE(obj), pch_msi_irq_handler, PCH_MSI_IRQ_NUM);
|
||||
}
|
||||
|
||||
static const TypeInfo loongarch_pch_msi_info = {
|
||||
.name = TYPE_LOONGARCH_PCH_MSI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(LoongArchPCHMSI),
|
||||
.instance_init = loongarch_pch_msi_init,
|
||||
};
|
||||
|
||||
static void loongarch_pch_msi_register_types(void)
|
||||
{
|
||||
type_register_static(&loongarch_pch_msi_info);
|
||||
}
|
||||
|
||||
type_init(loongarch_pch_msi_register_types)
|
|
@ -0,0 +1,431 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU Loongson 7A1000 I/O interrupt controller.
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/loongarch/virt.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/intc/loongarch_pch_pic.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
|
||||
{
|
||||
unsigned long val;
|
||||
int irq;
|
||||
|
||||
if (level) {
|
||||
val = mask & s->intirr & ~s->int_mask;
|
||||
if (val) {
|
||||
irq = find_first_bit(&val, 64);
|
||||
s->intisr |= 0x1ULL << irq;
|
||||
qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
|
||||
}
|
||||
} else {
|
||||
val = mask & s->intisr;
|
||||
if (val) {
|
||||
irq = find_first_bit(&val, 64);
|
||||
s->intisr &= ~(0x1ULL << irq);
|
||||
qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pch_pic_irq_handler(void *opaque, int irq, int level)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
|
||||
uint64_t mask = 1ULL << irq;
|
||||
|
||||
assert(irq < PCH_PIC_IRQ_NUM);
|
||||
trace_loongarch_pch_pic_irq_handler(irq, level);
|
||||
|
||||
if (s->intedge & mask) {
|
||||
/* Edge triggered */
|
||||
if (level) {
|
||||
if ((s->last_intirr & mask) == 0) {
|
||||
s->intirr |= mask;
|
||||
}
|
||||
s->last_intirr |= mask;
|
||||
} else {
|
||||
s->last_intirr &= ~mask;
|
||||
}
|
||||
} else {
|
||||
/* Level triggered */
|
||||
if (level) {
|
||||
s->intirr |= mask;
|
||||
s->last_intirr |= mask;
|
||||
} else {
|
||||
s->intirr &= ~mask;
|
||||
s->last_intirr &= ~mask;
|
||||
}
|
||||
}
|
||||
pch_pic_update_irq(s, mask, level);
|
||||
}
|
||||
|
||||
static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
|
||||
uint64_t val = 0;
|
||||
uint32_t offset = addr & 0xfff;
|
||||
|
||||
switch (offset) {
|
||||
case PCH_PIC_INT_ID_LO:
|
||||
val = PCH_PIC_INT_ID_VAL;
|
||||
break;
|
||||
case PCH_PIC_INT_ID_HI:
|
||||
val = PCH_PIC_INT_ID_NUM;
|
||||
break;
|
||||
case PCH_PIC_INT_MASK_LO:
|
||||
val = (uint32_t)s->int_mask;
|
||||
break;
|
||||
case PCH_PIC_INT_MASK_HI:
|
||||
val = s->int_mask >> 32;
|
||||
break;
|
||||
case PCH_PIC_INT_EDGE_LO:
|
||||
val = (uint32_t)s->intedge;
|
||||
break;
|
||||
case PCH_PIC_INT_EDGE_HI:
|
||||
val = s->intedge >> 32;
|
||||
break;
|
||||
case PCH_PIC_HTMSI_EN_LO:
|
||||
val = (uint32_t)s->htmsi_en;
|
||||
break;
|
||||
case PCH_PIC_HTMSI_EN_HI:
|
||||
val = s->htmsi_en >> 32;
|
||||
break;
|
||||
case PCH_PIC_AUTO_CTRL0_LO:
|
||||
case PCH_PIC_AUTO_CTRL0_HI:
|
||||
case PCH_PIC_AUTO_CTRL1_LO:
|
||||
case PCH_PIC_AUTO_CTRL1_HI:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
trace_loongarch_pch_pic_low_readw(size, addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
|
||||
{
|
||||
uint64_t mask = 0xffffffff00000000;
|
||||
uint64_t data = target;
|
||||
|
||||
return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
|
||||
}
|
||||
|
||||
static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
|
||||
uint32_t offset, old_valid, data = (uint32_t)value;
|
||||
uint64_t old, int_mask;
|
||||
offset = addr & 0xfff;
|
||||
|
||||
trace_loongarch_pch_pic_low_writew(size, addr, data);
|
||||
|
||||
switch (offset) {
|
||||
case PCH_PIC_INT_MASK_LO:
|
||||
old = s->int_mask;
|
||||
s->int_mask = get_writew_val(old, data, 0);
|
||||
old_valid = (uint32_t)old;
|
||||
if (old_valid & ~data) {
|
||||
pch_pic_update_irq(s, (old_valid & ~data), 1);
|
||||
}
|
||||
if (~old_valid & data) {
|
||||
pch_pic_update_irq(s, (~old_valid & data), 0);
|
||||
}
|
||||
break;
|
||||
case PCH_PIC_INT_MASK_HI:
|
||||
old = s->int_mask;
|
||||
s->int_mask = get_writew_val(old, data, 1);
|
||||
old_valid = (uint32_t)(old >> 32);
|
||||
int_mask = old_valid & ~data;
|
||||
if (int_mask) {
|
||||
pch_pic_update_irq(s, int_mask << 32, 1);
|
||||
}
|
||||
int_mask = ~old_valid & data;
|
||||
if (int_mask) {
|
||||
pch_pic_update_irq(s, int_mask << 32, 0);
|
||||
}
|
||||
break;
|
||||
case PCH_PIC_INT_EDGE_LO:
|
||||
s->intedge = get_writew_val(s->intedge, data, 0);
|
||||
break;
|
||||
case PCH_PIC_INT_EDGE_HI:
|
||||
s->intedge = get_writew_val(s->intedge, data, 1);
|
||||
break;
|
||||
case PCH_PIC_INT_CLEAR_LO:
|
||||
if (s->intedge & data) {
|
||||
s->intirr &= (~data);
|
||||
pch_pic_update_irq(s, data, 0);
|
||||
s->intisr &= (~data);
|
||||
}
|
||||
break;
|
||||
case PCH_PIC_INT_CLEAR_HI:
|
||||
value <<= 32;
|
||||
if (s->intedge & value) {
|
||||
s->intirr &= (~value);
|
||||
pch_pic_update_irq(s, value, 0);
|
||||
s->intisr &= (~value);
|
||||
}
|
||||
break;
|
||||
case PCH_PIC_HTMSI_EN_LO:
|
||||
s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
|
||||
break;
|
||||
case PCH_PIC_HTMSI_EN_HI:
|
||||
s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
|
||||
break;
|
||||
case PCH_PIC_AUTO_CTRL0_LO:
|
||||
case PCH_PIC_AUTO_CTRL0_HI:
|
||||
case PCH_PIC_AUTO_CTRL1_LO:
|
||||
case PCH_PIC_AUTO_CTRL1_HI:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
|
||||
uint64_t val = 0;
|
||||
uint32_t offset = addr & 0xfff;
|
||||
|
||||
switch (offset) {
|
||||
case STATUS_LO_START:
|
||||
val = (uint32_t)(s->intisr & (~s->int_mask));
|
||||
break;
|
||||
case STATUS_HI_START:
|
||||
val = (s->intisr & (~s->int_mask)) >> 32;
|
||||
break;
|
||||
case POL_LO_START:
|
||||
val = (uint32_t)s->int_polarity;
|
||||
break;
|
||||
case POL_HI_START:
|
||||
val = s->int_polarity >> 32;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
trace_loongarch_pch_pic_high_readw(size, addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
|
||||
uint32_t offset, data = (uint32_t)value;
|
||||
offset = addr & 0xfff;
|
||||
|
||||
trace_loongarch_pch_pic_high_writew(size, addr, data);
|
||||
|
||||
switch (offset) {
|
||||
case STATUS_LO_START:
|
||||
s->intisr = get_writew_val(s->intisr, data, 0);
|
||||
break;
|
||||
case STATUS_HI_START:
|
||||
s->intisr = get_writew_val(s->intisr, data, 1);
|
||||
break;
|
||||
case POL_LO_START:
|
||||
s->int_polarity = get_writew_val(s->int_polarity, data, 0);
|
||||
break;
|
||||
case POL_HI_START:
|
||||
s->int_polarity = get_writew_val(s->int_polarity, data, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
|
||||
uint64_t val = 0;
|
||||
uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
|
||||
int64_t offset_tmp;
|
||||
|
||||
switch (offset) {
|
||||
case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
|
||||
offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
|
||||
if (offset_tmp >= 0 && offset_tmp < 64) {
|
||||
val = s->htmsi_vector[offset_tmp];
|
||||
}
|
||||
break;
|
||||
case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
|
||||
offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
|
||||
if (offset_tmp >= 0 && offset_tmp < 64) {
|
||||
val = s->route_entry[offset_tmp];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
trace_loongarch_pch_pic_readb(size, addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
|
||||
uint64_t data, unsigned size)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
|
||||
int32_t offset_tmp;
|
||||
uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
|
||||
|
||||
trace_loongarch_pch_pic_writeb(size, addr, data);
|
||||
|
||||
switch (offset) {
|
||||
case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
|
||||
offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
|
||||
if (offset_tmp >= 0 && offset_tmp < 64) {
|
||||
s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
|
||||
}
|
||||
break;
|
||||
case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
|
||||
offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
|
||||
if (offset_tmp >= 0 && offset_tmp < 64) {
|
||||
s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
|
||||
.read = loongarch_pch_pic_low_readw,
|
||||
.write = loongarch_pch_pic_low_writew,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
|
||||
.read = loongarch_pch_pic_high_readw,
|
||||
.write = loongarch_pch_pic_high_writew,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
|
||||
.read = loongarch_pch_pic_readb,
|
||||
.write = loongarch_pch_pic_writeb,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void loongarch_pch_pic_reset(DeviceState *d)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d);
|
||||
int i;
|
||||
|
||||
s->int_mask = -1;
|
||||
s->htmsi_en = 0x0;
|
||||
s->intedge = 0x0;
|
||||
s->intclr = 0x0;
|
||||
s->auto_crtl0 = 0x0;
|
||||
s->auto_crtl1 = 0x0;
|
||||
for (i = 0; i < 64; i++) {
|
||||
s->route_entry[i] = 0x1;
|
||||
s->htmsi_vector[i] = 0x0;
|
||||
}
|
||||
s->intirr = 0x0;
|
||||
s->intisr = 0x0;
|
||||
s->last_intirr = 0x0;
|
||||
s->int_polarity = 0x0;
|
||||
}
|
||||
|
||||
static void loongarch_pch_pic_init(Object *obj)
|
||||
{
|
||||
LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem32_low, obj,
|
||||
&loongarch_pch_pic_reg32_low_ops,
|
||||
s, PCH_PIC_NAME(.reg32_part1), 0x100);
|
||||
memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops,
|
||||
s, PCH_PIC_NAME(.reg8), 0x2a0);
|
||||
memory_region_init_io(&s->iomem32_high, obj,
|
||||
&loongarch_pch_pic_reg32_high_ops,
|
||||
s, PCH_PIC_NAME(.reg32_part2), 0xc60);
|
||||
sysbus_init_mmio(sbd, &s->iomem32_low);
|
||||
sysbus_init_mmio(sbd, &s->iomem8);
|
||||
sysbus_init_mmio(sbd, &s->iomem32_high);
|
||||
|
||||
qdev_init_gpio_out(DEVICE(obj), s->parent_irq, PCH_PIC_IRQ_NUM);
|
||||
qdev_init_gpio_in(DEVICE(obj), pch_pic_irq_handler, PCH_PIC_IRQ_NUM);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_loongarch_pch_pic = {
|
||||
.name = TYPE_LOONGARCH_PCH_PIC,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(int_mask, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(intedge, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(intclr, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC),
|
||||
VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64),
|
||||
VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64),
|
||||
VMSTATE_UINT64(last_intirr, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(intirr, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(intisr, LoongArchPCHPIC),
|
||||
VMSTATE_UINT64(int_polarity, LoongArchPCHPIC),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = loongarch_pch_pic_reset;
|
||||
dc->vmsd = &vmstate_loongarch_pch_pic;
|
||||
}
|
||||
|
||||
static const TypeInfo loongarch_pch_pic_info = {
|
||||
.name = TYPE_LOONGARCH_PCH_PIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(LoongArchPCHPIC),
|
||||
.instance_init = loongarch_pch_pic_init,
|
||||
.class_init = loongarch_pch_pic_class_init,
|
||||
};
|
||||
|
||||
static void loongarch_pch_pic_register_types(void)
|
||||
{
|
||||
type_register_static(&loongarch_pch_pic_info);
|
||||
}
|
||||
|
||||
type_init(loongarch_pch_pic_register_types)
|
|
@ -63,3 +63,7 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
|
|||
specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
|
||||
specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
|
||||
specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c'))
|
||||
specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c'))
|
||||
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
|
||||
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
|
||||
specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c'))
|
||||
|
|
|
@ -287,3 +287,25 @@ 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"
|
||||
|
||||
# loongarch_ipi.c
|
||||
loongarch_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
|
||||
loongarch_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
|
||||
|
||||
# loongarch_pch_pic.c
|
||||
loongarch_pch_pic_irq_handler(int irq, int level) "irq %d level %d"
|
||||
loongarch_pch_pic_low_readw(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64
|
||||
loongarch_pch_pic_low_writew(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64
|
||||
loongarch_pch_pic_high_readw(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64
|
||||
loongarch_pch_pic_high_writew(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64
|
||||
loongarch_pch_pic_readb(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64
|
||||
loongarch_pch_pic_writeb(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64
|
||||
|
||||
# loongarch_pch_msi.c
|
||||
loongarch_msi_set_irq(int irq_num) "set msi irq %d"
|
||||
|
||||
# loongarch_extioi.c
|
||||
loongarch_extioi_setirq(int irq, int level) "set extirq irq %d level %d"
|
||||
loongarch_extioi_readw(uint64_t addr, uint32_t val) "addr: 0x%"PRIx64 "val: 0x%x"
|
||||
loongarch_extioi_writew(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64 "val: 0x%" PRIx64
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
config LOONGARCH_VIRT
|
||||
bool
|
||||
select PCI
|
||||
select PCI_EXPRESS_GENERIC_BRIDGE
|
||||
imply VGA_PCI
|
||||
imply VIRTIO_VGA
|
||||
imply PCI_DEVICES
|
||||
select ISA_BUS
|
||||
select SERIAL
|
||||
select SERIAL_ISA
|
||||
select VIRTIO_PCI
|
||||
select LOONGARCH_IPI
|
||||
select LOONGARCH_PCH_PIC
|
||||
select LOONGARCH_PCH_MSI
|
||||
select LOONGARCH_EXTIOI
|
||||
select LS7A_RTC
|
|
@ -0,0 +1,382 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU loongson 3a5000 develop board emulation
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/datadir.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "sysemu/rtc.h"
|
||||
#include "hw/loongarch/virt.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/irq.h"
|
||||
#include "net/net.h"
|
||||
#include "hw/loader.h"
|
||||
#include "elf.h"
|
||||
#include "hw/intc/loongarch_ipi.h"
|
||||
#include "hw/intc/loongarch_extioi.h"
|
||||
#include "hw/intc/loongarch_pch_pic.h"
|
||||
#include "hw/intc/loongarch_pch_msi.h"
|
||||
#include "hw/pci-host/ls7a.h"
|
||||
#include "hw/pci-host/gpex.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
|
||||
#include "target/loongarch/cpu.h"
|
||||
|
||||
#define PM_BASE 0x10080000
|
||||
#define PM_SIZE 0x100
|
||||
#define PM_CTRL 0x10
|
||||
|
||||
/*
|
||||
* This is a placeholder for missing ACPI,
|
||||
* and will eventually be replaced.
|
||||
*/
|
||||
static uint64_t loongarch_virt_pm_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void loongarch_virt_pm_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
if (addr != PM_CTRL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case 0x00:
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
return;
|
||||
case 0xff:
|
||||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps loongarch_virt_pm_ops = {
|
||||
.read = loongarch_virt_pm_read,
|
||||
.write = loongarch_virt_pm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1
|
||||
}
|
||||
};
|
||||
|
||||
static struct _loaderparams {
|
||||
uint64_t ram_size;
|
||||
const char *kernel_filename;
|
||||
} loaderparams;
|
||||
|
||||
static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
|
||||
{
|
||||
return addr & 0x1fffffffll;
|
||||
}
|
||||
|
||||
static int64_t load_kernel_info(void)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_low, kernel_high;
|
||||
ssize_t kernel_size;
|
||||
|
||||
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
|
||||
cpu_loongarch_virt_to_phys, NULL,
|
||||
&kernel_entry, &kernel_low,
|
||||
&kernel_high, NULL, 0,
|
||||
EM_LOONGARCH, 1, 0);
|
||||
|
||||
if (kernel_size < 0) {
|
||||
error_report("could not load kernel '%s': %s",
|
||||
loaderparams.kernel_filename,
|
||||
load_elf_strerror(kernel_size));
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static void loongarch_devices_init(DeviceState *pch_pic)
|
||||
{
|
||||
DeviceState *gpex_dev;
|
||||
SysBusDevice *d;
|
||||
PCIBus *pci_bus;
|
||||
MemoryRegion *ecam_alias, *ecam_reg, *pio_alias, *pio_reg;
|
||||
MemoryRegion *mmio_alias, *mmio_reg, *pm_mem;
|
||||
int i;
|
||||
|
||||
gpex_dev = qdev_new(TYPE_GPEX_HOST);
|
||||
d = SYS_BUS_DEVICE(gpex_dev);
|
||||
sysbus_realize_and_unref(d, &error_fatal);
|
||||
pci_bus = PCI_HOST_BRIDGE(gpex_dev)->bus;
|
||||
|
||||
/* Map only part size_ecam bytes of ECAM space */
|
||||
ecam_alias = g_new0(MemoryRegion, 1);
|
||||
ecam_reg = sysbus_mmio_get_region(d, 0);
|
||||
memory_region_init_alias(ecam_alias, OBJECT(gpex_dev), "pcie-ecam",
|
||||
ecam_reg, 0, LS_PCIECFG_SIZE);
|
||||
memory_region_add_subregion(get_system_memory(), LS_PCIECFG_BASE,
|
||||
ecam_alias);
|
||||
|
||||
/* Map PCI mem space */
|
||||
mmio_alias = g_new0(MemoryRegion, 1);
|
||||
mmio_reg = sysbus_mmio_get_region(d, 1);
|
||||
memory_region_init_alias(mmio_alias, OBJECT(gpex_dev), "pcie-mmio",
|
||||
mmio_reg, LS7A_PCI_MEM_BASE, LS7A_PCI_MEM_SIZE);
|
||||
memory_region_add_subregion(get_system_memory(), LS7A_PCI_MEM_BASE,
|
||||
mmio_alias);
|
||||
|
||||
/* Map PCI IO port space. */
|
||||
pio_alias = g_new0(MemoryRegion, 1);
|
||||
pio_reg = sysbus_mmio_get_region(d, 2);
|
||||
memory_region_init_alias(pio_alias, OBJECT(gpex_dev), "pcie-io", pio_reg,
|
||||
LS7A_PCI_IO_OFFSET, LS7A_PCI_IO_SIZE);
|
||||
memory_region_add_subregion(get_system_memory(), LS7A_PCI_IO_BASE,
|
||||
pio_alias);
|
||||
|
||||
for (i = 0; i < GPEX_NUM_IRQS; i++) {
|
||||
sysbus_connect_irq(d, i,
|
||||
qdev_get_gpio_in(pch_pic, 16 + i));
|
||||
gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i);
|
||||
}
|
||||
|
||||
serial_mm_init(get_system_memory(), LS7A_UART_BASE, 0,
|
||||
qdev_get_gpio_in(pch_pic,
|
||||
LS7A_UART_IRQ - PCH_PIC_IRQ_OFFSET),
|
||||
115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
|
||||
|
||||
/* Network init */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
NICInfo *nd = &nd_table[i];
|
||||
|
||||
if (!nd->model) {
|
||||
nd->model = g_strdup("virtio");
|
||||
}
|
||||
|
||||
pci_nic_init_nofail(nd, pci_bus, nd->model, NULL);
|
||||
}
|
||||
|
||||
/* VGA setup */
|
||||
pci_vga_init(pci_bus);
|
||||
|
||||
/*
|
||||
* There are some invalid guest memory access.
|
||||
* Create some unimplemented devices to emulate this.
|
||||
*/
|
||||
create_unimplemented_device("pci-dma-cfg", 0x1001041c, 0x4);
|
||||
sysbus_create_simple("ls7a_rtc", LS7A_RTC_REG_BASE,
|
||||
qdev_get_gpio_in(pch_pic,
|
||||
LS7A_RTC_IRQ - PCH_PIC_IRQ_OFFSET));
|
||||
|
||||
pm_mem = g_new(MemoryRegion, 1);
|
||||
memory_region_init_io(pm_mem, NULL, &loongarch_virt_pm_ops,
|
||||
NULL, "loongarch_virt_pm", PM_SIZE);
|
||||
memory_region_add_subregion(get_system_memory(), PM_BASE, pm_mem);
|
||||
}
|
||||
|
||||
static void loongarch_irq_init(LoongArchMachineState *lams)
|
||||
{
|
||||
MachineState *ms = MACHINE(lams);
|
||||
DeviceState *pch_pic, *pch_msi, *cpudev;
|
||||
DeviceState *ipi, *extioi;
|
||||
SysBusDevice *d;
|
||||
LoongArchCPU *lacpu;
|
||||
CPULoongArchState *env;
|
||||
CPUState *cpu_state;
|
||||
int cpu, pin, i;
|
||||
|
||||
ipi = qdev_new(TYPE_LOONGARCH_IPI);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal);
|
||||
|
||||
extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal);
|
||||
|
||||
/*
|
||||
* The connection of interrupts:
|
||||
* +-----+ +---------+ +-------+
|
||||
* | IPI |--> | CPUINTC | <-- | Timer |
|
||||
* +-----+ +---------+ +-------+
|
||||
* ^
|
||||
* |
|
||||
* +---------+
|
||||
* | EIOINTC |
|
||||
* +---------+
|
||||
* ^ ^
|
||||
* | |
|
||||
* +---------+ +---------+
|
||||
* | PCH-PIC | | PCH-MSI |
|
||||
* +---------+ +---------+
|
||||
* ^ ^ ^
|
||||
* | | |
|
||||
* +--------+ +---------+ +---------+
|
||||
* | UARTs | | Devices | | Devices |
|
||||
* +--------+ +---------+ +---------+
|
||||
*/
|
||||
for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
|
||||
cpu_state = qemu_get_cpu(cpu);
|
||||
cpudev = DEVICE(cpu_state);
|
||||
lacpu = LOONGARCH_CPU(cpu_state);
|
||||
env = &(lacpu->env);
|
||||
|
||||
/* connect ipi irq to cpu irq */
|
||||
qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI));
|
||||
/* IPI iocsr memory region */
|
||||
memory_region_add_subregion(&env->system_iocsr, SMP_IPI_MAILBOX,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi),
|
||||
cpu));
|
||||
/* extioi iocsr memory region */
|
||||
memory_region_add_subregion(&env->system_iocsr, APIC_BASE,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi),
|
||||
cpu));
|
||||
}
|
||||
|
||||
/*
|
||||
* connect ext irq to the cpu irq
|
||||
* cpu_pin[9:2] <= intc_pin[7:0]
|
||||
*/
|
||||
for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
|
||||
cpudev = DEVICE(qemu_get_cpu(cpu));
|
||||
for (pin = 0; pin < LS3A_INTC_IP; pin++) {
|
||||
qdev_connect_gpio_out(extioi, (cpu * 8 + pin),
|
||||
qdev_get_gpio_in(cpudev, pin + 2));
|
||||
}
|
||||
}
|
||||
|
||||
pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC);
|
||||
d = SYS_BUS_DEVICE(pch_pic);
|
||||
sysbus_realize_and_unref(d, &error_fatal);
|
||||
memory_region_add_subregion(get_system_memory(), LS7A_IOAPIC_REG_BASE,
|
||||
sysbus_mmio_get_region(d, 0));
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
LS7A_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET,
|
||||
sysbus_mmio_get_region(d, 1));
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
LS7A_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO,
|
||||
sysbus_mmio_get_region(d, 2));
|
||||
|
||||
/* Connect 64 pch_pic irqs to extioi */
|
||||
for (int i = 0; i < PCH_PIC_IRQ_NUM; i++) {
|
||||
qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i));
|
||||
}
|
||||
|
||||
pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI);
|
||||
d = SYS_BUS_DEVICE(pch_msi);
|
||||
sysbus_realize_and_unref(d, &error_fatal);
|
||||
sysbus_mmio_map(d, 0, LS7A_PCH_MSI_ADDR_LOW);
|
||||
for (i = 0; i < PCH_MSI_IRQ_NUM; i++) {
|
||||
/* Connect 192 pch_msi irqs to extioi */
|
||||
qdev_connect_gpio_out(DEVICE(d), i,
|
||||
qdev_get_gpio_in(extioi, i + PCH_MSI_IRQ_START));
|
||||
}
|
||||
|
||||
loongarch_devices_init(pch_pic);
|
||||
}
|
||||
|
||||
static void reset_load_elf(void *opaque)
|
||||
{
|
||||
LoongArchCPU *cpu = opaque;
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
if (env->load_elf) {
|
||||
cpu_set_pc(CPU(cpu), env->elf_address);
|
||||
}
|
||||
}
|
||||
|
||||
static void loongarch_init(MachineState *machine)
|
||||
{
|
||||
const char *cpu_model = machine->cpu_type;
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
ram_addr_t offset = 0;
|
||||
ram_addr_t ram_size = machine->ram_size;
|
||||
uint64_t highram_size = 0;
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
|
||||
LoongArchCPU *lacpu;
|
||||
int i;
|
||||
int64_t kernel_addr = 0;
|
||||
|
||||
if (!cpu_model) {
|
||||
cpu_model = LOONGARCH_CPU_TYPE_NAME("la464");
|
||||
}
|
||||
|
||||
if (!strstr(cpu_model, "la464")) {
|
||||
error_report("LoongArch/TCG needs cpu type la464");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ram_size < 1 * GiB) {
|
||||
error_report("ram_size must be greater than 1G.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Init CPUs */
|
||||
for (i = 0; i < machine->smp.cpus; i++) {
|
||||
cpu_create(machine->cpu_type);
|
||||
}
|
||||
|
||||
/* Add memory region */
|
||||
memory_region_init_alias(&lams->lowmem, NULL, "loongarch.lowram",
|
||||
machine->ram, 0, 256 * MiB);
|
||||
memory_region_add_subregion(address_space_mem, offset, &lams->lowmem);
|
||||
offset += 256 * MiB;
|
||||
highram_size = ram_size - 256 * MiB;
|
||||
memory_region_init_alias(&lams->highmem, NULL, "loongarch.highmem",
|
||||
machine->ram, offset, highram_size);
|
||||
memory_region_add_subregion(address_space_mem, 0x90000000, &lams->highmem);
|
||||
/* Add isa io region */
|
||||
memory_region_init_alias(&lams->isa_io, NULL, "isa-io",
|
||||
get_system_io(), 0, LOONGARCH_ISA_IO_SIZE);
|
||||
memory_region_add_subregion(address_space_mem, LOONGARCH_ISA_IO_BASE,
|
||||
&lams->isa_io);
|
||||
if (kernel_filename) {
|
||||
loaderparams.ram_size = ram_size;
|
||||
loaderparams.kernel_filename = kernel_filename;
|
||||
kernel_addr = load_kernel_info();
|
||||
if (!machine->firmware) {
|
||||
for (i = 0; i < machine->smp.cpus; i++) {
|
||||
lacpu = LOONGARCH_CPU(qemu_get_cpu(i));
|
||||
lacpu->env.load_elf = true;
|
||||
lacpu->env.elf_address = kernel_addr;
|
||||
qemu_register_reset(reset_load_elf, lacpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Initialize the IO interrupt subsystem */
|
||||
loongarch_irq_init(lams);
|
||||
}
|
||||
|
||||
static void loongarch_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->desc = "Loongson-3A5000 LS7A1000 machine";
|
||||
mc->init = loongarch_init;
|
||||
mc->default_ram_size = 1 * GiB;
|
||||
mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464");
|
||||
mc->default_ram_id = "loongarch.ram";
|
||||
mc->max_cpus = LOONGARCH_MAX_VCPUS;
|
||||
mc->is_default = 1;
|
||||
mc->default_kernel_irqchip_split = false;
|
||||
mc->block_default_type = IF_VIRTIO;
|
||||
mc->default_boot_order = "c";
|
||||
mc->no_cdrom = 1;
|
||||
}
|
||||
|
||||
static const TypeInfo loongarch_machine_types[] = {
|
||||
{
|
||||
.name = TYPE_LOONGARCH_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(LoongArchMachineState),
|
||||
.class_init = loongarch_class_init,
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPES(loongarch_machine_types)
|
|
@ -0,0 +1,4 @@
|
|||
loongarch_ss = ss.source_set()
|
||||
loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('loongson3.c'))
|
||||
|
||||
hw_arch += {'loongarch': loongarch_ss}
|
|
@ -50,6 +50,7 @@ subdir('avr')
|
|||
subdir('cris')
|
||||
subdir('hppa')
|
||||
subdir('i386')
|
||||
subdir('loongarch')
|
||||
subdir('m68k')
|
||||
subdir('microblaze')
|
||||
subdir('mips')
|
||||
|
|
|
@ -27,3 +27,6 @@ config SUN4V_RTC
|
|||
|
||||
config GOLDFISH_RTC
|
||||
bool
|
||||
|
||||
config LS7A_RTC
|
||||
bool
|
||||
|
|
|
@ -0,0 +1,528 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Loongarch LS7A Real Time Clock emulation
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "include/hw/register.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/log.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "sysemu/rtc.h"
|
||||
#include "hw/registerfields.h"
|
||||
|
||||
#define SYS_TOYTRIM 0x20
|
||||
#define SYS_TOYWRITE0 0x24
|
||||
#define SYS_TOYWRITE1 0x28
|
||||
#define SYS_TOYREAD0 0x2C
|
||||
#define SYS_TOYREAD1 0x30
|
||||
#define SYS_TOYMATCH0 0x34
|
||||
#define SYS_TOYMATCH1 0x38
|
||||
#define SYS_TOYMATCH2 0x3C
|
||||
#define SYS_RTCCTRL 0x40
|
||||
#define SYS_RTCTRIM 0x60
|
||||
#define SYS_RTCWRTIE0 0x64
|
||||
#define SYS_RTCREAD0 0x68
|
||||
#define SYS_RTCMATCH0 0x6C
|
||||
#define SYS_RTCMATCH1 0x70
|
||||
#define SYS_RTCMATCH2 0x74
|
||||
|
||||
#define LS7A_RTC_FREQ 32768
|
||||
#define TIMER_NUMS 3
|
||||
/*
|
||||
* Shift bits and filed mask
|
||||
*/
|
||||
|
||||
FIELD(TOY, MON, 26, 6)
|
||||
FIELD(TOY, DAY, 21, 5)
|
||||
FIELD(TOY, HOUR, 16, 5)
|
||||
FIELD(TOY, MIN, 10, 6)
|
||||
FIELD(TOY, SEC, 4, 6)
|
||||
FIELD(TOY, MSEC, 0, 4)
|
||||
|
||||
FIELD(TOY_MATCH, YEAR, 26, 6)
|
||||
FIELD(TOY_MATCH, MON, 22, 4)
|
||||
FIELD(TOY_MATCH, DAY, 17, 5)
|
||||
FIELD(TOY_MATCH, HOUR, 12, 5)
|
||||
FIELD(TOY_MATCH, MIN, 6, 6)
|
||||
FIELD(TOY_MATCH, SEC, 0, 6)
|
||||
|
||||
FIELD(RTC_CTRL, RTCEN, 13, 1)
|
||||
FIELD(RTC_CTRL, TOYEN, 11, 1)
|
||||
FIELD(RTC_CTRL, EO, 8, 1)
|
||||
|
||||
#define TYPE_LS7A_RTC "ls7a_rtc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LS7ARtcState, LS7A_RTC)
|
||||
|
||||
struct LS7ARtcState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
/*
|
||||
* Needed to preserve the tick_count across migration, even if the
|
||||
* absolute value of the rtc_clock is different on the source and
|
||||
* destination.
|
||||
*/
|
||||
int64_t offset_toy;
|
||||
int64_t offset_rtc;
|
||||
uint64_t save_toy_mon;
|
||||
uint64_t save_toy_year;
|
||||
uint64_t save_rtc;
|
||||
int64_t data;
|
||||
int tidx;
|
||||
uint32_t toymatch[3];
|
||||
uint32_t toytrim;
|
||||
uint32_t cntrctl;
|
||||
uint32_t rtctrim;
|
||||
uint32_t rtccount;
|
||||
uint32_t rtcmatch[3];
|
||||
QEMUTimer *toy_timer[TIMER_NUMS];
|
||||
QEMUTimer *rtc_timer[TIMER_NUMS];
|
||||
qemu_irq irq;
|
||||
};
|
||||
|
||||
/* switch nanoseconds time to rtc ticks */
|
||||
static inline uint64_t ls7a_rtc_ticks(void)
|
||||
{
|
||||
return qemu_clock_get_ns(rtc_clock) * LS7A_RTC_FREQ / NANOSECONDS_PER_SECOND;
|
||||
}
|
||||
|
||||
/* switch rtc ticks to nanoseconds */
|
||||
static inline uint64_t ticks_to_ns(uint64_t ticks)
|
||||
{
|
||||
return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ;
|
||||
}
|
||||
|
||||
static inline bool toy_enabled(LS7ARtcState *s)
|
||||
{
|
||||
return FIELD_EX32(s->cntrctl, RTC_CTRL, TOYEN) &&
|
||||
FIELD_EX32(s->cntrctl, RTC_CTRL, EO);
|
||||
}
|
||||
|
||||
static inline bool rtc_enabled(LS7ARtcState *s)
|
||||
{
|
||||
return FIELD_EX32(s->cntrctl, RTC_CTRL, RTCEN) &&
|
||||
FIELD_EX32(s->cntrctl, RTC_CTRL, EO);
|
||||
}
|
||||
|
||||
/* parse toy value to struct tm */
|
||||
static inline void toy_val_to_time_mon(uint64_t toy_val, struct tm *tm)
|
||||
{
|
||||
tm->tm_sec = FIELD_EX32(toy_val, TOY, SEC);
|
||||
tm->tm_min = FIELD_EX32(toy_val, TOY, MIN);
|
||||
tm->tm_hour = FIELD_EX32(toy_val, TOY, HOUR);
|
||||
tm->tm_mday = FIELD_EX32(toy_val, TOY, DAY);
|
||||
tm->tm_mon = FIELD_EX32(toy_val, TOY, MON) - 1;
|
||||
}
|
||||
|
||||
static inline void toy_val_to_time_year(uint64_t toy_year, struct tm *tm)
|
||||
{
|
||||
tm->tm_year = toy_year;
|
||||
}
|
||||
|
||||
/* parse struct tm to toy value */
|
||||
static inline uint64_t toy_time_to_val_mon(struct tm tm)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
|
||||
val = FIELD_DP32(val, TOY, MON, tm.tm_mon + 1);
|
||||
val = FIELD_DP32(val, TOY, DAY, tm.tm_mday);
|
||||
val = FIELD_DP32(val, TOY, HOUR, tm.tm_hour);
|
||||
val = FIELD_DP32(val, TOY, MIN, tm.tm_min);
|
||||
val = FIELD_DP32(val, TOY, SEC, tm.tm_sec);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint64_t toy_time_to_val_year(struct tm tm)
|
||||
{
|
||||
uint64_t year;
|
||||
|
||||
year = tm.tm_year;
|
||||
return year;
|
||||
}
|
||||
|
||||
static inline void toymatch_val_to_time(uint64_t val, struct tm *tm)
|
||||
{
|
||||
tm->tm_sec = FIELD_EX32(val, TOY_MATCH, SEC);
|
||||
tm->tm_min = FIELD_EX32(val, TOY_MATCH, MIN);
|
||||
tm->tm_hour = FIELD_EX32(val, TOY_MATCH, HOUR);
|
||||
tm->tm_mday = FIELD_EX32(val, TOY_MATCH, DAY);
|
||||
tm->tm_mon = FIELD_EX32(val, TOY_MATCH, MON) - 1;
|
||||
tm->tm_year += (FIELD_EX32(val, TOY_MATCH, YEAR) - (tm->tm_year & 0x3f));
|
||||
}
|
||||
|
||||
static void toymatch_write(LS7ARtcState *s, struct tm *tm, uint64_t val, int num)
|
||||
{
|
||||
int64_t now, expire_time;
|
||||
|
||||
/* it do not support write when toy disabled */
|
||||
if (toy_enabled(s)) {
|
||||
s->toymatch[num] = val;
|
||||
/* caculate expire time */
|
||||
now = qemu_clock_get_ms(rtc_clock);
|
||||
toymatch_val_to_time(val, tm);
|
||||
expire_time = now + (qemu_timedate_diff(tm) - s->offset_toy) * 1000;
|
||||
timer_mod(s->toy_timer[num], expire_time);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)
|
||||
{
|
||||
uint64_t expire_ns;
|
||||
|
||||
/* it do not support write when toy disabled */
|
||||
if (rtc_enabled(s)) {
|
||||
s->rtcmatch[num] = val;
|
||||
/* caculate expire time */
|
||||
expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
|
||||
timer_mod_ns(s->rtc_timer[num], expire_ns);
|
||||
}
|
||||
}
|
||||
|
||||
static void ls7a_toy_stop(LS7ARtcState *s)
|
||||
{
|
||||
int i;
|
||||
struct tm tm;
|
||||
/*
|
||||
* save time when disabled toy,
|
||||
* because toy time not add counters.
|
||||
*/
|
||||
qemu_get_timedate(&tm, s->offset_toy);
|
||||
s->save_toy_mon = toy_time_to_val_mon(tm);
|
||||
s->save_toy_year = toy_time_to_val_year(tm);
|
||||
|
||||
/* delete timers, and when re-enabled, recaculate expire time */
|
||||
for (i = 0; i < TIMER_NUMS; i++) {
|
||||
timer_del(s->toy_timer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void ls7a_rtc_stop(LS7ARtcState *s)
|
||||
{
|
||||
int i;
|
||||
uint64_t time;
|
||||
|
||||
/* save rtc time */
|
||||
time = ls7a_rtc_ticks() + s->offset_rtc;
|
||||
s->save_rtc = time;
|
||||
|
||||
/* delete timers, and when re-enabled, recaculate expire time */
|
||||
for (i = 0; i < TIMER_NUMS; i++) {
|
||||
timer_del(s->rtc_timer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void ls7a_toy_start(LS7ARtcState *s)
|
||||
{
|
||||
int i;
|
||||
uint64_t expire_time, now;
|
||||
struct tm tm;
|
||||
/*
|
||||
* need to recaculate toy offset
|
||||
* and expire time when enable it.
|
||||
*/
|
||||
toy_val_to_time_mon(s->save_toy_mon, &tm);
|
||||
toy_val_to_time_year(s->save_toy_year, &tm);
|
||||
|
||||
s->offset_toy = qemu_timedate_diff(&tm);
|
||||
now = qemu_clock_get_ms(rtc_clock);
|
||||
|
||||
/* recaculate expire time and enable timer */
|
||||
for (i = 0; i < TIMER_NUMS; i++) {
|
||||
toymatch_val_to_time(s->toymatch[i], &tm);
|
||||
expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
|
||||
timer_mod(s->toy_timer[i], expire_time);
|
||||
}
|
||||
}
|
||||
|
||||
static void ls7a_rtc_start(LS7ARtcState *s)
|
||||
{
|
||||
int i;
|
||||
uint64_t expire_time, now;
|
||||
|
||||
/*
|
||||
* need to recaculate rtc offset
|
||||
* and expire time when enable it.
|
||||
*/
|
||||
now = ls7a_rtc_ticks();
|
||||
s->offset_rtc = s->save_rtc - now;
|
||||
|
||||
/* recaculate expire time and enable timer */
|
||||
for (i = 0; i < TIMER_NUMS; i++) {
|
||||
expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc);
|
||||
timer_mod_ns(s->rtc_timer[i], expire_time);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
LS7ARtcState *s = LS7A_RTC(opaque);
|
||||
struct tm tm;
|
||||
int val = 0;
|
||||
|
||||
switch (addr) {
|
||||
case SYS_TOYREAD0:
|
||||
/* if toy disabled, read save toy time */
|
||||
if (toy_enabled(s)) {
|
||||
qemu_get_timedate(&tm, s->offset_toy);
|
||||
val = toy_time_to_val_mon(tm);
|
||||
} else {
|
||||
/* read save mon val */
|
||||
val = s->save_toy_mon;
|
||||
}
|
||||
break;
|
||||
case SYS_TOYREAD1:
|
||||
/* if toy disabled, read save toy time */
|
||||
if (toy_enabled(s)) {
|
||||
qemu_get_timedate(&tm, s->offset_toy);
|
||||
val = tm.tm_year;
|
||||
} else {
|
||||
/* read save year val */
|
||||
val = s->save_toy_year;
|
||||
}
|
||||
break;
|
||||
case SYS_TOYMATCH0:
|
||||
val = s->toymatch[0];
|
||||
break;
|
||||
case SYS_TOYMATCH1:
|
||||
val = s->toymatch[1];
|
||||
break;
|
||||
case SYS_TOYMATCH2:
|
||||
val = s->toymatch[2];
|
||||
break;
|
||||
case SYS_RTCCTRL:
|
||||
val = s->cntrctl;
|
||||
break;
|
||||
case SYS_RTCREAD0:
|
||||
/* if rtc disabled, read save rtc time */
|
||||
if (rtc_enabled(s)) {
|
||||
val = ls7a_rtc_ticks() + s->offset_rtc;
|
||||
} else {
|
||||
val = s->save_rtc;
|
||||
}
|
||||
break;
|
||||
case SYS_RTCMATCH0:
|
||||
val = s->rtcmatch[0];
|
||||
break;
|
||||
case SYS_RTCMATCH1:
|
||||
val = s->rtcmatch[1];
|
||||
break;
|
||||
case SYS_RTCMATCH2:
|
||||
val = s->rtcmatch[2];
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void ls7a_rtc_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
int old_toyen, old_rtcen, new_toyen, new_rtcen;
|
||||
LS7ARtcState *s = LS7A_RTC(opaque);
|
||||
struct tm tm;
|
||||
|
||||
switch (addr) {
|
||||
case SYS_TOYWRITE0:
|
||||
/* it do not support write when toy disabled */
|
||||
if (toy_enabled(s)) {
|
||||
qemu_get_timedate(&tm, s->offset_toy);
|
||||
tm.tm_sec = FIELD_EX32(val, TOY, SEC);
|
||||
tm.tm_min = FIELD_EX32(val, TOY, MIN);
|
||||
tm.tm_hour = FIELD_EX32(val, TOY, HOUR);
|
||||
tm.tm_mday = FIELD_EX32(val, TOY, DAY);
|
||||
tm.tm_mon = FIELD_EX32(val, TOY, MON) - 1;
|
||||
s->offset_toy = qemu_timedate_diff(&tm);
|
||||
}
|
||||
break;
|
||||
case SYS_TOYWRITE1:
|
||||
if (toy_enabled(s)) {
|
||||
qemu_get_timedate(&tm, s->offset_toy);
|
||||
tm.tm_year = val;
|
||||
s->offset_toy = qemu_timedate_diff(&tm);
|
||||
}
|
||||
break;
|
||||
case SYS_TOYMATCH0:
|
||||
toymatch_write(s, &tm, val, 0);
|
||||
break;
|
||||
case SYS_TOYMATCH1:
|
||||
toymatch_write(s, &tm, val, 1);
|
||||
break;
|
||||
case SYS_TOYMATCH2:
|
||||
toymatch_write(s, &tm, val, 2);
|
||||
break;
|
||||
case SYS_RTCCTRL:
|
||||
/* get old ctrl */
|
||||
old_toyen = toy_enabled(s);
|
||||
old_rtcen = rtc_enabled(s);
|
||||
|
||||
s->cntrctl = val;
|
||||
/* get new ctrl */
|
||||
new_toyen = toy_enabled(s);
|
||||
new_rtcen = rtc_enabled(s);
|
||||
|
||||
/*
|
||||
* we do not consider if EO changed, as it always set at most time.
|
||||
* toy or rtc enabled should start timer. otherwise, stop timer
|
||||
*/
|
||||
if (old_toyen != new_toyen) {
|
||||
if (new_toyen) {
|
||||
ls7a_toy_start(s);
|
||||
} else {
|
||||
ls7a_toy_stop(s);
|
||||
}
|
||||
}
|
||||
if (old_rtcen != new_rtcen) {
|
||||
if (new_rtcen) {
|
||||
ls7a_rtc_start(s);
|
||||
} else {
|
||||
ls7a_rtc_stop(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SYS_RTCWRTIE0:
|
||||
if (rtc_enabled(s)) {
|
||||
s->offset_rtc = val - ls7a_rtc_ticks();
|
||||
}
|
||||
break;
|
||||
case SYS_RTCMATCH0:
|
||||
rtcmatch_write(s, val, 0);
|
||||
break;
|
||||
case SYS_RTCMATCH1:
|
||||
rtcmatch_write(s, val, 1);
|
||||
break;
|
||||
case SYS_RTCMATCH2:
|
||||
rtcmatch_write(s, val, 2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ls7a_rtc_ops = {
|
||||
.read = ls7a_rtc_read,
|
||||
.write = ls7a_rtc_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void toy_timer_cb(void *opaque)
|
||||
{
|
||||
LS7ARtcState *s = opaque;
|
||||
|
||||
if (toy_enabled(s)) {
|
||||
qemu_irq_pulse(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_timer_cb(void *opaque)
|
||||
{
|
||||
LS7ARtcState *s = opaque;
|
||||
|
||||
if (rtc_enabled(s)) {
|
||||
qemu_irq_pulse(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void ls7a_rtc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
int i;
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
LS7ARtcState *d = LS7A_RTC(sbd);
|
||||
memory_region_init_io(&d->iomem, NULL, &ls7a_rtc_ops,
|
||||
(void *)d, "ls7a_rtc", 0x100);
|
||||
|
||||
sysbus_init_irq(sbd, &d->irq);
|
||||
|
||||
sysbus_init_mmio(sbd, &d->iomem);
|
||||
for (i = 0; i < TIMER_NUMS; i++) {
|
||||
d->toymatch[i] = 0;
|
||||
d->rtcmatch[i] = 0;
|
||||
d->toy_timer[i] = timer_new_ms(rtc_clock, toy_timer_cb, d);
|
||||
d->rtc_timer[i] = timer_new_ms(rtc_clock, rtc_timer_cb, d);
|
||||
}
|
||||
d->offset_toy = 0;
|
||||
d->offset_rtc = 0;
|
||||
d->save_toy_mon = 0;
|
||||
d->save_toy_year = 0;
|
||||
d->save_rtc = 0;
|
||||
|
||||
create_unimplemented_device("mmio fallback 1", 0x10013ffc, 0x4);
|
||||
}
|
||||
|
||||
static int ls7a_rtc_pre_save(void *opaque)
|
||||
{
|
||||
LS7ARtcState *s = LS7A_RTC(opaque);
|
||||
|
||||
ls7a_toy_stop(s);
|
||||
ls7a_rtc_stop(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls7a_rtc_post_load(void *opaque, int version_id)
|
||||
{
|
||||
LS7ARtcState *s = LS7A_RTC(opaque);
|
||||
if (toy_enabled(s)) {
|
||||
ls7a_toy_start(s);
|
||||
}
|
||||
|
||||
if (rtc_enabled(s)) {
|
||||
ls7a_rtc_start(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ls7a_rtc = {
|
||||
.name = "ls7a_rtc",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.pre_save = ls7a_rtc_pre_save,
|
||||
.post_load = ls7a_rtc_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(offset_toy, LS7ARtcState),
|
||||
VMSTATE_INT64(offset_rtc, LS7ARtcState),
|
||||
VMSTATE_UINT64(save_toy_mon, LS7ARtcState),
|
||||
VMSTATE_UINT64(save_toy_year, LS7ARtcState),
|
||||
VMSTATE_UINT64(save_rtc, LS7ARtcState),
|
||||
VMSTATE_UINT32_ARRAY(toymatch, LS7ARtcState, TIMER_NUMS),
|
||||
VMSTATE_UINT32_ARRAY(rtcmatch, LS7ARtcState, TIMER_NUMS),
|
||||
VMSTATE_UINT32(cntrctl, LS7ARtcState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void ls7a_rtc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->vmsd = &vmstate_ls7a_rtc;
|
||||
dc->realize = ls7a_rtc_realize;
|
||||
dc->desc = "ls7a rtc";
|
||||
}
|
||||
|
||||
static const TypeInfo ls7a_rtc_info = {
|
||||
.name = TYPE_LS7A_RTC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(LS7ARtcState),
|
||||
.class_init = ls7a_rtc_class_init,
|
||||
};
|
||||
|
||||
static void ls7a_rtc_register_types(void)
|
||||
{
|
||||
type_register_static(&ls7a_rtc_info);
|
||||
}
|
||||
|
||||
type_init(ls7a_rtc_register_types)
|
|
@ -11,6 +11,7 @@ softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_rtc.c'))
|
|||
softmmu_ss.add(when: 'CONFIG_SUN4V_RTC', if_true: files('sun4v-rtc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_rtc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c'))
|
||||
|
||||
specific_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c'))
|
||||
|
|
|
@ -253,6 +253,7 @@ enum bfd_architecture
|
|||
#define bfd_mach_rx 0x75
|
||||
#define bfd_mach_rx_v2 0x76
|
||||
#define bfd_mach_rx_v3 0x77
|
||||
bfd_arch_loongarch,
|
||||
bfd_arch_last
|
||||
};
|
||||
#define bfd_mach_s390_31 31
|
||||
|
@ -458,6 +459,7 @@ int print_insn_riscv64 (bfd_vma, disassemble_info*);
|
|||
int print_insn_riscv128 (bfd_vma, disassemble_info*);
|
||||
int print_insn_rx(bfd_vma, disassemble_info *);
|
||||
int print_insn_hexagon(bfd_vma, disassemble_info *);
|
||||
int print_insn_loongarch(bfd_vma, disassemble_info *);
|
||||
|
||||
#ifdef CONFIG_CAPSTONE
|
||||
bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#pragma GCC poison TARGET_CRIS
|
||||
#pragma GCC poison TARGET_HEXAGON
|
||||
#pragma GCC poison TARGET_HPPA
|
||||
#pragma GCC poison TARGET_LOONGARCH64
|
||||
#pragma GCC poison TARGET_M68K
|
||||
#pragma GCC poison TARGET_MICROBLAZE
|
||||
#pragma GCC poison TARGET_MIPS
|
||||
|
@ -71,6 +72,7 @@
|
|||
#pragma GCC poison CONFIG_HPPA_DIS
|
||||
#pragma GCC poison CONFIG_I386_DIS
|
||||
#pragma GCC poison CONFIG_HEXAGON_DIS
|
||||
#pragma GCC poison CONFIG_LOONGARCH_DIS
|
||||
#pragma GCC poison CONFIG_M68K_DIS
|
||||
#pragma GCC poison CONFIG_MICROBLAZE_DIS
|
||||
#pragma GCC poison CONFIG_MIPS_DIS
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch 3A5000 ext interrupt controller definitions
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/loongarch/virt.h"
|
||||
|
||||
#ifndef LOONGARCH_EXTIOI_H
|
||||
#define LOONGARCH_EXTIOI_H
|
||||
|
||||
#define LS3A_INTC_IP 8
|
||||
#define EXTIOI_IRQS (256)
|
||||
#define EXTIOI_IRQS_BITMAP_SIZE (256 / 8)
|
||||
/* map to ipnum per 32 irqs */
|
||||
#define EXTIOI_IRQS_IPMAP_SIZE (256 / 32)
|
||||
#define EXTIOI_IRQS_COREMAP_SIZE 256
|
||||
#define EXTIOI_IRQS_NODETYPE_COUNT 16
|
||||
#define EXTIOI_IRQS_GROUP_COUNT 8
|
||||
|
||||
#define APIC_OFFSET 0x400
|
||||
#define APIC_BASE (0x1000ULL + APIC_OFFSET)
|
||||
|
||||
#define EXTIOI_NODETYPE_START (0x4a0 - APIC_OFFSET)
|
||||
#define EXTIOI_NODETYPE_END (0x4c0 - APIC_OFFSET)
|
||||
#define EXTIOI_IPMAP_START (0x4c0 - APIC_OFFSET)
|
||||
#define EXTIOI_IPMAP_END (0x4c8 - APIC_OFFSET)
|
||||
#define EXTIOI_ENABLE_START (0x600 - APIC_OFFSET)
|
||||
#define EXTIOI_ENABLE_END (0x620 - APIC_OFFSET)
|
||||
#define EXTIOI_BOUNCE_START (0x680 - APIC_OFFSET)
|
||||
#define EXTIOI_BOUNCE_END (0x6a0 - APIC_OFFSET)
|
||||
#define EXTIOI_ISR_START (0x700 - APIC_OFFSET)
|
||||
#define EXTIOI_ISR_END (0x720 - APIC_OFFSET)
|
||||
#define EXTIOI_COREISR_START (0x800 - APIC_OFFSET)
|
||||
#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET)
|
||||
#define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET)
|
||||
#define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET)
|
||||
|
||||
#define TYPE_LOONGARCH_EXTIOI "loongarch.extioi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI)
|
||||
struct LoongArchExtIOI {
|
||||
SysBusDevice parent_obj;
|
||||
/* hardware state */
|
||||
uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2];
|
||||
uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT];
|
||||
uint32_t isr[EXTIOI_IRQS / 32];
|
||||
uint32_t coreisr[LOONGARCH_MAX_VCPUS][EXTIOI_IRQS_GROUP_COUNT];
|
||||
uint32_t enable[EXTIOI_IRQS / 32];
|
||||
uint32_t ipmap[EXTIOI_IRQS_IPMAP_SIZE / 4];
|
||||
uint32_t coremap[EXTIOI_IRQS / 4];
|
||||
uint32_t sw_pending[EXTIOI_IRQS / 32];
|
||||
DECLARE_BITMAP(sw_isr[LOONGARCH_MAX_VCPUS][LS3A_INTC_IP], EXTIOI_IRQS);
|
||||
uint8_t sw_ipmap[EXTIOI_IRQS_IPMAP_SIZE];
|
||||
uint8_t sw_coremap[EXTIOI_IRQS];
|
||||
qemu_irq parent_irq[LOONGARCH_MAX_VCPUS][LS3A_INTC_IP];
|
||||
qemu_irq irq[EXTIOI_IRQS];
|
||||
MemoryRegion extioi_iocsr_mem[LOONGARCH_MAX_VCPUS];
|
||||
MemoryRegion extioi_system_mem;
|
||||
};
|
||||
#endif /* LOONGARCH_EXTIOI_H */
|
|
@ -0,0 +1,52 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch ipi interrupt header files
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef HW_LOONGARCH_IPI_H
|
||||
#define HW_LOONGARCH_IPI_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
/* Mainy used by iocsr read and write */
|
||||
#define SMP_IPI_MAILBOX 0x1000ULL
|
||||
#define CORE_STATUS_OFF 0x0
|
||||
#define CORE_EN_OFF 0x4
|
||||
#define CORE_SET_OFF 0x8
|
||||
#define CORE_CLEAR_OFF 0xc
|
||||
#define CORE_BUF_20 0x20
|
||||
#define CORE_BUF_28 0x28
|
||||
#define CORE_BUF_30 0x30
|
||||
#define CORE_BUF_38 0x38
|
||||
#define IOCSR_IPI_SEND 0x40
|
||||
#define IOCSR_MAIL_SEND 0x48
|
||||
#define IOCSR_ANY_SEND 0x158
|
||||
|
||||
/* IPI system memory address */
|
||||
#define IPI_SYSTEM_MEM 0x1fe01000
|
||||
|
||||
#define MAX_IPI_CORE_NUM 4
|
||||
#define MAX_IPI_MBX_NUM 4
|
||||
|
||||
#define TYPE_LOONGARCH_IPI "loongarch_ipi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI)
|
||||
|
||||
typedef struct IPICore {
|
||||
uint32_t status;
|
||||
uint32_t en;
|
||||
uint32_t set;
|
||||
uint32_t clear;
|
||||
/* 64bit buf divide into 2 32bit buf */
|
||||
uint32_t buf[MAX_IPI_MBX_NUM * 2];
|
||||
qemu_irq irq;
|
||||
} IPICore;
|
||||
|
||||
struct LoongArchIPI {
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion ipi_iocsr_mem[MAX_IPI_CORE_NUM];
|
||||
MemoryRegion ipi_system_mem[MAX_IPI_CORE_NUM];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch 7A1000 I/O interrupt controller definitions
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHMSI, LOONGARCH_PCH_MSI)
|
||||
|
||||
/* Msi irq start start from 64 to 255 */
|
||||
#define PCH_MSI_IRQ_START 64
|
||||
#define PCH_MSI_IRQ_END 255
|
||||
#define PCH_MSI_IRQ_NUM 192
|
||||
|
||||
struct LoongArchPCHMSI {
|
||||
SysBusDevice parent_obj;
|
||||
qemu_irq pch_msi_irq[PCH_MSI_IRQ_NUM];
|
||||
MemoryRegion msi_mmio;
|
||||
};
|
|
@ -0,0 +1,69 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch 7A1000 I/O interrupt controller definitions
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic"
|
||||
#define PCH_PIC_NAME(name) TYPE_LOONGARCH_PCH_PIC#name
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC)
|
||||
|
||||
#define PCH_PIC_IRQ_START 0
|
||||
#define PCH_PIC_IRQ_END 63
|
||||
#define PCH_PIC_IRQ_NUM 64
|
||||
#define PCH_PIC_INT_ID_VAL 0x7000000UL
|
||||
#define PCH_PIC_INT_ID_NUM 0x3f0001UL
|
||||
|
||||
#define PCH_PIC_INT_ID_LO 0x00
|
||||
#define PCH_PIC_INT_ID_HI 0x04
|
||||
#define PCH_PIC_INT_MASK_LO 0x20
|
||||
#define PCH_PIC_INT_MASK_HI 0x24
|
||||
#define PCH_PIC_HTMSI_EN_LO 0x40
|
||||
#define PCH_PIC_HTMSI_EN_HI 0x44
|
||||
#define PCH_PIC_INT_EDGE_LO 0x60
|
||||
#define PCH_PIC_INT_EDGE_HI 0x64
|
||||
#define PCH_PIC_INT_CLEAR_LO 0x80
|
||||
#define PCH_PIC_INT_CLEAR_HI 0x84
|
||||
#define PCH_PIC_AUTO_CTRL0_LO 0xc0
|
||||
#define PCH_PIC_AUTO_CTRL0_HI 0xc4
|
||||
#define PCH_PIC_AUTO_CTRL1_LO 0xe0
|
||||
#define PCH_PIC_AUTO_CTRL1_HI 0xe4
|
||||
#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100
|
||||
#define PCH_PIC_ROUTE_ENTRY_END 0x13f
|
||||
#define PCH_PIC_HTMSI_VEC_OFFSET 0x200
|
||||
#define PCH_PIC_HTMSI_VEC_END 0x23f
|
||||
#define PCH_PIC_INT_STATUS_LO 0x3a0
|
||||
#define PCH_PIC_INT_STATUS_HI 0x3a4
|
||||
#define PCH_PIC_INT_POL_LO 0x3e0
|
||||
#define PCH_PIC_INT_POL_HI 0x3e4
|
||||
|
||||
#define STATUS_LO_START 0
|
||||
#define STATUS_HI_START 0x4
|
||||
#define POL_LO_START 0x40
|
||||
#define POL_HI_START 0x44
|
||||
struct LoongArchPCHPIC {
|
||||
SysBusDevice parent_obj;
|
||||
qemu_irq parent_irq[64];
|
||||
uint64_t int_mask; /*0x020 interrupt mask register*/
|
||||
uint64_t htmsi_en; /*0x040 1=msi*/
|
||||
uint64_t intedge; /*0x060 edge=1 level =0*/
|
||||
uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/
|
||||
uint64_t auto_crtl0; /*0x0c0*/
|
||||
uint64_t auto_crtl1; /*0x0e0*/
|
||||
uint64_t last_intirr; /* edge detection */
|
||||
uint64_t intirr; /* 0x380 interrupt request register */
|
||||
uint64_t intisr; /* 0x3a0 interrupt service register */
|
||||
/*
|
||||
* 0x3e0 interrupt level polarity selection
|
||||
* register 0 for high level trigger
|
||||
*/
|
||||
uint64_t int_polarity;
|
||||
|
||||
uint8_t route_entry[64]; /*0x100 - 0x138*/
|
||||
uint8_t htmsi_vector[64]; /*0x200 - 0x238*/
|
||||
|
||||
MemoryRegion iomem32_low;
|
||||
MemoryRegion iomem32_high;
|
||||
MemoryRegion iomem8;
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Definitions for loongarch board emulation.
|
||||
*
|
||||
* Copyright (C) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef HW_LOONGARCH_H
|
||||
#define HW_LOONGARCH_H
|
||||
|
||||
#include "target/loongarch/cpu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "hw/intc/loongarch_ipi.h"
|
||||
|
||||
#define LOONGARCH_MAX_VCPUS 4
|
||||
|
||||
#define LOONGARCH_ISA_IO_BASE 0x18000000UL
|
||||
#define LOONGARCH_ISA_IO_SIZE 0x0004000
|
||||
|
||||
struct LoongArchMachineState {
|
||||
/*< private >*/
|
||||
MachineState parent_obj;
|
||||
|
||||
IPICore ipi_core[MAX_IPI_CORE_NUM];
|
||||
MemoryRegion lowmem;
|
||||
MemoryRegion highmem;
|
||||
MemoryRegion isa_io;
|
||||
};
|
||||
|
||||
#define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt")
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_MACHINE)
|
||||
#endif
|
|
@ -0,0 +1,44 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch CPU
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef HW_LS7A_H
|
||||
#define HW_LS7A_H
|
||||
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "hw/pci-host/pam.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define LS7A_PCI_MEM_BASE 0x40000000UL
|
||||
#define LS7A_PCI_MEM_SIZE 0x40000000UL
|
||||
#define LS7A_PCI_IO_OFFSET 0x4000
|
||||
#define LS_PCIECFG_BASE 0x20000000
|
||||
#define LS_PCIECFG_SIZE 0x08000000
|
||||
#define LS7A_PCI_IO_BASE 0x18004000UL
|
||||
#define LS7A_PCI_IO_SIZE 0xC000
|
||||
|
||||
#define LS7A_PCH_REG_BASE 0x10000000UL
|
||||
#define LS7A_IOAPIC_REG_BASE (LS7A_PCH_REG_BASE)
|
||||
#define LS7A_PCH_MSI_ADDR_LOW 0x2FF00000UL
|
||||
|
||||
/*
|
||||
* According to the kernel pch irq start from 64 offset
|
||||
* 0 ~ 16 irqs used for non-pci device while 16 ~ 64 irqs
|
||||
* used for pci device.
|
||||
*/
|
||||
#define PCH_PIC_IRQ_OFFSET 64
|
||||
#define LS7A_DEVICE_IRQS 16
|
||||
#define LS7A_PCI_IRQS 48
|
||||
#define LS7A_UART_IRQ (PCH_PIC_IRQ_OFFSET + 2)
|
||||
#define LS7A_UART_BASE 0x1fe001e0
|
||||
#define LS7A_RTC_IRQ (PCH_PIC_IRQ_OFFSET + 3)
|
||||
#define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000)
|
||||
#define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100)
|
||||
#define LS7A_RTC_LEN 0x100
|
||||
#endif
|
|
@ -24,6 +24,7 @@ enum {
|
|||
QEMU_ARCH_RX = (1 << 20),
|
||||
QEMU_ARCH_AVR = (1 << 21),
|
||||
QEMU_ARCH_HEXAGON = (1 << 22),
|
||||
QEMU_ARCH_LOONGARCH = (1 << 23),
|
||||
};
|
||||
|
||||
extern const uint32_t arch_type;
|
||||
|
|
|
@ -2349,6 +2349,7 @@ disassemblers = {
|
|||
'sh4' : ['CONFIG_SH4_DIS'],
|
||||
'sparc' : ['CONFIG_SPARC_DIS'],
|
||||
'xtensa' : ['CONFIG_XTENSA_DIS'],
|
||||
'loongarch' : ['CONFIG_LOONGARCH_DIS'],
|
||||
}
|
||||
if link_language == 'cpp'
|
||||
disassemblers += {
|
||||
|
|
|
@ -323,7 +323,8 @@
|
|||
'TARGET_ARM',
|
||||
'TARGET_I386',
|
||||
'TARGET_S390X',
|
||||
'TARGET_MIPS' ] } }
|
||||
'TARGET_MIPS',
|
||||
'TARGET_LOONGARCH64' ] } }
|
||||
|
||||
##
|
||||
# @query-cpu-definitions:
|
||||
|
@ -339,4 +340,5 @@
|
|||
'TARGET_ARM',
|
||||
'TARGET_I386',
|
||||
'TARGET_S390X',
|
||||
'TARGET_MIPS' ] } }
|
||||
'TARGET_MIPS',
|
||||
'TARGET_LOONGARCH64' ] } }
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
##
|
||||
{ 'enum' : 'SysEmuTarget',
|
||||
'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386',
|
||||
'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
|
||||
'loongarch64', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
|
||||
'mips64el', 'mipsel', 'nios2', 'or1k', 'ppc',
|
||||
'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
|
||||
'sh4eb', 'sparc', 'sparc64', 'tricore',
|
||||
|
|
|
@ -60,7 +60,8 @@ typedef struct QDevAlias
|
|||
QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \
|
||||
QEMU_ARCH_MIPS | QEMU_ARCH_PPC | \
|
||||
QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \
|
||||
QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA)
|
||||
QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA | \
|
||||
QEMU_ARCH_LOONGARCH)
|
||||
#define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X)
|
||||
#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K)
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ source avr/Kconfig
|
|||
source cris/Kconfig
|
||||
source hppa/Kconfig
|
||||
source i386/Kconfig
|
||||
source loongarch/Kconfig
|
||||
source m68k/Kconfig
|
||||
source microblaze/Kconfig
|
||||
source mips/Kconfig
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
config LOONGARCH64
|
||||
bool
|
|
@ -0,0 +1,64 @@
|
|||
- Introduction
|
||||
|
||||
LoongArch is the general processor architecture of Loongson.
|
||||
|
||||
The following versions of the LoongArch core are supported
|
||||
core: 3A5000
|
||||
https://github.com/loongson/LoongArch-Documentation/releases/download/2021.08.17/LoongArch-Vol1-v1.00-EN.pdf
|
||||
|
||||
We can get the latest loongarch documents at https://github.com/loongson/LoongArch-Documentation/tags.
|
||||
|
||||
|
||||
- System emulation
|
||||
|
||||
Mainly emulate a virt 3A5000 board and ls7a bridge that is not exactly the same as the host.
|
||||
3A5000 support multiple interrupt cascading while here we just emulate the extioi interrupt
|
||||
cascading. LS7A1000 host bridge support multiple devices, such as sata, gmac, uart, rtc
|
||||
and so on. But we just realize the rtc. Others use the qemu common devices. It does not affect
|
||||
the general use. We also introduced the emulation of devices at docs/system/loongarch/loongson3.rst.
|
||||
|
||||
This version only supports running binary files in ELF format, and does not depend on BIOS and kernel file.
|
||||
You can compile the test program with 'make & make check-tcg' and run the test case with the following command:
|
||||
|
||||
1. Install LoongArch cross-tools on X86 machines.
|
||||
|
||||
Download cross-tools.
|
||||
|
||||
wget https://github.com/loongson/build-tools/releases/latest/download/loongarch64-clfs-20211202-cross-tools.tar.xz
|
||||
|
||||
tar -vxf loongarch64-clfs-20211202-cross-tools.tar.xz -C /opt
|
||||
|
||||
Config cross-tools env.
|
||||
|
||||
. setenv.sh
|
||||
|
||||
setenv.sh:
|
||||
|
||||
#!/bin/sh
|
||||
set -x
|
||||
CC_PREFIX=/opt/cross-tools
|
||||
|
||||
export PATH=$CC_PREFIX/bin:$PATH
|
||||
export LD_LIBRARY_PATH=$CC_PREFIX/lib:$LD_LIBRARY_PATH
|
||||
export LD_LIBRARY_PATH=$CC_PREFIX/loongarch64-unknown-linux-gnu/lib/:$LD_LIBRARY_PATH
|
||||
set +x
|
||||
|
||||
2. Test tests/tcg/multiarch.
|
||||
|
||||
./configure --disable-rdma --disable-pvrdma --prefix=/usr \
|
||||
--target-list="loongarch64-softmmu" \
|
||||
--disable-libiscsi --disable-libnfs --disable-libpmem \
|
||||
--disable-glusterfs --enable-libusb --enable-usb-redir \
|
||||
--disable-opengl --disable-xen --enable-spice --disable-werror \
|
||||
--enable-debug --disable-capstone --disable-kvm --enable-profiler
|
||||
|
||||
cd build/
|
||||
|
||||
make && make check-tcg
|
||||
|
||||
or
|
||||
|
||||
./build/qemu-system-loongarch64 -machine virt -m 4G -cpu Loongson-3A5000 -smp 1 -kernel build/tests/tcg/loongarch64-softmmu/hello -monitor none -display none -chardev file,path=hello.out,id=output -serial chardev:output
|
||||
|
||||
- Note.
|
||||
We can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/
|
|
@ -0,0 +1,64 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch constant timer support
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "cpu.h"
|
||||
#include "internals.h"
|
||||
#include "cpu-csr.h"
|
||||
|
||||
#define TIMER_PERIOD 10 /* 10 ns period for 100 MHz frequency */
|
||||
#define CONSTANT_TIMER_TICK_MASK 0xfffffffffffcUL
|
||||
#define CONSTANT_TIMER_ENABLE 0x1UL
|
||||
|
||||
uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu)
|
||||
{
|
||||
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
|
||||
}
|
||||
|
||||
uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu)
|
||||
{
|
||||
uint64_t now, expire;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
expire = timer_expire_time_ns(&cpu->timer);
|
||||
|
||||
return (expire - now) / TIMER_PERIOD;
|
||||
}
|
||||
|
||||
void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
|
||||
uint64_t value)
|
||||
{
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
uint64_t now, next;
|
||||
|
||||
env->CSR_TCFG = value;
|
||||
if (value & CONSTANT_TIMER_ENABLE) {
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
|
||||
timer_mod(&cpu->timer, next);
|
||||
} else {
|
||||
timer_del(&cpu->timer);
|
||||
}
|
||||
}
|
||||
|
||||
void loongarch_constant_timer_cb(void *opaque)
|
||||
{
|
||||
LoongArchCPU *cpu = opaque;
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
uint64_t now, next;
|
||||
|
||||
if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) {
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
next = now + (env->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
|
||||
timer_mod(&cpu->timer, next);
|
||||
} else {
|
||||
env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
|
||||
}
|
||||
|
||||
loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1);
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch CSRs
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef LOONGARCH_CPU_CSR_H
|
||||
#define LOONGARCH_CPU_CSR_H
|
||||
|
||||
#include "hw/registerfields.h"
|
||||
|
||||
/* Base on kernal definitions: arch/loongarch/include/asm/loongarch.h */
|
||||
|
||||
/* Basic CSRs */
|
||||
#define LOONGARCH_CSR_CRMD 0x0 /* Current mode info */
|
||||
|
||||
#define LOONGARCH_CSR_PRMD 0x1 /* Prev-exception mode info */
|
||||
FIELD(CSR_PRMD, PPLV, 0, 2)
|
||||
FIELD(CSR_PRMD, PIE, 2, 1)
|
||||
FIELD(CSR_PRMD, PWE, 3, 1)
|
||||
|
||||
#define LOONGARCH_CSR_EUEN 0x2 /* Extended unit enable */
|
||||
FIELD(CSR_EUEN, FPE, 0, 1)
|
||||
FIELD(CSR_EUEN, SXE, 1, 1)
|
||||
FIELD(CSR_EUEN, ASXE, 2, 1)
|
||||
FIELD(CSR_EUEN, BTE, 3, 1)
|
||||
|
||||
#define LOONGARCH_CSR_MISC 0x3 /* Misc config */
|
||||
FIELD(CSR_MISC, VA32, 0, 4)
|
||||
FIELD(CSR_MISC, DRDTL, 4, 4)
|
||||
FIELD(CSR_MISC, RPCNTL, 8, 4)
|
||||
FIELD(CSR_MISC, ALCL, 12, 4)
|
||||
FIELD(CSR_MISC, DWPL, 16, 3)
|
||||
|
||||
#define LOONGARCH_CSR_ECFG 0x4 /* Exception config */
|
||||
FIELD(CSR_ECFG, LIE, 0, 13)
|
||||
FIELD(CSR_ECFG, VS, 16, 3)
|
||||
|
||||
#define LOONGARCH_CSR_ESTAT 0x5 /* Exception status */
|
||||
FIELD(CSR_ESTAT, IS, 0, 13)
|
||||
FIELD(CSR_ESTAT, ECODE, 16, 6)
|
||||
FIELD(CSR_ESTAT, ESUBCODE, 22, 9)
|
||||
|
||||
#define LOONGARCH_CSR_ERA 0x6 /* Exception return address */
|
||||
|
||||
#define LOONGARCH_CSR_BADV 0x7 /* Bad virtual address */
|
||||
|
||||
#define LOONGARCH_CSR_BADI 0x8 /* Bad instruction */
|
||||
|
||||
#define LOONGARCH_CSR_EENTRY 0xc /* Exception entry address */
|
||||
|
||||
/* TLB related CSRs */
|
||||
#define LOONGARCH_CSR_TLBIDX 0x10 /* TLB Index, EHINV, PageSize, NP */
|
||||
FIELD(CSR_TLBIDX, INDEX, 0, 12)
|
||||
FIELD(CSR_TLBIDX, PS, 24, 6)
|
||||
FIELD(CSR_TLBIDX, NE, 31, 1)
|
||||
|
||||
#define LOONGARCH_CSR_TLBEHI 0x11 /* TLB EntryHi */
|
||||
FIELD(CSR_TLBEHI, VPPN, 13, 35)
|
||||
|
||||
#define LOONGARCH_CSR_TLBELO0 0x12 /* TLB EntryLo0 */
|
||||
#define LOONGARCH_CSR_TLBELO1 0x13 /* TLB EntryLo1 */
|
||||
FIELD(TLBENTRY, V, 0, 1)
|
||||
FIELD(TLBENTRY, D, 1, 1)
|
||||
FIELD(TLBENTRY, PLV, 2, 2)
|
||||
FIELD(TLBENTRY, MAT, 4, 2)
|
||||
FIELD(TLBENTRY, G, 6, 1)
|
||||
FIELD(TLBENTRY, PPN, 12, 36)
|
||||
FIELD(TLBENTRY, NR, 61, 1)
|
||||
FIELD(TLBENTRY, NX, 62, 1)
|
||||
FIELD(TLBENTRY, RPLV, 63, 1)
|
||||
|
||||
#define LOONGARCH_CSR_ASID 0x18 /* Address space identifier */
|
||||
FIELD(CSR_ASID, ASID, 0, 10)
|
||||
FIELD(CSR_ASID, ASIDBITS, 16, 8)
|
||||
|
||||
/* Page table base address when badv[47] = 0 */
|
||||
#define LOONGARCH_CSR_PGDL 0x19
|
||||
/* Page table base address when badv[47] = 1 */
|
||||
#define LOONGARCH_CSR_PGDH 0x1a
|
||||
|
||||
#define LOONGARCH_CSR_PGD 0x1b /* Page table base address */
|
||||
|
||||
/* Page walk controller's low addr */
|
||||
#define LOONGARCH_CSR_PWCL 0x1c
|
||||
FIELD(CSR_PWCL, PTBASE, 0, 5)
|
||||
FIELD(CSR_PWCL, PTWIDTH, 5, 5)
|
||||
FIELD(CSR_PWCL, DIR1_BASE, 10, 5)
|
||||
FIELD(CSR_PWCL, DIR1_WIDTH, 15, 5)
|
||||
FIELD(CSR_PWCL, DIR2_BASE, 20, 5)
|
||||
FIELD(CSR_PWCL, DIR2_WIDTH, 25, 5)
|
||||
FIELD(CSR_PWCL, PTEWIDTH, 30, 2)
|
||||
|
||||
/* Page walk controller's high addr */
|
||||
#define LOONGARCH_CSR_PWCH 0x1d
|
||||
FIELD(CSR_PWCH, DIR3_BASE, 0, 6)
|
||||
FIELD(CSR_PWCH, DIR3_WIDTH, 6, 6)
|
||||
FIELD(CSR_PWCH, DIR4_BASE, 12, 6)
|
||||
FIELD(CSR_PWCH, DIR4_WIDTH, 18, 6)
|
||||
|
||||
#define LOONGARCH_CSR_STLBPS 0x1e /* Stlb page size */
|
||||
FIELD(CSR_STLBPS, PS, 0, 5)
|
||||
|
||||
#define LOONGARCH_CSR_RVACFG 0x1f /* Reduced virtual address config */
|
||||
FIELD(CSR_RVACFG, RBITS, 0, 4)
|
||||
|
||||
/* Config CSRs */
|
||||
#define LOONGARCH_CSR_CPUID 0x20 /* CPU core id */
|
||||
|
||||
#define LOONGARCH_CSR_PRCFG1 0x21 /* Config1 */
|
||||
FIELD(CSR_PRCFG1, SAVE_NUM, 0, 4)
|
||||
FIELD(CSR_PRCFG1, TIMER_BITS, 4, 8)
|
||||
FIELD(CSR_PRCFG1, VSMAX, 12, 3)
|
||||
|
||||
#define LOONGARCH_CSR_PRCFG2 0x22 /* Config2 */
|
||||
|
||||
#define LOONGARCH_CSR_PRCFG3 0x23 /* Config3 */
|
||||
FIELD(CSR_PRCFG3, TLB_TYPE, 0, 4)
|
||||
FIELD(CSR_PRCFG3, MTLB_ENTRY, 4, 8)
|
||||
FIELD(CSR_PRCFG3, STLB_WAYS, 12, 8)
|
||||
FIELD(CSR_PRCFG3, STLB_SETS, 20, 8)
|
||||
|
||||
/*
|
||||
* Save registers count can read from PRCFG1.SAVE_NUM
|
||||
* The Min count is 1. Max count is 15.
|
||||
*/
|
||||
#define LOONGARCH_CSR_SAVE(N) (0x30 + N)
|
||||
|
||||
/* Timer CSRs */
|
||||
#define LOONGARCH_CSR_TID 0x40 /* Timer ID */
|
||||
|
||||
#define LOONGARCH_CSR_TCFG 0x41 /* Timer config */
|
||||
FIELD(CSR_TCFG, EN, 0, 1)
|
||||
FIELD(CSR_TCFG, PERIODIC, 1, 1)
|
||||
FIELD(CSR_TCFG, INIT_VAL, 2, 46)
|
||||
|
||||
#define LOONGARCH_CSR_TVAL 0x42 /* Timer ticks remain */
|
||||
|
||||
#define LOONGARCH_CSR_CNTC 0x43 /* Timer offset */
|
||||
|
||||
#define LOONGARCH_CSR_TICLR 0x44 /* Timer interrupt clear */
|
||||
|
||||
/* LLBCTL CSRs */
|
||||
#define LOONGARCH_CSR_LLBCTL 0x60 /* LLBit control */
|
||||
FIELD(CSR_LLBCTL, ROLLB, 0, 1)
|
||||
FIELD(CSR_LLBCTL, WCLLB, 1, 1)
|
||||
FIELD(CSR_LLBCTL, KLO, 2, 1)
|
||||
|
||||
/* Implement dependent */
|
||||
#define LOONGARCH_CSR_IMPCTL1 0x80 /* LoongArch config1 */
|
||||
|
||||
#define LOONGARCH_CSR_IMPCTL2 0x81 /* LoongArch config2*/
|
||||
|
||||
/* TLB Refill CSRs */
|
||||
#define LOONGARCH_CSR_TLBRENTRY 0x88 /* TLB refill exception address */
|
||||
#define LOONGARCH_CSR_TLBRBADV 0x89 /* TLB refill badvaddr */
|
||||
#define LOONGARCH_CSR_TLBRERA 0x8a /* TLB refill ERA */
|
||||
#define LOONGARCH_CSR_TLBRSAVE 0x8b /* KScratch for TLB refill */
|
||||
FIELD(CSR_TLBRERA, ISTLBR, 0, 1)
|
||||
FIELD(CSR_TLBRERA, PC, 2, 62)
|
||||
#define LOONGARCH_CSR_TLBRELO0 0x8c /* TLB refill entrylo0 */
|
||||
#define LOONGARCH_CSR_TLBRELO1 0x8d /* TLB refill entrylo1 */
|
||||
#define LOONGARCH_CSR_TLBREHI 0x8e /* TLB refill entryhi */
|
||||
FIELD(CSR_TLBREHI, PS, 0, 6)
|
||||
FIELD(CSR_TLBREHI, VPPN, 13, 35)
|
||||
#define LOONGARCH_CSR_TLBRPRMD 0x8f /* TLB refill mode info */
|
||||
FIELD(CSR_TLBRPRMD, PPLV, 0, 2)
|
||||
FIELD(CSR_TLBRPRMD, PIE, 2, 1)
|
||||
FIELD(CSR_TLBRPRMD, PWE, 4, 1)
|
||||
|
||||
/* Machine Error CSRs */
|
||||
#define LOONGARCH_CSR_MERRCTL 0x90 /* ERRCTL */
|
||||
FIELD(CSR_MERRCTL, ISMERR, 0, 1)
|
||||
#define LOONGARCH_CSR_MERRINFO1 0x91
|
||||
#define LOONGARCH_CSR_MERRINFO2 0x92
|
||||
#define LOONGARCH_CSR_MERRENTRY 0x93 /* MError exception base */
|
||||
#define LOONGARCH_CSR_MERRERA 0x94 /* MError exception PC */
|
||||
#define LOONGARCH_CSR_MERRSAVE 0x95 /* KScratch for error exception */
|
||||
|
||||
#define LOONGARCH_CSR_CTAG 0x98 /* TagLo + TagHi */
|
||||
|
||||
/* Direct map windows CSRs*/
|
||||
#define LOONGARCH_CSR_DMW(N) (0x180 + N)
|
||||
FIELD(CSR_DMW, PLV0, 0, 1)
|
||||
FIELD(CSR_DMW, PLV1, 1, 1)
|
||||
FIELD(CSR_DMW, PLV2, 2, 1)
|
||||
FIELD(CSR_DMW, PLV3, 3, 1)
|
||||
FIELD(CSR_DMW, MAT, 4, 2)
|
||||
FIELD(CSR_DMW, VSEG, 60, 4)
|
||||
|
||||
#define dmw_va2pa(va) \
|
||||
(va & MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS))
|
||||
|
||||
/* Debug CSRs */
|
||||
#define LOONGARCH_CSR_DBG 0x500 /* debug config */
|
||||
FIELD(CSR_DBG, DST, 0, 1)
|
||||
FIELD(CSR_DBG, DREV, 1, 7)
|
||||
FIELD(CSR_DBG, DEI, 8, 1)
|
||||
FIELD(CSR_DBG, DCL, 9, 1)
|
||||
FIELD(CSR_DBG, DFW, 10, 1)
|
||||
FIELD(CSR_DBG, DMW, 11, 1)
|
||||
FIELD(CSR_DBG, ECODE, 16, 6)
|
||||
|
||||
#define LOONGARCH_CSR_DERA 0x501 /* Debug era */
|
||||
#define LOONGARCH_CSR_DSAVE 0x502 /* Debug save */
|
||||
|
||||
#endif /* LOONGARCH_CPU_CSR_H */
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch CPU parameters for QEMU.
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef LOONGARCH_CPU_PARAM_H
|
||||
#define LOONGARCH_CPU_PARAM_H
|
||||
|
||||
#define TARGET_LONG_BITS 64
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 48
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 48
|
||||
|
||||
#define TARGET_PAGE_BITS 14
|
||||
#define NB_MMU_MODES 5
|
||||
|
||||
#endif
|
|
@ -0,0 +1,704 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch CPU
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "qapi/qapi-commands-machine-target.h"
|
||||
#include "cpu.h"
|
||||
#include "internals.h"
|
||||
#include "fpu/softfloat-helpers.h"
|
||||
#include "cpu-csr.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "hw/loader.h"
|
||||
|
||||
const char * const regnames[32] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
||||
};
|
||||
|
||||
const char * const fregnames[32] = {
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
||||
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
|
||||
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
|
||||
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
|
||||
};
|
||||
|
||||
static const char * const excp_names[] = {
|
||||
[EXCCODE_INT] = "Interrupt",
|
||||
[EXCCODE_PIL] = "Page invalid exception for load",
|
||||
[EXCCODE_PIS] = "Page invalid exception for store",
|
||||
[EXCCODE_PIF] = "Page invalid exception for fetch",
|
||||
[EXCCODE_PME] = "Page modified exception",
|
||||
[EXCCODE_PNR] = "Page Not Readable exception",
|
||||
[EXCCODE_PNX] = "Page Not Executable exception",
|
||||
[EXCCODE_PPI] = "Page Privilege error",
|
||||
[EXCCODE_ADEF] = "Address error for instruction fetch",
|
||||
[EXCCODE_ADEM] = "Address error for Memory access",
|
||||
[EXCCODE_SYS] = "Syscall",
|
||||
[EXCCODE_BRK] = "Break",
|
||||
[EXCCODE_INE] = "Instruction Non-Existent",
|
||||
[EXCCODE_IPE] = "Instruction privilege error",
|
||||
[EXCCODE_FPE] = "Floating Point Exception",
|
||||
[EXCCODE_DBP] = "Debug breakpoint",
|
||||
};
|
||||
|
||||
const char *loongarch_exception_name(int32_t exception)
|
||||
{
|
||||
assert(excp_names[exception]);
|
||||
return excp_names[exception];
|
||||
}
|
||||
|
||||
void G_NORETURN do_raise_exception(CPULoongArchState *env,
|
||||
uint32_t exception,
|
||||
uintptr_t pc)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "%s: %d (%s)\n",
|
||||
__func__,
|
||||
exception,
|
||||
loongarch_exception_name(exception));
|
||||
cs->exception_index = exception;
|
||||
|
||||
cpu_loop_exit_restore(cs, pc);
|
||||
}
|
||||
|
||||
static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
env->pc = value;
|
||||
}
|
||||
|
||||
#include "hw/loongarch/virt.h"
|
||||
|
||||
void loongarch_cpu_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
LoongArchCPU *cpu = opaque;
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
if (irq < 0 || irq >= N_IRQS) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0);
|
||||
|
||||
if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
|
||||
{
|
||||
bool ret = 0;
|
||||
|
||||
ret = (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE) &&
|
||||
!(FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if there is pending and not masked out interrupt */
|
||||
static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
|
||||
{
|
||||
uint32_t pending;
|
||||
uint32_t status;
|
||||
bool r;
|
||||
|
||||
pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
|
||||
status = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
|
||||
|
||||
r = (pending & status) != 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void loongarch_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
bool update_badinstr = 1;
|
||||
int cause = -1;
|
||||
const char *name;
|
||||
bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
|
||||
uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS);
|
||||
|
||||
if (cs->exception_index != EXCCODE_INT) {
|
||||
if (cs->exception_index < 0 ||
|
||||
cs->exception_index > ARRAY_SIZE(excp_names)) {
|
||||
name = "unknown";
|
||||
} else {
|
||||
name = excp_names[cs->exception_index];
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT,
|
||||
"%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx
|
||||
" TLBRERA " TARGET_FMT_lx " %s exception\n", __func__,
|
||||
env->pc, env->CSR_ERA, env->CSR_TLBRERA, name);
|
||||
}
|
||||
|
||||
switch (cs->exception_index) {
|
||||
case EXCCODE_DBP:
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1);
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC);
|
||||
goto set_DERA;
|
||||
set_DERA:
|
||||
env->CSR_DERA = env->pc;
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1);
|
||||
env->pc = env->CSR_EENTRY + 0x480;
|
||||
break;
|
||||
case EXCCODE_INT:
|
||||
if (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
|
||||
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DEI, 1);
|
||||
goto set_DERA;
|
||||
}
|
||||
QEMU_FALLTHROUGH;
|
||||
case EXCCODE_PIF:
|
||||
cause = cs->exception_index;
|
||||
update_badinstr = 0;
|
||||
break;
|
||||
case EXCCODE_ADEM:
|
||||
case EXCCODE_SYS:
|
||||
case EXCCODE_BRK:
|
||||
case EXCCODE_PIL:
|
||||
case EXCCODE_PIS:
|
||||
case EXCCODE_PME:
|
||||
case EXCCODE_PNR:
|
||||
case EXCCODE_PNX:
|
||||
case EXCCODE_PPI:
|
||||
case EXCCODE_INE:
|
||||
case EXCCODE_IPE:
|
||||
case EXCCODE_FPE:
|
||||
cause = cs->exception_index;
|
||||
break;
|
||||
default:
|
||||
qemu_log("Error: exception(%d) '%s' has not been supported\n",
|
||||
cs->exception_index, excp_names[cs->exception_index]);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (update_badinstr) {
|
||||
env->CSR_BADI = cpu_ldl_code(env, env->pc);
|
||||
}
|
||||
|
||||
/* Save PLV and IE */
|
||||
if (tlbfill) {
|
||||
env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV,
|
||||
FIELD_EX64(env->CSR_CRMD,
|
||||
CSR_CRMD, PLV));
|
||||
env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE,
|
||||
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
|
||||
/* set the DA mode */
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0);
|
||||
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA,
|
||||
PC, (env->pc >> 2));
|
||||
} else {
|
||||
env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause);
|
||||
env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV,
|
||||
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV));
|
||||
env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE,
|
||||
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
|
||||
env->CSR_ERA = env->pc;
|
||||
}
|
||||
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0);
|
||||
|
||||
if (cs->exception_index == EXCCODE_INT) {
|
||||
/* Interrupt */
|
||||
uint32_t vector = 0;
|
||||
uint32_t pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
|
||||
pending &= FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
|
||||
|
||||
/* Find the highest-priority interrupt. */
|
||||
vector = 31 - clz32(pending);
|
||||
env->pc = env->CSR_EENTRY + (EXCCODE_EXTERNAL_INT + vector) * vec_size;
|
||||
qemu_log_mask(CPU_LOG_INT,
|
||||
"%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
|
||||
" cause %d\n" " A " TARGET_FMT_lx " D "
|
||||
TARGET_FMT_lx " vector = %d ExC " TARGET_FMT_lx "ExS"
|
||||
TARGET_FMT_lx "\n",
|
||||
__func__, env->pc, env->CSR_ERA,
|
||||
cause, env->CSR_BADV, env->CSR_DERA, vector,
|
||||
env->CSR_ECFG, env->CSR_ESTAT);
|
||||
} else {
|
||||
if (tlbfill) {
|
||||
env->pc = env->CSR_TLBRENTRY;
|
||||
} else {
|
||||
env->pc = env->CSR_EENTRY;
|
||||
env->pc += cause * vec_size;
|
||||
}
|
||||
qemu_log_mask(CPU_LOG_INT,
|
||||
"%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
|
||||
" cause %d%s\n, ESTAT " TARGET_FMT_lx
|
||||
" EXCFG " TARGET_FMT_lx " BADVA " TARGET_FMT_lx
|
||||
"BADI " TARGET_FMT_lx " SYS_NUM " TARGET_FMT_lu
|
||||
" cpu %d asid " TARGET_FMT_lx "\n", __func__, env->pc,
|
||||
tlbfill ? env->CSR_TLBRERA : env->CSR_ERA,
|
||||
cause, tlbfill ? "(refill)" : "", env->CSR_ESTAT,
|
||||
env->CSR_ECFG,
|
||||
tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV,
|
||||
env->CSR_BADI, env->gpr[11], cs->cpu_index,
|
||||
env->CSR_ASID);
|
||||
}
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
|
||||
vaddr addr, unsigned size,
|
||||
MMUAccessType access_type,
|
||||
int mmu_idx, MemTxAttrs attrs,
|
||||
MemTxResult response,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
if (access_type == MMU_INST_FETCH) {
|
||||
do_raise_exception(env, EXCCODE_ADEF, retaddr);
|
||||
} else {
|
||||
do_raise_exception(env, EXCCODE_ADEM, retaddr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
if (cpu_loongarch_hw_interrupts_enabled(env) &&
|
||||
cpu_loongarch_hw_interrupts_pending(env)) {
|
||||
/* Raise it */
|
||||
cs->exception_index = EXCCODE_INT;
|
||||
loongarch_cpu_do_interrupt(cs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
|
||||
const TranslationBlock *tb)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
env->pc = tb->pc;
|
||||
}
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
static bool loongarch_cpu_has_work(CPUState *cs)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
bool has_work = false;
|
||||
|
||||
if ((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
cpu_loongarch_hw_interrupts_pending(env)) {
|
||||
has_work = true;
|
||||
}
|
||||
|
||||
return has_work;
|
||||
}
|
||||
|
||||
static void loongarch_la464_initfn(Object *obj)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 21; i++) {
|
||||
env->cpucfg[i] = 0x0;
|
||||
}
|
||||
|
||||
env->cpucfg[0] = 0x14c010; /* PRID */
|
||||
|
||||
uint32_t data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG1, ARCH, 2);
|
||||
data = FIELD_DP32(data, CPUCFG1, PGMMU, 1);
|
||||
data = FIELD_DP32(data, CPUCFG1, IOCSR, 1);
|
||||
data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f);
|
||||
data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f);
|
||||
data = FIELD_DP32(data, CPUCFG1, UAL, 1);
|
||||
data = FIELD_DP32(data, CPUCFG1, RI, 1);
|
||||
data = FIELD_DP32(data, CPUCFG1, EP, 1);
|
||||
data = FIELD_DP32(data, CPUCFG1, RPLV, 1);
|
||||
data = FIELD_DP32(data, CPUCFG1, HP, 1);
|
||||
data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1);
|
||||
env->cpucfg[1] = data;
|
||||
|
||||
data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG2, FP, 1);
|
||||
data = FIELD_DP32(data, CPUCFG2, FP_SP, 1);
|
||||
data = FIELD_DP32(data, CPUCFG2, FP_DP, 1);
|
||||
data = FIELD_DP32(data, CPUCFG2, FP_VER, 1);
|
||||
data = FIELD_DP32(data, CPUCFG2, LLFTP, 1);
|
||||
data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1);
|
||||
data = FIELD_DP32(data, CPUCFG2, LAM, 1);
|
||||
env->cpucfg[2] = data;
|
||||
|
||||
env->cpucfg[4] = 100 * 1000 * 1000; /* Crystal frequency */
|
||||
|
||||
data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG5, CC_MUL, 1);
|
||||
data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1);
|
||||
env->cpucfg[5] = data;
|
||||
|
||||
data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1);
|
||||
data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1);
|
||||
data = FIELD_DP32(data, CPUCFG16, L2_IUPRE, 1);
|
||||
data = FIELD_DP32(data, CPUCFG16, L2_IUUNIFY, 1);
|
||||
data = FIELD_DP32(data, CPUCFG16, L2_IUPRIV, 1);
|
||||
data = FIELD_DP32(data, CPUCFG16, L3_IUPRE, 1);
|
||||
data = FIELD_DP32(data, CPUCFG16, L3_IUUNIFY, 1);
|
||||
data = FIELD_DP32(data, CPUCFG16, L3_IUINCL, 1);
|
||||
env->cpucfg[16] = data;
|
||||
|
||||
data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG17, L1IU_WAYS, 3);
|
||||
data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 8);
|
||||
data = FIELD_DP32(data, CPUCFG17, L1IU_SIZE, 6);
|
||||
env->cpucfg[17] = data;
|
||||
|
||||
data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 3);
|
||||
data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 8);
|
||||
data = FIELD_DP32(data, CPUCFG18, L1D_SIZE, 6);
|
||||
env->cpucfg[18] = data;
|
||||
|
||||
data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 15);
|
||||
data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 8);
|
||||
data = FIELD_DP32(data, CPUCFG19, L2IU_SIZE, 6);
|
||||
env->cpucfg[19] = data;
|
||||
|
||||
data = 0;
|
||||
data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15);
|
||||
data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14);
|
||||
data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 6);
|
||||
env->cpucfg[20] = data;
|
||||
|
||||
env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
|
||||
}
|
||||
|
||||
static void loongarch_cpu_list_entry(gpointer data, gpointer user_data)
|
||||
{
|
||||
const char *typename = object_class_get_name(OBJECT_CLASS(data));
|
||||
|
||||
qemu_printf("%s\n", typename);
|
||||
}
|
||||
|
||||
void loongarch_cpu_list(void)
|
||||
{
|
||||
GSList *list;
|
||||
list = object_class_get_list_sorted(TYPE_LOONGARCH_CPU, false);
|
||||
g_slist_foreach(list, loongarch_cpu_list_entry, NULL);
|
||||
g_slist_free(list);
|
||||
}
|
||||
|
||||
static void loongarch_cpu_reset(DeviceState *dev)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
lacc->parent_reset(dev);
|
||||
|
||||
env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3;
|
||||
env->fcsr0 = 0x0;
|
||||
|
||||
int n;
|
||||
/* Set csr registers value after reset */
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 1);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 1);
|
||||
|
||||
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, FPE, 0);
|
||||
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, SXE, 0);
|
||||
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, ASXE, 0);
|
||||
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, BTE, 0);
|
||||
|
||||
env->CSR_MISC = 0;
|
||||
|
||||
env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, VS, 0);
|
||||
env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, LIE, 0);
|
||||
|
||||
env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2));
|
||||
env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0);
|
||||
env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
|
||||
env->CSR_LLBCTL = FIELD_DP64(env->CSR_LLBCTL, CSR_LLBCTL, KLO, 0);
|
||||
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
|
||||
env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0);
|
||||
|
||||
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
|
||||
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
|
||||
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
|
||||
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0);
|
||||
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0);
|
||||
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV2, 0);
|
||||
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0);
|
||||
}
|
||||
|
||||
env->pc = 0x1c000000;
|
||||
|
||||
restore_fp_status(env);
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info)
|
||||
{
|
||||
info->print_insn = print_insn_loongarch;
|
||||
}
|
||||
|
||||
static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
cpu_exec_realizefn(cs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
loongarch_cpu_register_gdb_regs_for_features(cs);
|
||||
|
||||
cpu_reset(cs);
|
||||
qemu_init_vcpu(cs);
|
||||
|
||||
lacc->parent_realize(dev, errp);
|
||||
}
|
||||
|
||||
static void loongarch_qemu_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
}
|
||||
|
||||
static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
switch (addr) {
|
||||
case FEATURE_REG:
|
||||
return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI |
|
||||
1ULL << IOCSRF_CSRIPI;
|
||||
case VENDOR_REG:
|
||||
return 0x6e6f73676e6f6f4cULL; /* "Loongson" */
|
||||
case CPUNAME_REG:
|
||||
return 0x303030354133ULL; /* "3A5000" */
|
||||
case MISC_FUNC_REG:
|
||||
return 1ULL << IOCSRM_EXTIOI_EN;
|
||||
}
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps loongarch_qemu_ops = {
|
||||
.read = loongarch_qemu_read,
|
||||
.write = loongarch_qemu_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 8,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static void loongarch_cpu_init(Object *obj)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
cpu_set_cpustate_pointers(cpu);
|
||||
qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS);
|
||||
timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL,
|
||||
&loongarch_constant_timer_cb, cpu);
|
||||
memory_region_init_io(&env->system_iocsr, OBJECT(cpu), NULL,
|
||||
env, "iocsr", UINT64_MAX);
|
||||
address_space_init(&env->address_space_iocsr, &env->system_iocsr, "IOCSR");
|
||||
memory_region_init_io(&env->iocsr_mem, OBJECT(cpu), &loongarch_qemu_ops,
|
||||
NULL, "iocsr_misc", 0x428);
|
||||
memory_region_add_subregion(&env->system_iocsr, 0, &env->iocsr_mem);
|
||||
}
|
||||
|
||||
static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
|
||||
{
|
||||
ObjectClass *oc;
|
||||
char *typename;
|
||||
|
||||
typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model);
|
||||
oc = object_class_by_name(typename);
|
||||
g_free(typename);
|
||||
return oc;
|
||||
}
|
||||
|
||||
void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
int i;
|
||||
|
||||
qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc);
|
||||
qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0,
|
||||
get_float_exception_flags(&env->fp_status));
|
||||
|
||||
/* gpr */
|
||||
for (i = 0; i < 32; i++) {
|
||||
if ((i & 3) == 0) {
|
||||
qemu_fprintf(f, " GPR%02d:", i);
|
||||
}
|
||||
qemu_fprintf(f, " %s %016" PRIx64, regnames[i], env->gpr[i]);
|
||||
if ((i & 3) == 3) {
|
||||
qemu_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
qemu_fprintf(f, "CRMD=%016" PRIx64 "\n", env->CSR_CRMD);
|
||||
qemu_fprintf(f, "PRMD=%016" PRIx64 "\n", env->CSR_PRMD);
|
||||
qemu_fprintf(f, "EUEN=%016" PRIx64 "\n", env->CSR_EUEN);
|
||||
qemu_fprintf(f, "ESTAT=%016" PRIx64 "\n", env->CSR_ESTAT);
|
||||
qemu_fprintf(f, "ERA=%016" PRIx64 "\n", env->CSR_ERA);
|
||||
qemu_fprintf(f, "BADV=%016" PRIx64 "\n", env->CSR_BADV);
|
||||
qemu_fprintf(f, "BADI=%016" PRIx64 "\n", env->CSR_BADI);
|
||||
qemu_fprintf(f, "EENTRY=%016" PRIx64 "\n", env->CSR_EENTRY);
|
||||
qemu_fprintf(f, "PRCFG1=%016" PRIx64 ", PRCFG2=%016" PRIx64 ","
|
||||
" PRCFG3=%016" PRIx64 "\n",
|
||||
env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3);
|
||||
qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY);
|
||||
qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV);
|
||||
qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA);
|
||||
|
||||
/* fpr */
|
||||
if (flags & CPU_DUMP_FPU) {
|
||||
for (i = 0; i < 32; i++) {
|
||||
qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i]);
|
||||
if ((i & 3) == 3) {
|
||||
qemu_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
|
||||
static struct TCGCPUOps loongarch_tcg_ops = {
|
||||
.initialize = loongarch_translate_init,
|
||||
.synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
|
||||
|
||||
.tlb_fill = loongarch_cpu_tlb_fill,
|
||||
.cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
|
||||
.do_interrupt = loongarch_cpu_do_interrupt,
|
||||
.do_transaction_failed = loongarch_cpu_do_transaction_failed,
|
||||
};
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
#include "hw/core/sysemu-cpu-ops.h"
|
||||
|
||||
static const struct SysemuCPUOps loongarch_sysemu_ops = {
|
||||
.get_phys_page_debug = loongarch_cpu_get_phys_page_debug,
|
||||
};
|
||||
|
||||
static void loongarch_cpu_class_init(ObjectClass *c, void *data)
|
||||
{
|
||||
LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c);
|
||||
CPUClass *cc = CPU_CLASS(c);
|
||||
DeviceClass *dc = DEVICE_CLASS(c);
|
||||
|
||||
device_class_set_parent_realize(dc, loongarch_cpu_realizefn,
|
||||
&lacc->parent_realize);
|
||||
device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset);
|
||||
|
||||
cc->class_by_name = loongarch_cpu_class_by_name;
|
||||
cc->has_work = loongarch_cpu_has_work;
|
||||
cc->dump_state = loongarch_cpu_dump_state;
|
||||
cc->set_pc = loongarch_cpu_set_pc;
|
||||
dc->vmsd = &vmstate_loongarch_cpu;
|
||||
cc->sysemu_ops = &loongarch_sysemu_ops;
|
||||
cc->disas_set_info = loongarch_cpu_disas_set_info;
|
||||
cc->gdb_read_register = loongarch_cpu_gdb_read_register;
|
||||
cc->gdb_write_register = loongarch_cpu_gdb_write_register;
|
||||
cc->disas_set_info = loongarch_cpu_disas_set_info;
|
||||
cc->gdb_num_core_regs = 34;
|
||||
cc->gdb_core_xml_file = "loongarch-base64.xml";
|
||||
cc->gdb_stop_before_watchpoint = true;
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
cc->tcg_ops = &loongarch_tcg_ops;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define DEFINE_LOONGARCH_CPU_TYPE(model, initfn) \
|
||||
{ \
|
||||
.parent = TYPE_LOONGARCH_CPU, \
|
||||
.instance_init = initfn, \
|
||||
.name = LOONGARCH_CPU_TYPE_NAME(model), \
|
||||
}
|
||||
|
||||
static const TypeInfo loongarch_cpu_type_infos[] = {
|
||||
{
|
||||
.name = TYPE_LOONGARCH_CPU,
|
||||
.parent = TYPE_CPU,
|
||||
.instance_size = sizeof(LoongArchCPU),
|
||||
.instance_init = loongarch_cpu_init,
|
||||
|
||||
.abstract = true,
|
||||
.class_size = sizeof(LoongArchCPUClass),
|
||||
.class_init = loongarch_cpu_class_init,
|
||||
},
|
||||
DEFINE_LOONGARCH_CPU_TYPE("la464", loongarch_la464_initfn),
|
||||
};
|
||||
|
||||
DEFINE_TYPES(loongarch_cpu_type_infos)
|
||||
|
||||
static void loongarch_cpu_add_definition(gpointer data, gpointer user_data)
|
||||
{
|
||||
ObjectClass *oc = data;
|
||||
CpuDefinitionInfoList **cpu_list = user_data;
|
||||
CpuDefinitionInfo *info = g_new0(CpuDefinitionInfo, 1);
|
||||
const char *typename = object_class_get_name(oc);
|
||||
|
||||
info->name = g_strndup(typename,
|
||||
strlen(typename) - strlen("-" TYPE_LOONGARCH_CPU));
|
||||
info->q_typename = g_strdup(typename);
|
||||
|
||||
QAPI_LIST_PREPEND(*cpu_list, info);
|
||||
}
|
||||
|
||||
CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
|
||||
{
|
||||
CpuDefinitionInfoList *cpu_list = NULL;
|
||||
GSList *list;
|
||||
|
||||
list = object_class_get_list(TYPE_LOONGARCH_CPU, false);
|
||||
g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list);
|
||||
g_slist_free(list);
|
||||
|
||||
return cpu_list;
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch CPU
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef LOONGARCH_CPU_H
|
||||
#define LOONGARCH_CPU_H
|
||||
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "fpu/softfloat-types.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/memory.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define IOCSRF_TEMP 0
|
||||
#define IOCSRF_NODECNT 1
|
||||
#define IOCSRF_MSI 2
|
||||
#define IOCSRF_EXTIOI 3
|
||||
#define IOCSRF_CSRIPI 4
|
||||
#define IOCSRF_FREQCSR 5
|
||||
#define IOCSRF_FREQSCALE 6
|
||||
#define IOCSRF_DVFSV1 7
|
||||
#define IOCSRF_GMOD 9
|
||||
#define IOCSRF_VM 11
|
||||
|
||||
#define FEATURE_REG 0x8
|
||||
#define VENDOR_REG 0x10
|
||||
#define CPUNAME_REG 0x20
|
||||
#define MISC_FUNC_REG 0x420
|
||||
#define IOCSRM_EXTIOI_EN 48
|
||||
|
||||
#define IOCSR_MEM_SIZE 0x428
|
||||
|
||||
#define TCG_GUEST_DEFAULT_MO (0)
|
||||
|
||||
#define FCSR0_M1 0x1f /* FCSR1 mask, Enables */
|
||||
#define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */
|
||||
#define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */
|
||||
#define FCSR0_RM 8 /* Round Mode bit num on fcsr0 */
|
||||
|
||||
FIELD(FCSR0, ENABLES, 0, 5)
|
||||
FIELD(FCSR0, RM, 8, 2)
|
||||
FIELD(FCSR0, FLAGS, 16, 5)
|
||||
FIELD(FCSR0, CAUSE, 24, 5)
|
||||
|
||||
#define GET_FP_CAUSE(REG) FIELD_EX32(REG, FCSR0, CAUSE)
|
||||
#define SET_FP_CAUSE(REG, V) FIELD_DP32(REG, FCSR0, CAUSE, V)
|
||||
#define GET_FP_ENABLES(REG) FIELD_EX32(REG, FCSR0, ENABLES)
|
||||
#define SET_FP_ENABLES(REG, V) FIELD_DP32(REG, FCSR0, ENABLES, V)
|
||||
#define GET_FP_FLAGS(REG) FIELD_EX32(REG, FCSR0, FLAGS)
|
||||
#define SET_FP_FLAGS(REG, V) FIELD_DP32(REG, FCSR0, FLAGS, V)
|
||||
#define UPDATE_FP_FLAGS(REG, V) \
|
||||
do { \
|
||||
(REG) |= FIELD_DP32(0, FCSR0, FLAGS, V); \
|
||||
} while (0)
|
||||
|
||||
#define FP_INEXACT 1
|
||||
#define FP_UNDERFLOW 2
|
||||
#define FP_OVERFLOW 4
|
||||
#define FP_DIV0 8
|
||||
#define FP_INVALID 16
|
||||
|
||||
#define EXCCODE_EXTERNAL_INT 64 /* plus external interrupt number */
|
||||
#define EXCCODE_INT 0
|
||||
#define EXCCODE_PIL 1
|
||||
#define EXCCODE_PIS 2
|
||||
#define EXCCODE_PIF 3
|
||||
#define EXCCODE_PME 4
|
||||
#define EXCCODE_PNR 5
|
||||
#define EXCCODE_PNX 6
|
||||
#define EXCCODE_PPI 7
|
||||
#define EXCCODE_ADEF 8 /* Different exception subcode */
|
||||
#define EXCCODE_ADEM 8
|
||||
#define EXCCODE_ALE 9
|
||||
#define EXCCODE_BCE 10
|
||||
#define EXCCODE_SYS 11
|
||||
#define EXCCODE_BRK 12
|
||||
#define EXCCODE_INE 13
|
||||
#define EXCCODE_IPE 14
|
||||
#define EXCCODE_FPD 15
|
||||
#define EXCCODE_SXD 16
|
||||
#define EXCCODE_ASXD 17
|
||||
#define EXCCODE_FPE 18 /* Different exception subcode */
|
||||
#define EXCCODE_VFPE 18
|
||||
#define EXCCODE_WPEF 19 /* Different exception subcode */
|
||||
#define EXCCODE_WPEM 19
|
||||
#define EXCCODE_BTD 20
|
||||
#define EXCCODE_BTE 21
|
||||
#define EXCCODE_DBP 26 /* Reserved subcode used for debug */
|
||||
|
||||
/* cpucfg[0] bits */
|
||||
FIELD(CPUCFG0, PRID, 0, 32)
|
||||
|
||||
/* cpucfg[1] bits */
|
||||
FIELD(CPUCFG1, ARCH, 0, 2)
|
||||
FIELD(CPUCFG1, PGMMU, 2, 1)
|
||||
FIELD(CPUCFG1, IOCSR, 3, 1)
|
||||
FIELD(CPUCFG1, PALEN, 4, 8)
|
||||
FIELD(CPUCFG1, VALEN, 12, 8)
|
||||
FIELD(CPUCFG1, UAL, 20, 1)
|
||||
FIELD(CPUCFG1, RI, 21, 1)
|
||||
FIELD(CPUCFG1, EP, 22, 1)
|
||||
FIELD(CPUCFG1, RPLV, 23, 1)
|
||||
FIELD(CPUCFG1, HP, 24, 1)
|
||||
FIELD(CPUCFG1, IOCSR_BRD, 25, 1)
|
||||
FIELD(CPUCFG1, MSG_INT, 26, 1)
|
||||
|
||||
/* cpucfg[2] bits */
|
||||
FIELD(CPUCFG2, FP, 0, 1)
|
||||
FIELD(CPUCFG2, FP_SP, 1, 1)
|
||||
FIELD(CPUCFG2, FP_DP, 2, 1)
|
||||
FIELD(CPUCFG2, FP_VER, 3, 3)
|
||||
FIELD(CPUCFG2, LSX, 6, 1)
|
||||
FIELD(CPUCFG2, LASX, 7, 1)
|
||||
FIELD(CPUCFG2, COMPLEX, 8, 1)
|
||||
FIELD(CPUCFG2, CRYPTO, 9, 1)
|
||||
FIELD(CPUCFG2, LVZ, 10, 1)
|
||||
FIELD(CPUCFG2, LVZ_VER, 11, 3)
|
||||
FIELD(CPUCFG2, LLFTP, 14, 1)
|
||||
FIELD(CPUCFG2, LLFTP_VER, 15, 3)
|
||||
FIELD(CPUCFG2, LBT_X86, 18, 1)
|
||||
FIELD(CPUCFG2, LBT_ARM, 19, 1)
|
||||
FIELD(CPUCFG2, LBT_MIPS, 20, 1)
|
||||
FIELD(CPUCFG2, LSPW, 21, 1)
|
||||
FIELD(CPUCFG2, LAM, 22, 1)
|
||||
|
||||
/* cpucfg[3] bits */
|
||||
FIELD(CPUCFG3, CCDMA, 0, 1)
|
||||
FIELD(CPUCFG3, SFB, 1, 1)
|
||||
FIELD(CPUCFG3, UCACC, 2, 1)
|
||||
FIELD(CPUCFG3, LLEXC, 3, 1)
|
||||
FIELD(CPUCFG3, SCDLY, 4, 1)
|
||||
FIELD(CPUCFG3, LLDBAR, 5, 1)
|
||||
FIELD(CPUCFG3, ITLBHMC, 6, 1)
|
||||
FIELD(CPUCFG3, ICHMC, 7, 1)
|
||||
FIELD(CPUCFG3, SPW_LVL, 8, 3)
|
||||
FIELD(CPUCFG3, SPW_HP_HF, 11, 1)
|
||||
FIELD(CPUCFG3, RVA, 12, 1)
|
||||
FIELD(CPUCFG3, RVAMAX, 13, 4)
|
||||
|
||||
/* cpucfg[4] bits */
|
||||
FIELD(CPUCFG4, CC_FREQ, 0, 32)
|
||||
|
||||
/* cpucfg[5] bits */
|
||||
FIELD(CPUCFG5, CC_MUL, 0, 16)
|
||||
FIELD(CPUCFG5, CC_DIV, 16, 16)
|
||||
|
||||
/* cpucfg[6] bits */
|
||||
FIELD(CPUCFG6, PMP, 0, 1)
|
||||
FIELD(CPUCFG6, PMVER, 1, 3)
|
||||
FIELD(CPUCFG6, PMNUM, 4, 4)
|
||||
FIELD(CPUCFG6, PMBITS, 8, 6)
|
||||
FIELD(CPUCFG6, UPM, 14, 1)
|
||||
|
||||
/* cpucfg[16] bits */
|
||||
FIELD(CPUCFG16, L1_IUPRE, 0, 1)
|
||||
FIELD(CPUCFG16, L1_IUUNIFY, 1, 1)
|
||||
FIELD(CPUCFG16, L1_DPRE, 2, 1)
|
||||
FIELD(CPUCFG16, L2_IUPRE, 3, 1)
|
||||
FIELD(CPUCFG16, L2_IUUNIFY, 4, 1)
|
||||
FIELD(CPUCFG16, L2_IUPRIV, 5, 1)
|
||||
FIELD(CPUCFG16, L2_IUINCL, 6, 1)
|
||||
FIELD(CPUCFG16, L2_DPRE, 7, 1)
|
||||
FIELD(CPUCFG16, L2_DPRIV, 8, 1)
|
||||
FIELD(CPUCFG16, L2_DINCL, 9, 1)
|
||||
FIELD(CPUCFG16, L3_IUPRE, 10, 1)
|
||||
FIELD(CPUCFG16, L3_IUUNIFY, 11, 1)
|
||||
FIELD(CPUCFG16, L3_IUPRIV, 12, 1)
|
||||
FIELD(CPUCFG16, L3_IUINCL, 13, 1)
|
||||
FIELD(CPUCFG16, L3_DPRE, 14, 1)
|
||||
FIELD(CPUCFG16, L3_DPRIV, 15, 1)
|
||||
FIELD(CPUCFG16, L3_DINCL, 16, 1)
|
||||
|
||||
/* cpucfg[17] bits */
|
||||
FIELD(CPUCFG17, L1IU_WAYS, 0, 16)
|
||||
FIELD(CPUCFG17, L1IU_SETS, 16, 8)
|
||||
FIELD(CPUCFG17, L1IU_SIZE, 24, 7)
|
||||
|
||||
/* cpucfg[18] bits */
|
||||
FIELD(CPUCFG18, L1D_WAYS, 0, 16)
|
||||
FIELD(CPUCFG18, L1D_SETS, 16, 8)
|
||||
FIELD(CPUCFG18, L1D_SIZE, 24, 7)
|
||||
|
||||
/* cpucfg[19] bits */
|
||||
FIELD(CPUCFG19, L2IU_WAYS, 0, 16)
|
||||
FIELD(CPUCFG19, L2IU_SETS, 16, 8)
|
||||
FIELD(CPUCFG19, L2IU_SIZE, 24, 7)
|
||||
|
||||
/* cpucfg[20] bits */
|
||||
FIELD(CPUCFG20, L3IU_WAYS, 0, 16)
|
||||
FIELD(CPUCFG20, L3IU_SETS, 16, 8)
|
||||
FIELD(CPUCFG20, L3IU_SIZE, 24, 7)
|
||||
|
||||
/*CSR_CRMD */
|
||||
FIELD(CSR_CRMD, PLV, 0, 2)
|
||||
FIELD(CSR_CRMD, IE, 2, 1)
|
||||
FIELD(CSR_CRMD, DA, 3, 1)
|
||||
FIELD(CSR_CRMD, PG, 4, 1)
|
||||
FIELD(CSR_CRMD, DATF, 5, 2)
|
||||
FIELD(CSR_CRMD, DATM, 7, 2)
|
||||
FIELD(CSR_CRMD, WE, 9, 1)
|
||||
|
||||
extern const char * const regnames[32];
|
||||
extern const char * const fregnames[32];
|
||||
|
||||
#define N_IRQS 13
|
||||
#define IRQ_TIMER 11
|
||||
#define IRQ_IPI 12
|
||||
|
||||
#define LOONGARCH_STLB 2048 /* 2048 STLB */
|
||||
#define LOONGARCH_MTLB 64 /* 64 MTLB */
|
||||
#define LOONGARCH_TLB_MAX (LOONGARCH_STLB + LOONGARCH_MTLB)
|
||||
|
||||
/*
|
||||
* define the ASID PS E VPPN field of TLB
|
||||
*/
|
||||
FIELD(TLB_MISC, E, 0, 1)
|
||||
FIELD(TLB_MISC, ASID, 1, 10)
|
||||
FIELD(TLB_MISC, VPPN, 13, 35)
|
||||
FIELD(TLB_MISC, PS, 48, 6)
|
||||
|
||||
struct LoongArchTLB {
|
||||
uint64_t tlb_misc;
|
||||
/* Fields corresponding to CSR_TLBELO0/1 */
|
||||
uint64_t tlb_entry0;
|
||||
uint64_t tlb_entry1;
|
||||
};
|
||||
typedef struct LoongArchTLB LoongArchTLB;
|
||||
|
||||
typedef struct CPUArchState {
|
||||
uint64_t gpr[32];
|
||||
uint64_t pc;
|
||||
|
||||
uint64_t fpr[32];
|
||||
float_status fp_status;
|
||||
bool cf[8];
|
||||
|
||||
uint32_t fcsr0;
|
||||
uint32_t fcsr0_mask;
|
||||
|
||||
uint32_t cpucfg[21];
|
||||
|
||||
uint64_t lladdr; /* LL virtual address compared against SC */
|
||||
uint64_t llval;
|
||||
|
||||
uint64_t badaddr;
|
||||
|
||||
/* LoongArch CSRs */
|
||||
uint64_t CSR_CRMD;
|
||||
uint64_t CSR_PRMD;
|
||||
uint64_t CSR_EUEN;
|
||||
uint64_t CSR_MISC;
|
||||
uint64_t CSR_ECFG;
|
||||
uint64_t CSR_ESTAT;
|
||||
uint64_t CSR_ERA;
|
||||
uint64_t CSR_BADV;
|
||||
uint64_t CSR_BADI;
|
||||
uint64_t CSR_EENTRY;
|
||||
uint64_t CSR_TLBIDX;
|
||||
uint64_t CSR_TLBEHI;
|
||||
uint64_t CSR_TLBELO0;
|
||||
uint64_t CSR_TLBELO1;
|
||||
uint64_t CSR_ASID;
|
||||
uint64_t CSR_PGDL;
|
||||
uint64_t CSR_PGDH;
|
||||
uint64_t CSR_PGD;
|
||||
uint64_t CSR_PWCL;
|
||||
uint64_t CSR_PWCH;
|
||||
uint64_t CSR_STLBPS;
|
||||
uint64_t CSR_RVACFG;
|
||||
uint64_t CSR_PRCFG1;
|
||||
uint64_t CSR_PRCFG2;
|
||||
uint64_t CSR_PRCFG3;
|
||||
uint64_t CSR_SAVE[16];
|
||||
uint64_t CSR_TID;
|
||||
uint64_t CSR_TCFG;
|
||||
uint64_t CSR_TVAL;
|
||||
uint64_t CSR_CNTC;
|
||||
uint64_t CSR_TICLR;
|
||||
uint64_t CSR_LLBCTL;
|
||||
uint64_t CSR_IMPCTL1;
|
||||
uint64_t CSR_IMPCTL2;
|
||||
uint64_t CSR_TLBRENTRY;
|
||||
uint64_t CSR_TLBRBADV;
|
||||
uint64_t CSR_TLBRERA;
|
||||
uint64_t CSR_TLBRSAVE;
|
||||
uint64_t CSR_TLBRELO0;
|
||||
uint64_t CSR_TLBRELO1;
|
||||
uint64_t CSR_TLBREHI;
|
||||
uint64_t CSR_TLBRPRMD;
|
||||
uint64_t CSR_MERRCTL;
|
||||
uint64_t CSR_MERRINFO1;
|
||||
uint64_t CSR_MERRINFO2;
|
||||
uint64_t CSR_MERRENTRY;
|
||||
uint64_t CSR_MERRERA;
|
||||
uint64_t CSR_MERRSAVE;
|
||||
uint64_t CSR_CTAG;
|
||||
uint64_t CSR_DMW[4];
|
||||
uint64_t CSR_DBG;
|
||||
uint64_t CSR_DERA;
|
||||
uint64_t CSR_DSAVE;
|
||||
|
||||
LoongArchTLB tlb[LOONGARCH_TLB_MAX];
|
||||
|
||||
AddressSpace address_space_iocsr;
|
||||
MemoryRegion system_iocsr;
|
||||
MemoryRegion iocsr_mem;
|
||||
bool load_elf;
|
||||
uint64_t elf_address;
|
||||
} CPULoongArchState;
|
||||
|
||||
/**
|
||||
* LoongArchCPU:
|
||||
* @env: #CPULoongArchState
|
||||
*
|
||||
* A LoongArch CPU.
|
||||
*/
|
||||
struct ArchCPU {
|
||||
/*< private >*/
|
||||
CPUState parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
CPUNegativeOffsetState neg;
|
||||
CPULoongArchState env;
|
||||
QEMUTimer timer;
|
||||
};
|
||||
|
||||
#define TYPE_LOONGARCH_CPU "loongarch-cpu"
|
||||
|
||||
OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass,
|
||||
LOONGARCH_CPU)
|
||||
|
||||
/**
|
||||
* LoongArchCPUClass:
|
||||
* @parent_realize: The parent class' realize handler.
|
||||
* @parent_reset: The parent class' reset handler.
|
||||
*
|
||||
* A LoongArch CPU model.
|
||||
*/
|
||||
struct LoongArchCPUClass {
|
||||
/*< private >*/
|
||||
CPUClass parent_class;
|
||||
/*< public >*/
|
||||
|
||||
DeviceRealize parent_realize;
|
||||
DeviceReset parent_reset;
|
||||
};
|
||||
|
||||
/*
|
||||
* LoongArch CPUs has 4 privilege levels.
|
||||
* 0 for kernel mode, 3 for user mode.
|
||||
* Define an extra index for DA(direct addressing) mode.
|
||||
*/
|
||||
#define MMU_KERNEL_IDX 0
|
||||
#define MMU_USER_IDX 3
|
||||
#define MMU_DA_IDX 4
|
||||
|
||||
static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch)
|
||||
{
|
||||
uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
|
||||
|
||||
if (!pg) {
|
||||
return MMU_DA_IDX;
|
||||
}
|
||||
return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
|
||||
}
|
||||
|
||||
static inline void cpu_get_tb_cpu_state(CPULoongArchState *env,
|
||||
target_ulong *pc,
|
||||
target_ulong *cs_base,
|
||||
uint32_t *flags)
|
||||
{
|
||||
*pc = env->pc;
|
||||
*cs_base = 0;
|
||||
*flags = cpu_mmu_index(env, false);
|
||||
}
|
||||
|
||||
void loongarch_cpu_list(void);
|
||||
|
||||
#define cpu_list loongarch_cpu_list
|
||||
|
||||
#include "exec/cpu-all.h"
|
||||
|
||||
#define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU
|
||||
#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
|
||||
#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
|
||||
|
||||
#endif /* LOONGARCH_CPU_H */
|
|
@ -0,0 +1,87 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch emulation helpers for CSRs
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "cpu.h"
|
||||
#include "internals.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "hw/irq.h"
|
||||
#include "cpu-csr.h"
|
||||
#include "tcg/tcg-ldst.h"
|
||||
|
||||
target_ulong helper_csrrd_pgd(CPULoongArchState *env)
|
||||
{
|
||||
int64_t v;
|
||||
|
||||
if (env->CSR_TLBRERA & 0x1) {
|
||||
v = env->CSR_TLBRBADV;
|
||||
} else {
|
||||
v = env->CSR_BADV;
|
||||
}
|
||||
|
||||
if ((v >> 63) & 0x1) {
|
||||
v = env->CSR_PGDH;
|
||||
} else {
|
||||
v = env->CSR_PGDL;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
target_ulong helper_csrrd_tval(CPULoongArchState *env)
|
||||
{
|
||||
LoongArchCPU *cpu = env_archcpu(env);
|
||||
|
||||
return cpu_loongarch_get_constant_timer_ticks(cpu);
|
||||
}
|
||||
|
||||
target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val)
|
||||
{
|
||||
int64_t old_v = env->CSR_ESTAT;
|
||||
|
||||
/* Only IS[1:0] can be written */
|
||||
env->CSR_ESTAT = deposit64(env->CSR_ESTAT, 0, 2, val);
|
||||
|
||||
return old_v;
|
||||
}
|
||||
|
||||
target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val)
|
||||
{
|
||||
int64_t old_v = env->CSR_ASID;
|
||||
|
||||
/* Only ASID filed of CSR_ASID can be written */
|
||||
env->CSR_ASID = deposit64(env->CSR_ASID, 0, 10, val);
|
||||
if (old_v != env->CSR_ASID) {
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
return old_v;
|
||||
}
|
||||
|
||||
target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val)
|
||||
{
|
||||
LoongArchCPU *cpu = env_archcpu(env);
|
||||
int64_t old_v = env->CSR_TCFG;
|
||||
|
||||
cpu_loongarch_store_constant_timer_config(cpu, val);
|
||||
|
||||
return old_v;
|
||||
}
|
||||
|
||||
target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val)
|
||||
{
|
||||
LoongArchCPU *cpu = env_archcpu(env);
|
||||
int64_t old_v = 0;
|
||||
|
||||
if (val & 0x1) {
|
||||
loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0);
|
||||
}
|
||||
return old_v;
|
||||
}
|
|
@ -0,0 +1,757 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch Disassembler
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "disas/dis-asm.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "cpu-csr.h"
|
||||
|
||||
typedef struct {
|
||||
disassemble_info *info;
|
||||
uint64_t pc;
|
||||
uint32_t insn;
|
||||
} DisasContext;
|
||||
|
||||
static inline int plus_1(DisasContext *ctx, int x)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
static inline int shl_2(DisasContext *ctx, int x)
|
||||
{
|
||||
return x << 2;
|
||||
}
|
||||
|
||||
#define CSR_NAME(REG) \
|
||||
[LOONGARCH_CSR_##REG] = (#REG)
|
||||
|
||||
static const char * const csr_names[] = {
|
||||
CSR_NAME(CRMD),
|
||||
CSR_NAME(PRMD),
|
||||
CSR_NAME(EUEN),
|
||||
CSR_NAME(MISC),
|
||||
CSR_NAME(ECFG),
|
||||
CSR_NAME(ESTAT),
|
||||
CSR_NAME(ERA),
|
||||
CSR_NAME(BADV),
|
||||
CSR_NAME(BADI),
|
||||
CSR_NAME(EENTRY),
|
||||
CSR_NAME(TLBIDX),
|
||||
CSR_NAME(TLBEHI),
|
||||
CSR_NAME(TLBELO0),
|
||||
CSR_NAME(TLBELO1),
|
||||
CSR_NAME(ASID),
|
||||
CSR_NAME(PGDL),
|
||||
CSR_NAME(PGDH),
|
||||
CSR_NAME(PGD),
|
||||
CSR_NAME(PWCL),
|
||||
CSR_NAME(PWCH),
|
||||
CSR_NAME(STLBPS),
|
||||
CSR_NAME(RVACFG),
|
||||
CSR_NAME(CPUID),
|
||||
CSR_NAME(PRCFG1),
|
||||
CSR_NAME(PRCFG2),
|
||||
CSR_NAME(PRCFG3),
|
||||
CSR_NAME(SAVE(0)),
|
||||
CSR_NAME(SAVE(1)),
|
||||
CSR_NAME(SAVE(2)),
|
||||
CSR_NAME(SAVE(3)),
|
||||
CSR_NAME(SAVE(4)),
|
||||
CSR_NAME(SAVE(5)),
|
||||
CSR_NAME(SAVE(6)),
|
||||
CSR_NAME(SAVE(7)),
|
||||
CSR_NAME(SAVE(8)),
|
||||
CSR_NAME(SAVE(9)),
|
||||
CSR_NAME(SAVE(10)),
|
||||
CSR_NAME(SAVE(11)),
|
||||
CSR_NAME(SAVE(12)),
|
||||
CSR_NAME(SAVE(13)),
|
||||
CSR_NAME(SAVE(14)),
|
||||
CSR_NAME(SAVE(15)),
|
||||
CSR_NAME(TID),
|
||||
CSR_NAME(TCFG),
|
||||
CSR_NAME(TVAL),
|
||||
CSR_NAME(CNTC),
|
||||
CSR_NAME(TICLR),
|
||||
CSR_NAME(LLBCTL),
|
||||
CSR_NAME(IMPCTL1),
|
||||
CSR_NAME(IMPCTL2),
|
||||
CSR_NAME(TLBRENTRY),
|
||||
CSR_NAME(TLBRBADV),
|
||||
CSR_NAME(TLBRERA),
|
||||
CSR_NAME(TLBRSAVE),
|
||||
CSR_NAME(TLBRELO0),
|
||||
CSR_NAME(TLBRELO1),
|
||||
CSR_NAME(TLBREHI),
|
||||
CSR_NAME(TLBRPRMD),
|
||||
CSR_NAME(MERRCTL),
|
||||
CSR_NAME(MERRINFO1),
|
||||
CSR_NAME(MERRINFO2),
|
||||
CSR_NAME(MERRENTRY),
|
||||
CSR_NAME(MERRERA),
|
||||
CSR_NAME(MERRSAVE),
|
||||
CSR_NAME(CTAG),
|
||||
CSR_NAME(DMW(0)),
|
||||
CSR_NAME(DMW(1)),
|
||||
CSR_NAME(DMW(2)),
|
||||
CSR_NAME(DMW(3)),
|
||||
CSR_NAME(DBG),
|
||||
CSR_NAME(DERA),
|
||||
CSR_NAME(DSAVE),
|
||||
};
|
||||
|
||||
static const char *get_csr_name(unsigned num)
|
||||
{
|
||||
return ((num < ARRAY_SIZE(csr_names)) && (csr_names[num] != NULL)) ?
|
||||
csr_names[num] : "Undefined CSR";
|
||||
}
|
||||
|
||||
#define output(C, INSN, FMT, ...) \
|
||||
{ \
|
||||
(C)->info->fprintf_func((C)->info->stream, "%08x %-9s\t" FMT, \
|
||||
(C)->insn, INSN, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#include "decode-insns.c.inc"
|
||||
|
||||
int print_insn_loongarch(bfd_vma memaddr, struct disassemble_info *info)
|
||||
{
|
||||
bfd_byte buffer[4];
|
||||
uint32_t insn;
|
||||
int status;
|
||||
|
||||
status = (*info->read_memory_func)(memaddr, buffer, 4, info);
|
||||
if (status != 0) {
|
||||
(*info->memory_error_func)(status, memaddr, info);
|
||||
return -1;
|
||||
}
|
||||
insn = bfd_getl32(buffer);
|
||||
DisasContext ctx = {
|
||||
.info = info,
|
||||
.pc = memaddr,
|
||||
.insn = insn
|
||||
};
|
||||
|
||||
if (!decode(&ctx, insn)) {
|
||||
output(&ctx, "illegal", "");
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
static void output_r_i(DisasContext *ctx, arg_r_i *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, %d", a->rd, a->imm);
|
||||
}
|
||||
|
||||
static void output_rrr(DisasContext *ctx, arg_rrr *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d, r%d", a->rd, a->rj, a->rk);
|
||||
}
|
||||
|
||||
static void output_rr_i(DisasContext *ctx, arg_rr_i *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d, %d", a->rd, a->rj, a->imm);
|
||||
}
|
||||
|
||||
static void output_rrr_sa(DisasContext *ctx, arg_rrr_sa *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d, r%d, %d", a->rd, a->rj, a->rk, a->sa);
|
||||
}
|
||||
|
||||
static void output_rr(DisasContext *ctx, arg_rr *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d", a->rd, a->rj);
|
||||
}
|
||||
|
||||
static void output_rr_ms_ls(DisasContext *ctx, arg_rr_ms_ls *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d, %d, %d", a->rd, a->rj, a->ms, a->ls);
|
||||
}
|
||||
|
||||
static void output_hint_r_i(DisasContext *ctx, arg_hint_r_i *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "%d, r%d, %d", a->hint, a->rj, a->imm);
|
||||
}
|
||||
|
||||
static void output_i(DisasContext *ctx, arg_i *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "%d", a->imm);
|
||||
}
|
||||
|
||||
static void output_rr_jk(DisasContext *ctx, arg_rr_jk *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d", a->rj, a->rk);
|
||||
}
|
||||
|
||||
static void output_ff(DisasContext *ctx, arg_ff *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, f%d", a->fd, a->fj);
|
||||
}
|
||||
|
||||
static void output_fff(DisasContext *ctx, arg_fff *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, f%d, f%d", a->fd, a->fj, a->fk);
|
||||
}
|
||||
|
||||
static void output_ffff(DisasContext *ctx, arg_ffff *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, f%d, f%d, f%d", a->fd, a->fj, a->fk, a->fa);
|
||||
}
|
||||
|
||||
static void output_fffc(DisasContext *ctx, arg_fffc *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, f%d, f%d, %d", a->fd, a->fj, a->fk, a->ca);
|
||||
}
|
||||
|
||||
static void output_fr(DisasContext *ctx, arg_fr *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, r%d", a->fd, a->rj);
|
||||
}
|
||||
|
||||
static void output_rf(DisasContext *ctx, arg_rf *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, f%d", a->rd, a->fj);
|
||||
}
|
||||
|
||||
static void output_fcsrd_r(DisasContext *ctx, arg_fcsrd_r *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "fcsr%d, r%d", a->fcsrd, a->rj);
|
||||
}
|
||||
|
||||
static void output_r_fcsrs(DisasContext *ctx, arg_r_fcsrs *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, fcsr%d", a->rd, a->fcsrs);
|
||||
}
|
||||
|
||||
static void output_cf(DisasContext *ctx, arg_cf *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "fcc%d, f%d", a->cd, a->fj);
|
||||
}
|
||||
|
||||
static void output_fc(DisasContext *ctx, arg_fc *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, fcc%d", a->fd, a->cj);
|
||||
}
|
||||
|
||||
static void output_cr(DisasContext *ctx, arg_cr *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "fcc%d, r%d", a->cd, a->rj);
|
||||
}
|
||||
|
||||
static void output_rc(DisasContext *ctx, arg_rc *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, fcc%d", a->rd, a->cj);
|
||||
}
|
||||
|
||||
static void output_frr(DisasContext *ctx, arg_frr *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, r%d, r%d", a->fd, a->rj, a->rk);
|
||||
}
|
||||
|
||||
static void output_fr_i(DisasContext *ctx, arg_fr_i *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "f%d, r%d, %d", a->fd, a->rj, a->imm);
|
||||
}
|
||||
|
||||
static void output_r_offs(DisasContext *ctx, arg_r_offs *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, %d # 0x%" PRIx64, a->rj, a->offs,
|
||||
ctx->pc + a->offs);
|
||||
}
|
||||
|
||||
static void output_c_offs(DisasContext *ctx, arg_c_offs *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "fcc%d, %d # 0x%" PRIx64, a->cj, a->offs,
|
||||
ctx->pc + a->offs);
|
||||
}
|
||||
|
||||
static void output_offs(DisasContext *ctx, arg_offs *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "%d # 0x%" PRIx64, a->offs, ctx->pc + a->offs);
|
||||
}
|
||||
|
||||
static void output_rr_offs(DisasContext *ctx, arg_rr_offs *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d, %d # 0x%" PRIx64, a->rj,
|
||||
a->rd, a->offs, ctx->pc + a->offs);
|
||||
}
|
||||
|
||||
static void output_r_csr(DisasContext *ctx, arg_r_csr *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, %d # %s", a->rd, a->csr, get_csr_name(a->csr));
|
||||
}
|
||||
|
||||
static void output_rr_csr(DisasContext *ctx, arg_rr_csr *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, r%d, %d # %s",
|
||||
a->rd, a->rj, a->csr, get_csr_name(a->csr));
|
||||
}
|
||||
|
||||
static void output_empty(DisasContext *ctx, arg_empty *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "");
|
||||
}
|
||||
|
||||
static void output_i_rr(DisasContext *ctx, arg_i_rr *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "%d, r%d, r%d", a->imm, a->rj, a->rk);
|
||||
}
|
||||
|
||||
static void output_cop_r_i(DisasContext *ctx, arg_cop_r_i *a,
|
||||
const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "%d, r%d, %d", a->cop, a->rj, a->imm);
|
||||
}
|
||||
|
||||
static void output_j_i(DisasContext *ctx, arg_j_i *a, const char *mnemonic)
|
||||
{
|
||||
output(ctx, mnemonic, "r%d, %d", a->rj, a->imm);
|
||||
}
|
||||
|
||||
#define INSN(insn, type) \
|
||||
static bool trans_##insn(DisasContext *ctx, arg_##type * a) \
|
||||
{ \
|
||||
output_##type(ctx, a, #insn); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
INSN(clo_w, rr)
|
||||
INSN(clz_w, rr)
|
||||
INSN(cto_w, rr)
|
||||
INSN(ctz_w, rr)
|
||||
INSN(clo_d, rr)
|
||||
INSN(clz_d, rr)
|
||||
INSN(cto_d, rr)
|
||||
INSN(ctz_d, rr)
|
||||
INSN(revb_2h, rr)
|
||||
INSN(revb_4h, rr)
|
||||
INSN(revb_2w, rr)
|
||||
INSN(revb_d, rr)
|
||||
INSN(revh_2w, rr)
|
||||
INSN(revh_d, rr)
|
||||
INSN(bitrev_4b, rr)
|
||||
INSN(bitrev_8b, rr)
|
||||
INSN(bitrev_w, rr)
|
||||
INSN(bitrev_d, rr)
|
||||
INSN(ext_w_h, rr)
|
||||
INSN(ext_w_b, rr)
|
||||
INSN(rdtimel_w, rr)
|
||||
INSN(rdtimeh_w, rr)
|
||||
INSN(rdtime_d, rr)
|
||||
INSN(cpucfg, rr)
|
||||
INSN(asrtle_d, rr_jk)
|
||||
INSN(asrtgt_d, rr_jk)
|
||||
INSN(alsl_w, rrr_sa)
|
||||
INSN(alsl_wu, rrr_sa)
|
||||
INSN(bytepick_w, rrr_sa)
|
||||
INSN(bytepick_d, rrr_sa)
|
||||
INSN(add_w, rrr)
|
||||
INSN(add_d, rrr)
|
||||
INSN(sub_w, rrr)
|
||||
INSN(sub_d, rrr)
|
||||
INSN(slt, rrr)
|
||||
INSN(sltu, rrr)
|
||||
INSN(maskeqz, rrr)
|
||||
INSN(masknez, rrr)
|
||||
INSN(nor, rrr)
|
||||
INSN(and, rrr)
|
||||
INSN(or, rrr)
|
||||
INSN(xor, rrr)
|
||||
INSN(orn, rrr)
|
||||
INSN(andn, rrr)
|
||||
INSN(sll_w, rrr)
|
||||
INSN(srl_w, rrr)
|
||||
INSN(sra_w, rrr)
|
||||
INSN(sll_d, rrr)
|
||||
INSN(srl_d, rrr)
|
||||
INSN(sra_d, rrr)
|
||||
INSN(rotr_w, rrr)
|
||||
INSN(rotr_d, rrr)
|
||||
INSN(mul_w, rrr)
|
||||
INSN(mulh_w, rrr)
|
||||
INSN(mulh_wu, rrr)
|
||||
INSN(mul_d, rrr)
|
||||
INSN(mulh_d, rrr)
|
||||
INSN(mulh_du, rrr)
|
||||
INSN(mulw_d_w, rrr)
|
||||
INSN(mulw_d_wu, rrr)
|
||||
INSN(div_w, rrr)
|
||||
INSN(mod_w, rrr)
|
||||
INSN(div_wu, rrr)
|
||||
INSN(mod_wu, rrr)
|
||||
INSN(div_d, rrr)
|
||||
INSN(mod_d, rrr)
|
||||
INSN(div_du, rrr)
|
||||
INSN(mod_du, rrr)
|
||||
INSN(crc_w_b_w, rrr)
|
||||
INSN(crc_w_h_w, rrr)
|
||||
INSN(crc_w_w_w, rrr)
|
||||
INSN(crc_w_d_w, rrr)
|
||||
INSN(crcc_w_b_w, rrr)
|
||||
INSN(crcc_w_h_w, rrr)
|
||||
INSN(crcc_w_w_w, rrr)
|
||||
INSN(crcc_w_d_w, rrr)
|
||||
INSN(break, i)
|
||||
INSN(syscall, i)
|
||||
INSN(alsl_d, rrr_sa)
|
||||
INSN(slli_w, rr_i)
|
||||
INSN(slli_d, rr_i)
|
||||
INSN(srli_w, rr_i)
|
||||
INSN(srli_d, rr_i)
|
||||
INSN(srai_w, rr_i)
|
||||
INSN(srai_d, rr_i)
|
||||
INSN(rotri_w, rr_i)
|
||||
INSN(rotri_d, rr_i)
|
||||
INSN(bstrins_w, rr_ms_ls)
|
||||
INSN(bstrpick_w, rr_ms_ls)
|
||||
INSN(bstrins_d, rr_ms_ls)
|
||||
INSN(bstrpick_d, rr_ms_ls)
|
||||
INSN(fadd_s, fff)
|
||||
INSN(fadd_d, fff)
|
||||
INSN(fsub_s, fff)
|
||||
INSN(fsub_d, fff)
|
||||
INSN(fmul_s, fff)
|
||||
INSN(fmul_d, fff)
|
||||
INSN(fdiv_s, fff)
|
||||
INSN(fdiv_d, fff)
|
||||
INSN(fmax_s, fff)
|
||||
INSN(fmax_d, fff)
|
||||
INSN(fmin_s, fff)
|
||||
INSN(fmin_d, fff)
|
||||
INSN(fmaxa_s, fff)
|
||||
INSN(fmaxa_d, fff)
|
||||
INSN(fmina_s, fff)
|
||||
INSN(fmina_d, fff)
|
||||
INSN(fscaleb_s, fff)
|
||||
INSN(fscaleb_d, fff)
|
||||
INSN(fcopysign_s, fff)
|
||||
INSN(fcopysign_d, fff)
|
||||
INSN(fabs_s, ff)
|
||||
INSN(fabs_d, ff)
|
||||
INSN(fneg_s, ff)
|
||||
INSN(fneg_d, ff)
|
||||
INSN(flogb_s, ff)
|
||||
INSN(flogb_d, ff)
|
||||
INSN(fclass_s, ff)
|
||||
INSN(fclass_d, ff)
|
||||
INSN(fsqrt_s, ff)
|
||||
INSN(fsqrt_d, ff)
|
||||
INSN(frecip_s, ff)
|
||||
INSN(frecip_d, ff)
|
||||
INSN(frsqrt_s, ff)
|
||||
INSN(frsqrt_d, ff)
|
||||
INSN(fmov_s, ff)
|
||||
INSN(fmov_d, ff)
|
||||
INSN(movgr2fr_w, fr)
|
||||
INSN(movgr2fr_d, fr)
|
||||
INSN(movgr2frh_w, fr)
|
||||
INSN(movfr2gr_s, rf)
|
||||
INSN(movfr2gr_d, rf)
|
||||
INSN(movfrh2gr_s, rf)
|
||||
INSN(movgr2fcsr, fcsrd_r)
|
||||
INSN(movfcsr2gr, r_fcsrs)
|
||||
INSN(movfr2cf, cf)
|
||||
INSN(movcf2fr, fc)
|
||||
INSN(movgr2cf, cr)
|
||||
INSN(movcf2gr, rc)
|
||||
INSN(fcvt_s_d, ff)
|
||||
INSN(fcvt_d_s, ff)
|
||||
INSN(ftintrm_w_s, ff)
|
||||
INSN(ftintrm_w_d, ff)
|
||||
INSN(ftintrm_l_s, ff)
|
||||
INSN(ftintrm_l_d, ff)
|
||||
INSN(ftintrp_w_s, ff)
|
||||
INSN(ftintrp_w_d, ff)
|
||||
INSN(ftintrp_l_s, ff)
|
||||
INSN(ftintrp_l_d, ff)
|
||||
INSN(ftintrz_w_s, ff)
|
||||
INSN(ftintrz_w_d, ff)
|
||||
INSN(ftintrz_l_s, ff)
|
||||
INSN(ftintrz_l_d, ff)
|
||||
INSN(ftintrne_w_s, ff)
|
||||
INSN(ftintrne_w_d, ff)
|
||||
INSN(ftintrne_l_s, ff)
|
||||
INSN(ftintrne_l_d, ff)
|
||||
INSN(ftint_w_s, ff)
|
||||
INSN(ftint_w_d, ff)
|
||||
INSN(ftint_l_s, ff)
|
||||
INSN(ftint_l_d, ff)
|
||||
INSN(ffint_s_w, ff)
|
||||
INSN(ffint_s_l, ff)
|
||||
INSN(ffint_d_w, ff)
|
||||
INSN(ffint_d_l, ff)
|
||||
INSN(frint_s, ff)
|
||||
INSN(frint_d, ff)
|
||||
INSN(slti, rr_i)
|
||||
INSN(sltui, rr_i)
|
||||
INSN(addi_w, rr_i)
|
||||
INSN(addi_d, rr_i)
|
||||
INSN(lu52i_d, rr_i)
|
||||
INSN(andi, rr_i)
|
||||
INSN(ori, rr_i)
|
||||
INSN(xori, rr_i)
|
||||
INSN(fmadd_s, ffff)
|
||||
INSN(fmadd_d, ffff)
|
||||
INSN(fmsub_s, ffff)
|
||||
INSN(fmsub_d, ffff)
|
||||
INSN(fnmadd_s, ffff)
|
||||
INSN(fnmadd_d, ffff)
|
||||
INSN(fnmsub_s, ffff)
|
||||
INSN(fnmsub_d, ffff)
|
||||
INSN(fsel, fffc)
|
||||
INSN(addu16i_d, rr_i)
|
||||
INSN(lu12i_w, r_i)
|
||||
INSN(lu32i_d, r_i)
|
||||
INSN(pcaddi, r_i)
|
||||
INSN(pcalau12i, r_i)
|
||||
INSN(pcaddu12i, r_i)
|
||||
INSN(pcaddu18i, r_i)
|
||||
INSN(ll_w, rr_i)
|
||||
INSN(sc_w, rr_i)
|
||||
INSN(ll_d, rr_i)
|
||||
INSN(sc_d, rr_i)
|
||||
INSN(ldptr_w, rr_i)
|
||||
INSN(stptr_w, rr_i)
|
||||
INSN(ldptr_d, rr_i)
|
||||
INSN(stptr_d, rr_i)
|
||||
INSN(ld_b, rr_i)
|
||||
INSN(ld_h, rr_i)
|
||||
INSN(ld_w, rr_i)
|
||||
INSN(ld_d, rr_i)
|
||||
INSN(st_b, rr_i)
|
||||
INSN(st_h, rr_i)
|
||||
INSN(st_w, rr_i)
|
||||
INSN(st_d, rr_i)
|
||||
INSN(ld_bu, rr_i)
|
||||
INSN(ld_hu, rr_i)
|
||||
INSN(ld_wu, rr_i)
|
||||
INSN(preld, hint_r_i)
|
||||
INSN(fld_s, fr_i)
|
||||
INSN(fst_s, fr_i)
|
||||
INSN(fld_d, fr_i)
|
||||
INSN(fst_d, fr_i)
|
||||
INSN(ldx_b, rrr)
|
||||
INSN(ldx_h, rrr)
|
||||
INSN(ldx_w, rrr)
|
||||
INSN(ldx_d, rrr)
|
||||
INSN(stx_b, rrr)
|
||||
INSN(stx_h, rrr)
|
||||
INSN(stx_w, rrr)
|
||||
INSN(stx_d, rrr)
|
||||
INSN(ldx_bu, rrr)
|
||||
INSN(ldx_hu, rrr)
|
||||
INSN(ldx_wu, rrr)
|
||||
INSN(fldx_s, frr)
|
||||
INSN(fldx_d, frr)
|
||||
INSN(fstx_s, frr)
|
||||
INSN(fstx_d, frr)
|
||||
INSN(amswap_w, rrr)
|
||||
INSN(amswap_d, rrr)
|
||||
INSN(amadd_w, rrr)
|
||||
INSN(amadd_d, rrr)
|
||||
INSN(amand_w, rrr)
|
||||
INSN(amand_d, rrr)
|
||||
INSN(amor_w, rrr)
|
||||
INSN(amor_d, rrr)
|
||||
INSN(amxor_w, rrr)
|
||||
INSN(amxor_d, rrr)
|
||||
INSN(ammax_w, rrr)
|
||||
INSN(ammax_d, rrr)
|
||||
INSN(ammin_w, rrr)
|
||||
INSN(ammin_d, rrr)
|
||||
INSN(ammax_wu, rrr)
|
||||
INSN(ammax_du, rrr)
|
||||
INSN(ammin_wu, rrr)
|
||||
INSN(ammin_du, rrr)
|
||||
INSN(amswap_db_w, rrr)
|
||||
INSN(amswap_db_d, rrr)
|
||||
INSN(amadd_db_w, rrr)
|
||||
INSN(amadd_db_d, rrr)
|
||||
INSN(amand_db_w, rrr)
|
||||
INSN(amand_db_d, rrr)
|
||||
INSN(amor_db_w, rrr)
|
||||
INSN(amor_db_d, rrr)
|
||||
INSN(amxor_db_w, rrr)
|
||||
INSN(amxor_db_d, rrr)
|
||||
INSN(ammax_db_w, rrr)
|
||||
INSN(ammax_db_d, rrr)
|
||||
INSN(ammin_db_w, rrr)
|
||||
INSN(ammin_db_d, rrr)
|
||||
INSN(ammax_db_wu, rrr)
|
||||
INSN(ammax_db_du, rrr)
|
||||
INSN(ammin_db_wu, rrr)
|
||||
INSN(ammin_db_du, rrr)
|
||||
INSN(dbar, i)
|
||||
INSN(ibar, i)
|
||||
INSN(fldgt_s, frr)
|
||||
INSN(fldgt_d, frr)
|
||||
INSN(fldle_s, frr)
|
||||
INSN(fldle_d, frr)
|
||||
INSN(fstgt_s, frr)
|
||||
INSN(fstgt_d, frr)
|
||||
INSN(fstle_s, frr)
|
||||
INSN(fstle_d, frr)
|
||||
INSN(ldgt_b, rrr)
|
||||
INSN(ldgt_h, rrr)
|
||||
INSN(ldgt_w, rrr)
|
||||
INSN(ldgt_d, rrr)
|
||||
INSN(ldle_b, rrr)
|
||||
INSN(ldle_h, rrr)
|
||||
INSN(ldle_w, rrr)
|
||||
INSN(ldle_d, rrr)
|
||||
INSN(stgt_b, rrr)
|
||||
INSN(stgt_h, rrr)
|
||||
INSN(stgt_w, rrr)
|
||||
INSN(stgt_d, rrr)
|
||||
INSN(stle_b, rrr)
|
||||
INSN(stle_h, rrr)
|
||||
INSN(stle_w, rrr)
|
||||
INSN(stle_d, rrr)
|
||||
INSN(beqz, r_offs)
|
||||
INSN(bnez, r_offs)
|
||||
INSN(bceqz, c_offs)
|
||||
INSN(bcnez, c_offs)
|
||||
INSN(jirl, rr_offs)
|
||||
INSN(b, offs)
|
||||
INSN(bl, offs)
|
||||
INSN(beq, rr_offs)
|
||||
INSN(bne, rr_offs)
|
||||
INSN(blt, rr_offs)
|
||||
INSN(bge, rr_offs)
|
||||
INSN(bltu, rr_offs)
|
||||
INSN(bgeu, rr_offs)
|
||||
INSN(csrrd, r_csr)
|
||||
INSN(csrwr, r_csr)
|
||||
INSN(csrxchg, rr_csr)
|
||||
INSN(iocsrrd_b, rr)
|
||||
INSN(iocsrrd_h, rr)
|
||||
INSN(iocsrrd_w, rr)
|
||||
INSN(iocsrrd_d, rr)
|
||||
INSN(iocsrwr_b, rr)
|
||||
INSN(iocsrwr_h, rr)
|
||||
INSN(iocsrwr_w, rr)
|
||||
INSN(iocsrwr_d, rr)
|
||||
INSN(tlbsrch, empty)
|
||||
INSN(tlbrd, empty)
|
||||
INSN(tlbwr, empty)
|
||||
INSN(tlbfill, empty)
|
||||
INSN(tlbclr, empty)
|
||||
INSN(tlbflush, empty)
|
||||
INSN(invtlb, i_rr)
|
||||
INSN(cacop, cop_r_i)
|
||||
INSN(lddir, rr_i)
|
||||
INSN(ldpte, j_i)
|
||||
INSN(ertn, empty)
|
||||
INSN(idle, i)
|
||||
INSN(dbcl, i)
|
||||
|
||||
#define output_fcmp(C, PREFIX, SUFFIX) \
|
||||
{ \
|
||||
(C)->info->fprintf_func((C)->info->stream, "%08x %s%s\tfcc%d, f%d, f%d", \
|
||||
(C)->insn, PREFIX, SUFFIX, a->cd, \
|
||||
a->fj, a->fk); \
|
||||
}
|
||||
|
||||
static bool output_cff_fcond(DisasContext *ctx, arg_cff_fcond * a,
|
||||
const char *suffix)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (a->fcond) {
|
||||
case 0x0:
|
||||
output_fcmp(ctx, "fcmp_caf_", suffix);
|
||||
break;
|
||||
case 0x1:
|
||||
output_fcmp(ctx, "fcmp_saf_", suffix);
|
||||
break;
|
||||
case 0x2:
|
||||
output_fcmp(ctx, "fcmp_clt_", suffix);
|
||||
break;
|
||||
case 0x3:
|
||||
output_fcmp(ctx, "fcmp_slt_", suffix);
|
||||
break;
|
||||
case 0x4:
|
||||
output_fcmp(ctx, "fcmp_ceq_", suffix);
|
||||
break;
|
||||
case 0x5:
|
||||
output_fcmp(ctx, "fcmp_seq_", suffix);
|
||||
break;
|
||||
case 0x6:
|
||||
output_fcmp(ctx, "fcmp_cle_", suffix);
|
||||
break;
|
||||
case 0x7:
|
||||
output_fcmp(ctx, "fcmp_sle_", suffix);
|
||||
break;
|
||||
case 0x8:
|
||||
output_fcmp(ctx, "fcmp_cun_", suffix);
|
||||
break;
|
||||
case 0x9:
|
||||
output_fcmp(ctx, "fcmp_sun_", suffix);
|
||||
break;
|
||||
case 0xA:
|
||||
output_fcmp(ctx, "fcmp_cult_", suffix);
|
||||
break;
|
||||
case 0xB:
|
||||
output_fcmp(ctx, "fcmp_sult_", suffix);
|
||||
break;
|
||||
case 0xC:
|
||||
output_fcmp(ctx, "fcmp_cueq_", suffix);
|
||||
break;
|
||||
case 0xD:
|
||||
output_fcmp(ctx, "fcmp_sueq_", suffix);
|
||||
break;
|
||||
case 0xE:
|
||||
output_fcmp(ctx, "fcmp_cule_", suffix);
|
||||
break;
|
||||
case 0xF:
|
||||
output_fcmp(ctx, "fcmp_sule_", suffix);
|
||||
break;
|
||||
case 0x10:
|
||||
output_fcmp(ctx, "fcmp_cne_", suffix);
|
||||
break;
|
||||
case 0x11:
|
||||
output_fcmp(ctx, "fcmp_sne_", suffix);
|
||||
break;
|
||||
case 0x14:
|
||||
output_fcmp(ctx, "fcmp_cor_", suffix);
|
||||
break;
|
||||
case 0x15:
|
||||
output_fcmp(ctx, "fcmp_sor_", suffix);
|
||||
break;
|
||||
case 0x18:
|
||||
output_fcmp(ctx, "fcmp_cune_", suffix);
|
||||
break;
|
||||
case 0x19:
|
||||
output_fcmp(ctx, "fcmp_sune_", suffix);
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define FCMP_INSN(suffix) \
|
||||
static bool trans_fcmp_cond_##suffix(DisasContext *ctx, \
|
||||
arg_cff_fcond * a) \
|
||||
{ \
|
||||
return output_cff_fcond(ctx, a, #suffix); \
|
||||
}
|
||||
|
||||
FCMP_INSN(s)
|
||||
FCMP_INSN(d)
|
|
@ -0,0 +1,862 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch float point emulation helpers for QEMU
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "fpu/softfloat.h"
|
||||
#include "internals.h"
|
||||
|
||||
#define FLOAT_TO_INT32_OVERFLOW 0x7fffffff
|
||||
#define FLOAT_TO_INT64_OVERFLOW 0x7fffffffffffffffULL
|
||||
|
||||
static inline uint64_t nanbox_s(float32 fp)
|
||||
{
|
||||
return fp | MAKE_64BIT_MASK(32, 32);
|
||||
}
|
||||
|
||||
/* Convert loongarch rounding mode in fcsr0 to IEEE library */
|
||||
static const FloatRoundMode ieee_rm[4] = {
|
||||
float_round_nearest_even,
|
||||
float_round_to_zero,
|
||||
float_round_up,
|
||||
float_round_down
|
||||
};
|
||||
|
||||
void restore_fp_status(CPULoongArchState *env)
|
||||
{
|
||||
set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3],
|
||||
&env->fp_status);
|
||||
set_flush_to_zero(0, &env->fp_status);
|
||||
}
|
||||
|
||||
static int ieee_ex_to_loongarch(int xcpt)
|
||||
{
|
||||
int ret = 0;
|
||||
if (xcpt & float_flag_invalid) {
|
||||
ret |= FP_INVALID;
|
||||
}
|
||||
if (xcpt & float_flag_overflow) {
|
||||
ret |= FP_OVERFLOW;
|
||||
}
|
||||
if (xcpt & float_flag_underflow) {
|
||||
ret |= FP_UNDERFLOW;
|
||||
}
|
||||
if (xcpt & float_flag_divbyzero) {
|
||||
ret |= FP_DIV0;
|
||||
}
|
||||
if (xcpt & float_flag_inexact) {
|
||||
ret |= FP_INEXACT;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_fcsr0_mask(CPULoongArchState *env, uintptr_t pc, int mask)
|
||||
{
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
set_float_exception_flags(0, &env->fp_status);
|
||||
|
||||
flags &= ~mask;
|
||||
|
||||
if (!flags) {
|
||||
SET_FP_CAUSE(env->fcsr0, flags);
|
||||
return;
|
||||
} else {
|
||||
flags = ieee_ex_to_loongarch(flags);
|
||||
SET_FP_CAUSE(env->fcsr0, flags);
|
||||
}
|
||||
|
||||
if (GET_FP_ENABLES(env->fcsr0) & flags) {
|
||||
do_raise_exception(env, EXCCODE_FPE, pc);
|
||||
} else {
|
||||
UPDATE_FP_FLAGS(env->fcsr0, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_fcsr0(CPULoongArchState *env, uintptr_t pc)
|
||||
{
|
||||
update_fcsr0_mask(env, pc, 0);
|
||||
}
|
||||
|
||||
uint64_t helper_fadd_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_add((uint32_t)fj, (uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fadd_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_add(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fsub_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_sub((uint32_t)fj, (uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fsub_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_sub(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmul_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_mul((uint32_t)fj, (uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmul_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_mul(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fdiv_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_div((uint32_t)fj, (uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fdiv_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_div(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmax_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_maxnum((uint32_t)fj, (uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmax_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_maxnum(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmin_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_minnum((uint32_t)fj, (uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmin_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_minnum(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmaxa_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_maxnummag((uint32_t)fj,
|
||||
(uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmaxa_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_maxnummag(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmina_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_minnummag((uint32_t)fj,
|
||||
(uint32_t)fk, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmina_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_minnummag(fj, fk, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fscaleb_s(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
int32_t n = (int32_t)fk;
|
||||
|
||||
fd = nanbox_s(float32_scalbn((uint32_t)fj,
|
||||
n > 0x200 ? 0x200 :
|
||||
n < -0x200 ? -0x200 : n,
|
||||
&env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fscaleb_d(CPULoongArchState *env, uint64_t fj, uint64_t fk)
|
||||
{
|
||||
uint64_t fd;
|
||||
int64_t n = (int64_t)fk;
|
||||
|
||||
fd = float64_scalbn(fj,
|
||||
n > 0x1000 ? 0x1000 :
|
||||
n < -0x1000 ? -0x1000 : n,
|
||||
&env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fsqrt_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_sqrt((uint32_t)fj, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fsqrt_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_sqrt(fj, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_frecip_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_div(float32_one, (uint32_t)fj, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_frecip_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_div(float64_one, fj, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_frsqrt_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
uint32_t fp;
|
||||
|
||||
fp = float32_sqrt((uint32_t)fj, &env->fp_status);
|
||||
fd = nanbox_s(float32_div(float32_one, fp, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_frsqrt_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fp, fd;
|
||||
|
||||
fp = float64_sqrt(fj, &env->fp_status);
|
||||
fd = float64_div(float64_one, fp, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_flogb_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
uint32_t fp;
|
||||
float_status *status = &env->fp_status;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(status);
|
||||
|
||||
set_float_rounding_mode(float_round_down, status);
|
||||
fp = float32_log2((uint32_t)fj, status);
|
||||
fd = nanbox_s(float32_round_to_int(fp, status));
|
||||
set_float_rounding_mode(old_mode, status);
|
||||
update_fcsr0_mask(env, GETPC(), float_flag_inexact);
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_flogb_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
float_status *status = &env->fp_status;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(status);
|
||||
|
||||
set_float_rounding_mode(float_round_down, status);
|
||||
fd = float64_log2(fj, status);
|
||||
fd = float64_round_to_int(fd, status);
|
||||
set_float_rounding_mode(old_mode, status);
|
||||
update_fcsr0_mask(env, GETPC(), float_flag_inexact);
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fclass_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
float32 f = fj;
|
||||
bool sign = float32_is_neg(f);
|
||||
|
||||
if (float32_is_infinity(f)) {
|
||||
return sign ? 1 << 2 : 1 << 6;
|
||||
} else if (float32_is_zero(f)) {
|
||||
return sign ? 1 << 5 : 1 << 9;
|
||||
} else if (float32_is_zero_or_denormal(f)) {
|
||||
return sign ? 1 << 4 : 1 << 8;
|
||||
} else if (float32_is_any_nan(f)) {
|
||||
float_status s = { }; /* for snan_bit_is_one */
|
||||
return float32_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0;
|
||||
} else {
|
||||
return sign ? 1 << 3 : 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t helper_fclass_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
float64 f = fj;
|
||||
bool sign = float64_is_neg(f);
|
||||
|
||||
if (float64_is_infinity(f)) {
|
||||
return sign ? 1 << 2 : 1 << 6;
|
||||
} else if (float64_is_zero(f)) {
|
||||
return sign ? 1 << 5 : 1 << 9;
|
||||
} else if (float64_is_zero_or_denormal(f)) {
|
||||
return sign ? 1 << 4 : 1 << 8;
|
||||
} else if (float64_is_any_nan(f)) {
|
||||
float_status s = { }; /* for snan_bit_is_one */
|
||||
return float64_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0;
|
||||
} else {
|
||||
return sign ? 1 << 3 : 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t helper_fmuladd_s(CPULoongArchState *env, uint64_t fj,
|
||||
uint64_t fk, uint64_t fa, uint32_t flag)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float32_muladd((uint32_t)fj, (uint32_t)fk,
|
||||
(uint32_t)fa, flag, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fmuladd_d(CPULoongArchState *env, uint64_t fj,
|
||||
uint64_t fk, uint64_t fa, uint32_t flag)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_muladd(fj, fk, fa, flag, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
static uint64_t fcmp_common(CPULoongArchState *env, FloatRelation cmp,
|
||||
uint32_t flags)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
switch (cmp) {
|
||||
case float_relation_less:
|
||||
ret = (flags & FCMP_LT);
|
||||
break;
|
||||
case float_relation_equal:
|
||||
ret = (flags & FCMP_EQ);
|
||||
break;
|
||||
case float_relation_greater:
|
||||
ret = (flags & FCMP_GT);
|
||||
break;
|
||||
case float_relation_unordered:
|
||||
ret = (flags & FCMP_UN);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fcmp_cXXX_s */
|
||||
uint64_t helper_fcmp_c_s(CPULoongArchState *env, uint64_t fj,
|
||||
uint64_t fk, uint32_t flags)
|
||||
{
|
||||
FloatRelation cmp = float32_compare_quiet((uint32_t)fj,
|
||||
(uint32_t)fk, &env->fp_status);
|
||||
return fcmp_common(env, cmp, flags);
|
||||
}
|
||||
|
||||
/* fcmp_sXXX_s */
|
||||
uint64_t helper_fcmp_s_s(CPULoongArchState *env, uint64_t fj,
|
||||
uint64_t fk, uint32_t flags)
|
||||
{
|
||||
FloatRelation cmp = float32_compare((uint32_t)fj,
|
||||
(uint32_t)fk, &env->fp_status);
|
||||
return fcmp_common(env, cmp, flags);
|
||||
}
|
||||
|
||||
/* fcmp_cXXX_d */
|
||||
uint64_t helper_fcmp_c_d(CPULoongArchState *env, uint64_t fj,
|
||||
uint64_t fk, uint32_t flags)
|
||||
{
|
||||
FloatRelation cmp = float64_compare_quiet(fj, fk, &env->fp_status);
|
||||
return fcmp_common(env, cmp, flags);
|
||||
}
|
||||
|
||||
/* fcmp_sXXX_d */
|
||||
uint64_t helper_fcmp_s_d(CPULoongArchState *env, uint64_t fj,
|
||||
uint64_t fk, uint32_t flags)
|
||||
{
|
||||
FloatRelation cmp = float64_compare(fj, fk, &env->fp_status);
|
||||
return fcmp_common(env, cmp, flags);
|
||||
}
|
||||
|
||||
/* floating point conversion */
|
||||
uint64_t helper_fcvt_s_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(float64_to_float32(fj, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_fcvt_d_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float32_to_float64((uint32_t)fj, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ffint_s_w(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(int32_to_float32((int32_t)fj, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ffint_s_l(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = nanbox_s(int64_to_float32(fj, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ffint_d_w(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = int32_to_float64((int32_t)fj, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ffint_d_l(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = int64_to_float64(fj, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_frint_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = (uint64_t)(float32_round_to_int((uint32_t)fj, &env->fp_status));
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_frint_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_round_to_int(fj, &env->fp_status);
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrm_l_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_down, &env->fp_status);
|
||||
fd = float64_to_int64(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrm_l_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_down, &env->fp_status);
|
||||
fd = float32_to_int64((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrm_w_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_down, &env->fp_status);
|
||||
fd = (uint64_t)float64_to_int32(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrm_w_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_down, &env->fp_status);
|
||||
fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrp_l_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_up, &env->fp_status);
|
||||
fd = float64_to_int64(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrp_l_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_up, &env->fp_status);
|
||||
fd = float32_to_int64((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrp_w_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_up, &env->fp_status);
|
||||
fd = (uint64_t)float64_to_int32(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrp_w_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_up, &env->fp_status);
|
||||
fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrz_l_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
fd = float64_to_int64_round_to_zero(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrz_l_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
fd = float32_to_int64_round_to_zero((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrz_w_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
fd = (uint64_t)float64_to_int32_round_to_zero(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrz_w_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint32_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
fd = float32_to_int32_round_to_zero((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return (uint64_t)fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrne_l_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
|
||||
fd = float64_to_int64(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrne_l_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
|
||||
fd = float32_to_int64((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrne_w_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
|
||||
fd = (uint64_t)float64_to_int32(fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftintrne_w_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint32_t fd;
|
||||
FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status);
|
||||
|
||||
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
|
||||
fd = float32_to_int32((uint32_t)fj, &env->fp_status);
|
||||
set_float_rounding_mode(old_mode, &env->fp_status);
|
||||
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return (uint64_t)fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftint_l_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float64_to_int64(fj, &env->fp_status);
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftint_l_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = float32_to_int64((uint32_t)fj, &env->fp_status);
|
||||
if (get_float_exception_flags(&env->fp_status) &
|
||||
(float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT64_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftint_w_s(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status);
|
||||
if (get_float_exception_flags(&env->fp_status)
|
||||
& (float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t helper_ftint_w_d(CPULoongArchState *env, uint64_t fj)
|
||||
{
|
||||
uint64_t fd;
|
||||
|
||||
fd = (uint64_t)float64_to_int32(fj, &env->fp_status);
|
||||
if (get_float_exception_flags(&env->fp_status)
|
||||
& (float_flag_invalid | float_flag_overflow)) {
|
||||
fd = FLOAT_TO_INT32_OVERFLOW;
|
||||
}
|
||||
update_fcsr0(env, GETPC());
|
||||
return fd;
|
||||
}
|
||||
|
||||
void helper_set_rounding_mode(CPULoongArchState *env, uint32_t fcsr0)
|
||||
{
|
||||
set_float_rounding_mode(ieee_rm[(fcsr0 >> FCSR0_RM) & 0x3],
|
||||
&env->fp_status);
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* LOONGARCH gdb server stub
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "internals.h"
|
||||
#include "exec/gdbstub.h"
|
||||
|
||||
int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
|
||||
if (0 <= n && n < 32) {
|
||||
return gdb_get_regl(mem_buf, env->gpr[n]);
|
||||
} else if (n == 32) {
|
||||
return gdb_get_regl(mem_buf, env->pc);
|
||||
} else if (n == 33) {
|
||||
return gdb_get_regl(mem_buf, env->badaddr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
target_ulong tmp = ldtul_p(mem_buf);
|
||||
int length = 0;
|
||||
|
||||
if (0 <= n && n < 32) {
|
||||
env->gpr[n] = tmp;
|
||||
length = sizeof(target_ulong);
|
||||
} else if (n == 32) {
|
||||
env->pc = tmp;
|
||||
length = sizeof(target_ulong);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static int loongarch_gdb_get_fpu(CPULoongArchState *env,
|
||||
GByteArray *mem_buf, int n)
|
||||
{
|
||||
if (0 <= n && n < 32) {
|
||||
return gdb_get_reg64(mem_buf, env->fpr[n]);
|
||||
} else if (32 <= n && n < 40) {
|
||||
return gdb_get_reg8(mem_buf, env->cf[n - 32]);
|
||||
} else if (n == 40) {
|
||||
return gdb_get_reg32(mem_buf, env->fcsr0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loongarch_gdb_set_fpu(CPULoongArchState *env,
|
||||
uint8_t *mem_buf, int n)
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
if (0 <= n && n < 32) {
|
||||
env->fpr[n] = ldq_p(mem_buf);
|
||||
length = 8;
|
||||
} else if (32 <= n && n < 40) {
|
||||
env->cf[n - 32] = ldub_p(mem_buf);
|
||||
length = 1;
|
||||
} else if (n == 40) {
|
||||
env->fcsr0 = ldl_p(mem_buf);
|
||||
length = 4;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs)
|
||||
{
|
||||
gdb_register_coprocessor(cs, loongarch_gdb_get_fpu, loongarch_gdb_set_fpu,
|
||||
41, "loongarch-fpu64.xml", 0);
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
DEF_HELPER_2(raise_exception, noreturn, env, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_1(bitrev_w, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
DEF_HELPER_FLAGS_1(bitrev_d, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
|
||||
DEF_HELPER_FLAGS_3(asrtle_d, TCG_CALL_NO_WG, void, env, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl)
|
||||
|
||||
DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
|
||||
DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl)
|
||||
|
||||
/* Floating-point helper */
|
||||
DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmaxa_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmaxa_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmina_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmina_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_5(fmuladd_s, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32)
|
||||
DEF_HELPER_FLAGS_5(fmuladd_d, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(fscaleb_s, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fscaleb_d, TCG_CALL_NO_WG, i64, env, i64, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_2(flogb_s, TCG_CALL_NO_WG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(flogb_d, TCG_CALL_NO_WG, i64, env, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_WG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_WG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(frsqrt_s, TCG_CALL_NO_WG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(frsqrt_d, TCG_CALL_NO_WG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(frecip_s, TCG_CALL_NO_WG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(frecip_d, TCG_CALL_NO_WG, i64, env, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fclass_d, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
|
||||
/* fcmp.cXXX.s */
|
||||
DEF_HELPER_4(fcmp_c_s, i64, env, i64, i64, i32)
|
||||
/* fcmp.sXXX.s */
|
||||
DEF_HELPER_4(fcmp_s_s, i64, env, i64, i64, i32)
|
||||
/* fcmp.cXXX.d */
|
||||
DEF_HELPER_4(fcmp_c_d, i64, env, i64, i64, i32)
|
||||
/* fcmp.sXXX.d */
|
||||
DEF_HELPER_4(fcmp_s_d, i64, env, i64, i64, i32)
|
||||
|
||||
DEF_HELPER_2(fcvt_d_s, i64, env, i64)
|
||||
DEF_HELPER_2(fcvt_s_d, i64, env, i64)
|
||||
DEF_HELPER_2(ffint_d_w, i64, env, i64)
|
||||
DEF_HELPER_2(ffint_d_l, i64, env, i64)
|
||||
DEF_HELPER_2(ffint_s_w, i64, env, i64)
|
||||
DEF_HELPER_2(ffint_s_l, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrm_l_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrm_l_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrm_w_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrm_w_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrp_l_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrp_l_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrp_w_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrp_w_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrz_l_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrz_l_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrz_w_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrz_w_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrne_l_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrne_l_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrne_w_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftintrne_w_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftint_l_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftint_l_d, i64, env, i64)
|
||||
DEF_HELPER_2(ftint_w_s, i64, env, i64)
|
||||
DEF_HELPER_2(ftint_w_d, i64, env, i64)
|
||||
DEF_HELPER_2(frint_s, i64, env, i64)
|
||||
DEF_HELPER_2(frint_d, i64, env, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_RWG, void, env, i32)
|
||||
|
||||
DEF_HELPER_1(rdtime_d, i64, env)
|
||||
|
||||
/* CSRs helper */
|
||||
DEF_HELPER_1(csrrd_pgd, i64, env)
|
||||
DEF_HELPER_1(csrrd_tval, i64, env)
|
||||
DEF_HELPER_2(csrwr_estat, i64, env, tl)
|
||||
DEF_HELPER_2(csrwr_asid, i64, env, tl)
|
||||
DEF_HELPER_2(csrwr_tcfg, i64, env, tl)
|
||||
DEF_HELPER_2(csrwr_ticlr, i64, env, tl)
|
||||
DEF_HELPER_2(iocsrrd_b, i64, env, tl)
|
||||
DEF_HELPER_2(iocsrrd_h, i64, env, tl)
|
||||
DEF_HELPER_2(iocsrrd_w, i64, env, tl)
|
||||
DEF_HELPER_2(iocsrrd_d, i64, env, tl)
|
||||
DEF_HELPER_3(iocsrwr_b, void, env, tl, tl)
|
||||
DEF_HELPER_3(iocsrwr_h, void, env, tl, tl)
|
||||
DEF_HELPER_3(iocsrwr_w, void, env, tl, tl)
|
||||
DEF_HELPER_3(iocsrwr_d, void, env, tl, tl)
|
||||
|
||||
/* TLB helper */
|
||||
DEF_HELPER_1(tlbwr, void, env)
|
||||
DEF_HELPER_1(tlbfill, void, env)
|
||||
DEF_HELPER_1(tlbsrch, void, env)
|
||||
DEF_HELPER_1(tlbrd, void, env)
|
||||
DEF_HELPER_1(tlbclr, void, env)
|
||||
DEF_HELPER_1(tlbflush, void, env)
|
||||
DEF_HELPER_1(invtlb_all, void, env)
|
||||
DEF_HELPER_2(invtlb_all_g, void, env, i32)
|
||||
DEF_HELPER_2(invtlb_all_asid, void, env, tl)
|
||||
DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl)
|
||||
DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl)
|
||||
|
||||
DEF_HELPER_4(lddir, tl, env, tl, tl, i32)
|
||||
DEF_HELPER_4(ldpte, void, env, tl, tl, i32)
|
||||
DEF_HELPER_1(ertn, void, env)
|
||||
DEF_HELPER_1(idle, void, env)
|
|
@ -0,0 +1,304 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static bool gen_rrr(DisasContext *ctx, arg_rrr *a,
|
||||
DisasExtend src1_ext, DisasExtend src2_ext,
|
||||
DisasExtend dst_ext, void (*func)(TCGv, TCGv, TCGv))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, dst_ext);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, src1_ext);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, src2_ext);
|
||||
|
||||
func(dest, src1, src2);
|
||||
gen_set_gpr(a->rd, dest, dst_ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_rri_v(DisasContext *ctx, arg_rr_i *a,
|
||||
DisasExtend src_ext, DisasExtend dst_ext,
|
||||
void (*func)(TCGv, TCGv, TCGv))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, dst_ext);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, src_ext);
|
||||
TCGv src2 = tcg_constant_tl(a->imm);
|
||||
|
||||
func(dest, src1, src2);
|
||||
gen_set_gpr(a->rd, dest, dst_ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_rri_c(DisasContext *ctx, arg_rr_i *a,
|
||||
DisasExtend src_ext, DisasExtend dst_ext,
|
||||
void (*func)(TCGv, TCGv, target_long))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, dst_ext);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, src_ext);
|
||||
|
||||
func(dest, src1, a->imm);
|
||||
gen_set_gpr(a->rd, dest, dst_ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_rrr_sa(DisasContext *ctx, arg_rrr_sa *a,
|
||||
DisasExtend src_ext, DisasExtend dst_ext,
|
||||
void (*func)(TCGv, TCGv, TCGv, target_long))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, dst_ext);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, src_ext);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, src_ext);
|
||||
|
||||
func(dest, src1, src2, a->sa);
|
||||
gen_set_gpr(a->rd, dest, dst_ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_lu12i_w(DisasContext *ctx, arg_lu12i_w *a)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
|
||||
tcg_gen_movi_tl(dest, a->imm << 12);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_pc(DisasContext *ctx, arg_r_i *a,
|
||||
target_ulong (*func)(target_ulong, int))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
target_ulong addr = func(ctx->base.pc_next, a->imm);
|
||||
|
||||
tcg_gen_movi_tl(dest, addr);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gen_slt(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
tcg_gen_setcond_tl(TCG_COND_LT, dest, src1, src2);
|
||||
}
|
||||
|
||||
static void gen_sltu(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
tcg_gen_setcond_tl(TCG_COND_LTU, dest, src1, src2);
|
||||
}
|
||||
|
||||
static void gen_mulh_w(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
tcg_gen_mul_i64(dest, src1, src2);
|
||||
tcg_gen_sari_i64(dest, dest, 32);
|
||||
}
|
||||
|
||||
static void gen_mulh_d(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv discard = tcg_temp_new();
|
||||
tcg_gen_muls2_tl(discard, dest, src1, src2);
|
||||
tcg_temp_free(discard);
|
||||
}
|
||||
|
||||
static void gen_mulh_du(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv discard = tcg_temp_new();
|
||||
tcg_gen_mulu2_tl(discard, dest, src1, src2);
|
||||
tcg_temp_free(discard);
|
||||
}
|
||||
|
||||
static void prep_divisor_d(TCGv ret, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv t1 = tcg_temp_new();
|
||||
TCGv zero = tcg_constant_tl(0);
|
||||
|
||||
/*
|
||||
* If min / -1, set the divisor to 1.
|
||||
* This avoids potential host overflow trap and produces min.
|
||||
* If x / 0, set the divisor to 1.
|
||||
* This avoids potential host overflow trap;
|
||||
* the required result is undefined.
|
||||
*/
|
||||
tcg_gen_setcondi_tl(TCG_COND_EQ, ret, src1, INT64_MIN);
|
||||
tcg_gen_setcondi_tl(TCG_COND_EQ, t0, src2, -1);
|
||||
tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src2, 0);
|
||||
tcg_gen_and_tl(ret, ret, t0);
|
||||
tcg_gen_or_tl(ret, ret, t1);
|
||||
tcg_gen_movcond_tl(TCG_COND_NE, ret, ret, zero, ret, src2);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
}
|
||||
|
||||
static void prep_divisor_du(TCGv ret, TCGv src2)
|
||||
{
|
||||
TCGv zero = tcg_constant_tl(0);
|
||||
TCGv one = tcg_constant_tl(1);
|
||||
|
||||
/*
|
||||
* If x / 0, set the divisor to 1.
|
||||
* This avoids potential host overflow trap;
|
||||
* the required result is undefined.
|
||||
*/
|
||||
tcg_gen_movcond_tl(TCG_COND_EQ, ret, src2, zero, one, src2);
|
||||
}
|
||||
|
||||
static void gen_div_d(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
prep_divisor_d(t0, src1, src2);
|
||||
tcg_gen_div_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_rem_d(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
prep_divisor_d(t0, src1, src2);
|
||||
tcg_gen_rem_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_div_du(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
prep_divisor_du(t0, src2);
|
||||
tcg_gen_divu_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_rem_du(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
prep_divisor_du(t0, src2);
|
||||
tcg_gen_remu_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_div_w(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
/* We need not check for integer overflow for div_w. */
|
||||
prep_divisor_du(t0, src2);
|
||||
tcg_gen_div_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_rem_w(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
/* We need not check for integer overflow for rem_w. */
|
||||
prep_divisor_du(t0, src2);
|
||||
tcg_gen_rem_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_alsl(TCGv dest, TCGv src1, TCGv src2, target_long sa)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_shli_tl(t0, src1, sa);
|
||||
tcg_gen_add_tl(dest, t0, src2);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static bool trans_lu32i_d(DisasContext *ctx, arg_lu32i_d *a)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv src2 = tcg_constant_tl(a->imm);
|
||||
|
||||
tcg_gen_deposit_tl(dest, src1, src2, 32, 32);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_lu52i_d(DisasContext *ctx, arg_lu52i_d *a)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = tcg_constant_tl(a->imm);
|
||||
|
||||
tcg_gen_deposit_tl(dest, src1, src2, 52, 12);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static target_ulong gen_pcaddi(target_ulong pc, int imm)
|
||||
{
|
||||
return pc + (imm << 2);
|
||||
}
|
||||
|
||||
static target_ulong gen_pcalau12i(target_ulong pc, int imm)
|
||||
{
|
||||
return (pc + (imm << 12)) & ~0xfff;
|
||||
}
|
||||
|
||||
static target_ulong gen_pcaddu12i(target_ulong pc, int imm)
|
||||
{
|
||||
return pc + (imm << 12);
|
||||
}
|
||||
|
||||
static target_ulong gen_pcaddu18i(target_ulong pc, int imm)
|
||||
{
|
||||
return pc + ((target_ulong)(imm) << 18);
|
||||
}
|
||||
|
||||
static bool trans_addu16i_d(DisasContext *ctx, arg_addu16i_d *a)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
tcg_gen_addi_tl(dest, src1, a->imm << 16);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(add_w, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_add_tl)
|
||||
TRANS(add_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_add_tl)
|
||||
TRANS(sub_w, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_sub_tl)
|
||||
TRANS(sub_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_sub_tl)
|
||||
TRANS(and, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_and_tl)
|
||||
TRANS(or, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_or_tl)
|
||||
TRANS(xor, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_xor_tl)
|
||||
TRANS(nor, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_nor_tl)
|
||||
TRANS(andn, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_andc_tl)
|
||||
TRANS(orn, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_orc_tl)
|
||||
TRANS(slt, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_slt)
|
||||
TRANS(sltu, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sltu)
|
||||
TRANS(mul_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, tcg_gen_mul_tl)
|
||||
TRANS(mul_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_mul_tl)
|
||||
TRANS(mulh_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, gen_mulh_w)
|
||||
TRANS(mulh_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, gen_mulh_w)
|
||||
TRANS(mulh_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_d)
|
||||
TRANS(mulh_du, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_du)
|
||||
TRANS(mulw_d_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, tcg_gen_mul_tl)
|
||||
TRANS(mulw_d_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, tcg_gen_mul_tl)
|
||||
TRANS(div_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_div_w)
|
||||
TRANS(mod_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_rem_w)
|
||||
TRANS(div_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_div_du)
|
||||
TRANS(mod_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_rem_du)
|
||||
TRANS(div_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_d)
|
||||
TRANS(mod_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_d)
|
||||
TRANS(div_du, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_du)
|
||||
TRANS(mod_du, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_du)
|
||||
TRANS(slti, gen_rri_v, EXT_NONE, EXT_NONE, gen_slt)
|
||||
TRANS(sltui, gen_rri_v, EXT_NONE, EXT_NONE, gen_sltu)
|
||||
TRANS(addi_w, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_addi_tl)
|
||||
TRANS(addi_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_addi_tl)
|
||||
TRANS(alsl_w, gen_rrr_sa, EXT_NONE, EXT_SIGN, gen_alsl)
|
||||
TRANS(alsl_wu, gen_rrr_sa, EXT_NONE, EXT_ZERO, gen_alsl)
|
||||
TRANS(alsl_d, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_alsl)
|
||||
TRANS(pcaddi, gen_pc, gen_pcaddi)
|
||||
TRANS(pcalau12i, gen_pc, gen_pcalau12i)
|
||||
TRANS(pcaddu12i, gen_pc, gen_pcaddu12i)
|
||||
TRANS(pcaddu18i, gen_pc, gen_pcaddu18i)
|
||||
TRANS(andi, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_andi_tl)
|
||||
TRANS(ori, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_ori_tl)
|
||||
TRANS(xori, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_xori_tl)
|
|
@ -0,0 +1,113 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static bool gen_ll(DisasContext *ctx, arg_rr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
||||
tcg_gen_addi_tl(t0, src1, a->imm);
|
||||
tcg_gen_qemu_ld_i64(dest, t0, ctx->mem_idx, mop);
|
||||
tcg_gen_st_tl(t0, cpu_env, offsetof(CPULoongArchState, lladdr));
|
||||
tcg_gen_st_tl(dest, cpu_env, offsetof(CPULoongArchState, llval));
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_sc(DisasContext *ctx, arg_rr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv val = tcg_temp_new();
|
||||
|
||||
TCGLabel *l1 = gen_new_label();
|
||||
TCGLabel *done = gen_new_label();
|
||||
|
||||
tcg_gen_addi_tl(t0, src1, a->imm);
|
||||
tcg_gen_brcond_tl(TCG_COND_EQ, t0, cpu_lladdr, l1);
|
||||
tcg_gen_movi_tl(dest, 0);
|
||||
tcg_gen_br(done);
|
||||
|
||||
gen_set_label(l1);
|
||||
tcg_gen_mov_tl(val, src2);
|
||||
/* generate cmpxchg */
|
||||
tcg_gen_atomic_cmpxchg_tl(t0, cpu_lladdr, cpu_llval,
|
||||
val, ctx->mem_idx, mop);
|
||||
tcg_gen_setcond_tl(TCG_COND_EQ, dest, t0, cpu_llval);
|
||||
gen_set_label(done);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(val);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_am(DisasContext *ctx, arg_rrr *a,
|
||||
void (*func)(TCGv, TCGv, TCGv, TCGArg, MemOp),
|
||||
MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv val = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
if (a->rd != 0 && (a->rj == a->rd || a->rk == a->rd)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Warning: source register overlaps destination register"
|
||||
"in atomic insn at pc=0x" TARGET_FMT_lx "\n",
|
||||
ctx->base.pc_next - 4);
|
||||
return false;
|
||||
}
|
||||
|
||||
func(dest, addr, val, ctx->mem_idx, mop);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(ll_w, gen_ll, MO_TESL)
|
||||
TRANS(sc_w, gen_sc, MO_TESL)
|
||||
TRANS(ll_d, gen_ll, MO_TEUQ)
|
||||
TRANS(sc_d, gen_sc, MO_TEUQ)
|
||||
TRANS(amswap_w, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL)
|
||||
TRANS(amswap_d, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ)
|
||||
TRANS(amadd_w, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL)
|
||||
TRANS(amadd_d, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ)
|
||||
TRANS(amand_w, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL)
|
||||
TRANS(amand_d, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ)
|
||||
TRANS(amor_w, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL)
|
||||
TRANS(amor_d, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ)
|
||||
TRANS(amxor_w, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL)
|
||||
TRANS(amxor_d, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ)
|
||||
TRANS(ammax_w, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL)
|
||||
TRANS(ammax_d, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ)
|
||||
TRANS(ammin_w, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL)
|
||||
TRANS(ammin_d, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ)
|
||||
TRANS(ammax_wu, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL)
|
||||
TRANS(ammax_du, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ)
|
||||
TRANS(ammin_wu, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL)
|
||||
TRANS(ammin_du, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ)
|
||||
TRANS(amswap_db_w, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL)
|
||||
TRANS(amswap_db_d, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ)
|
||||
TRANS(amadd_db_w, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL)
|
||||
TRANS(amadd_db_d, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ)
|
||||
TRANS(amand_db_w, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL)
|
||||
TRANS(amand_db_d, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ)
|
||||
TRANS(amor_db_w, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL)
|
||||
TRANS(amor_db_d, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ)
|
||||
TRANS(amxor_db_w, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL)
|
||||
TRANS(amxor_db_d, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ)
|
||||
TRANS(ammax_db_w, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL)
|
||||
TRANS(ammax_db_d, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ)
|
||||
TRANS(ammin_db_w, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL)
|
||||
TRANS(ammin_db_d, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ)
|
||||
TRANS(ammax_db_wu, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL)
|
||||
TRANS(ammax_db_du, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ)
|
||||
TRANS(ammin_db_wu, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL)
|
||||
TRANS(ammin_db_du, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ)
|
|
@ -0,0 +1,212 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static bool gen_rr(DisasContext *ctx, arg_rr *a,
|
||||
DisasExtend src_ext, DisasExtend dst_ext,
|
||||
void (*func)(TCGv, TCGv))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, dst_ext);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, src_ext);
|
||||
|
||||
func(dest, src1);
|
||||
gen_set_gpr(a->rd, dest, dst_ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gen_bytepick_w(TCGv dest, TCGv src1, TCGv src2, target_long sa)
|
||||
{
|
||||
tcg_gen_concat_tl_i64(dest, src1, src2);
|
||||
tcg_gen_sextract_i64(dest, dest, (32 - sa * 8), 32);
|
||||
}
|
||||
|
||||
static void gen_bytepick_d(TCGv dest, TCGv src1, TCGv src2, target_long sa)
|
||||
{
|
||||
tcg_gen_extract2_i64(dest, src1, src2, (64 - sa * 8));
|
||||
}
|
||||
|
||||
static void gen_bstrins(TCGv dest, TCGv src1,
|
||||
unsigned int ls, unsigned int len)
|
||||
{
|
||||
tcg_gen_deposit_tl(dest, dest, src1, ls, len);
|
||||
}
|
||||
|
||||
static bool gen_rr_ms_ls(DisasContext *ctx, arg_rr_ms_ls *a,
|
||||
DisasExtend src_ext, DisasExtend dst_ext,
|
||||
void (*func)(TCGv, TCGv, unsigned int, unsigned int))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, dst_ext);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, src_ext);
|
||||
|
||||
if (a->ls > a->ms) {
|
||||
return false;
|
||||
}
|
||||
|
||||
func(dest, src1, a->ls, a->ms - a->ls + 1);
|
||||
gen_set_gpr(a->rd, dest, dst_ext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gen_clz_w(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_clzi_tl(dest, src1, TARGET_LONG_BITS);
|
||||
tcg_gen_subi_tl(dest, dest, TARGET_LONG_BITS - 32);
|
||||
}
|
||||
|
||||
static void gen_clo_w(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_not_tl(dest, src1);
|
||||
tcg_gen_ext32u_tl(dest, dest);
|
||||
gen_clz_w(dest, dest);
|
||||
}
|
||||
|
||||
static void gen_ctz_w(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_ori_tl(dest, src1, (target_ulong)MAKE_64BIT_MASK(32, 32));
|
||||
tcg_gen_ctzi_tl(dest, dest, TARGET_LONG_BITS);
|
||||
}
|
||||
|
||||
static void gen_cto_w(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_not_tl(dest, src1);
|
||||
gen_ctz_w(dest, dest);
|
||||
}
|
||||
|
||||
static void gen_clz_d(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_clzi_i64(dest, src1, TARGET_LONG_BITS);
|
||||
}
|
||||
|
||||
static void gen_clo_d(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_not_tl(dest, src1);
|
||||
gen_clz_d(dest, dest);
|
||||
}
|
||||
|
||||
static void gen_ctz_d(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_ctzi_tl(dest, src1, TARGET_LONG_BITS);
|
||||
}
|
||||
|
||||
static void gen_cto_d(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_not_tl(dest, src1);
|
||||
gen_ctz_d(dest, dest);
|
||||
}
|
||||
|
||||
static void gen_revb_2w(TCGv dest, TCGv src1)
|
||||
{
|
||||
tcg_gen_bswap64_i64(dest, src1);
|
||||
tcg_gen_rotri_i64(dest, dest, 32);
|
||||
}
|
||||
|
||||
static void gen_revb_2h(TCGv dest, TCGv src1)
|
||||
{
|
||||
TCGv mask = tcg_constant_tl(0x00FF00FF);
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv t1 = tcg_temp_new();
|
||||
|
||||
tcg_gen_shri_tl(t0, src1, 8);
|
||||
tcg_gen_and_tl(t0, t0, mask);
|
||||
tcg_gen_and_tl(t1, src1, mask);
|
||||
tcg_gen_shli_tl(t1, t1, 8);
|
||||
tcg_gen_or_tl(dest, t0, t1);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
}
|
||||
|
||||
static void gen_revb_4h(TCGv dest, TCGv src1)
|
||||
{
|
||||
TCGv mask = tcg_constant_tl(0x00FF00FF00FF00FFULL);
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv t1 = tcg_temp_new();
|
||||
|
||||
tcg_gen_shri_tl(t0, src1, 8);
|
||||
tcg_gen_and_tl(t0, t0, mask);
|
||||
tcg_gen_and_tl(t1, src1, mask);
|
||||
tcg_gen_shli_tl(t1, t1, 8);
|
||||
tcg_gen_or_tl(dest, t0, t1);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
}
|
||||
|
||||
static void gen_revh_2w(TCGv dest, TCGv src1)
|
||||
{
|
||||
TCGv_i64 t0 = tcg_temp_new_i64();
|
||||
TCGv_i64 t1 = tcg_temp_new_i64();
|
||||
TCGv_i64 mask = tcg_constant_i64(0x0000ffff0000ffffull);
|
||||
|
||||
tcg_gen_shri_i64(t0, src1, 16);
|
||||
tcg_gen_and_i64(t1, src1, mask);
|
||||
tcg_gen_and_i64(t0, t0, mask);
|
||||
tcg_gen_shli_i64(t1, t1, 16);
|
||||
tcg_gen_or_i64(dest, t1, t0);
|
||||
|
||||
tcg_temp_free_i64(t0);
|
||||
tcg_temp_free_i64(t1);
|
||||
}
|
||||
|
||||
static void gen_revh_d(TCGv dest, TCGv src1)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv t1 = tcg_temp_new();
|
||||
TCGv mask = tcg_constant_tl(0x0000FFFF0000FFFFULL);
|
||||
|
||||
tcg_gen_shri_tl(t1, src1, 16);
|
||||
tcg_gen_and_tl(t1, t1, mask);
|
||||
tcg_gen_and_tl(t0, src1, mask);
|
||||
tcg_gen_shli_tl(t0, t0, 16);
|
||||
tcg_gen_or_tl(t0, t0, t1);
|
||||
tcg_gen_rotri_tl(dest, t0, 32);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
}
|
||||
|
||||
static void gen_maskeqz(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv zero = tcg_constant_tl(0);
|
||||
|
||||
tcg_gen_movcond_tl(TCG_COND_EQ, dest, src2, zero, zero, src1);
|
||||
}
|
||||
|
||||
static void gen_masknez(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv zero = tcg_constant_tl(0);
|
||||
|
||||
tcg_gen_movcond_tl(TCG_COND_NE, dest, src2, zero, zero, src1);
|
||||
}
|
||||
|
||||
TRANS(ext_w_h, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext16s_tl)
|
||||
TRANS(ext_w_b, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext8s_tl)
|
||||
TRANS(clo_w, gen_rr, EXT_NONE, EXT_NONE, gen_clo_w)
|
||||
TRANS(clz_w, gen_rr, EXT_ZERO, EXT_NONE, gen_clz_w)
|
||||
TRANS(cto_w, gen_rr, EXT_NONE, EXT_NONE, gen_cto_w)
|
||||
TRANS(ctz_w, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_w)
|
||||
TRANS(clo_d, gen_rr, EXT_NONE, EXT_NONE, gen_clo_d)
|
||||
TRANS(clz_d, gen_rr, EXT_NONE, EXT_NONE, gen_clz_d)
|
||||
TRANS(cto_d, gen_rr, EXT_NONE, EXT_NONE, gen_cto_d)
|
||||
TRANS(ctz_d, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_d)
|
||||
TRANS(revb_2h, gen_rr, EXT_NONE, EXT_SIGN, gen_revb_2h)
|
||||
TRANS(revb_4h, gen_rr, EXT_NONE, EXT_NONE, gen_revb_4h)
|
||||
TRANS(revb_2w, gen_rr, EXT_NONE, EXT_NONE, gen_revb_2w)
|
||||
TRANS(revb_d, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_bswap64_i64)
|
||||
TRANS(revh_2w, gen_rr, EXT_NONE, EXT_NONE, gen_revh_2w)
|
||||
TRANS(revh_d, gen_rr, EXT_NONE, EXT_NONE, gen_revh_d)
|
||||
TRANS(bitrev_4b, gen_rr, EXT_ZERO, EXT_SIGN, gen_helper_bitswap)
|
||||
TRANS(bitrev_8b, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitswap)
|
||||
TRANS(bitrev_w, gen_rr, EXT_NONE, EXT_SIGN, gen_helper_bitrev_w)
|
||||
TRANS(bitrev_d, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitrev_d)
|
||||
TRANS(maskeqz, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_maskeqz)
|
||||
TRANS(masknez, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_masknez)
|
||||
TRANS(bytepick_w, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_w)
|
||||
TRANS(bytepick_d, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_d)
|
||||
TRANS(bstrins_w, gen_rr_ms_ls, EXT_NONE, EXT_NONE, gen_bstrins)
|
||||
TRANS(bstrins_d, gen_rr_ms_ls, EXT_NONE, EXT_NONE, gen_bstrins)
|
||||
TRANS(bstrpick_w, gen_rr_ms_ls, EXT_NONE, EXT_SIGN, tcg_gen_extract_tl)
|
||||
TRANS(bstrpick_d, gen_rr_ms_ls, EXT_NONE, EXT_NONE, tcg_gen_extract_tl)
|
|
@ -0,0 +1,83 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static bool trans_b(DisasContext *ctx, arg_b *a)
|
||||
{
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs);
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_bl(DisasContext *ctx, arg_bl *a)
|
||||
{
|
||||
tcg_gen_movi_tl(cpu_gpr[1], ctx->base.pc_next + 4);
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs);
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_jirl(DisasContext *ctx, arg_jirl *a)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
tcg_gen_addi_tl(cpu_pc, src1, a->offs);
|
||||
tcg_gen_movi_tl(dest, ctx->base.pc_next + 4);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gen_bc(DisasContext *ctx, TCGv src1, TCGv src2,
|
||||
target_long offs, TCGCond cond)
|
||||
{
|
||||
TCGLabel *l = gen_new_label();
|
||||
tcg_gen_brcond_tl(cond, src1, src2, l);
|
||||
gen_goto_tb(ctx, 1, ctx->base.pc_next + 4);
|
||||
gen_set_label(l);
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next + offs);
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
}
|
||||
|
||||
static bool gen_rr_bc(DisasContext *ctx, arg_rr_offs *a, TCGCond cond)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
|
||||
gen_bc(ctx, src1, src2, a->offs, cond);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_rz_bc(DisasContext *ctx, arg_r_offs *a, TCGCond cond)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = tcg_constant_tl(0);
|
||||
|
||||
gen_bc(ctx, src1, src2, a->offs, cond);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond)
|
||||
{
|
||||
TCGv src1 = tcg_temp_new();
|
||||
TCGv src2 = tcg_constant_tl(0);
|
||||
|
||||
tcg_gen_ld8u_tl(src1, cpu_env,
|
||||
offsetof(CPULoongArchState, cf[a->cj]));
|
||||
gen_bc(ctx, src1, src2, a->offs, cond);
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(beq, gen_rr_bc, TCG_COND_EQ)
|
||||
TRANS(bne, gen_rr_bc, TCG_COND_NE)
|
||||
TRANS(blt, gen_rr_bc, TCG_COND_LT)
|
||||
TRANS(bge, gen_rr_bc, TCG_COND_GE)
|
||||
TRANS(bltu, gen_rr_bc, TCG_COND_LTU)
|
||||
TRANS(bgeu, gen_rr_bc, TCG_COND_GEU)
|
||||
TRANS(beqz, gen_rz_bc, TCG_COND_EQ)
|
||||
TRANS(bnez, gen_rz_bc, TCG_COND_NE)
|
||||
TRANS(bceqz, gen_cz_bc, TCG_COND_EQ)
|
||||
TRANS(bcnez, gen_cz_bc, TCG_COND_NE)
|
|
@ -0,0 +1,101 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static bool trans_break(DisasContext *ctx, arg_break *a)
|
||||
{
|
||||
generate_exception(ctx, EXCCODE_BRK);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_syscall(DisasContext *ctx, arg_syscall *a)
|
||||
{
|
||||
generate_exception(ctx, EXCCODE_SYS);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_asrtle_d(DisasContext *ctx, arg_asrtle_d * a)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
gen_helper_asrtle_d(cpu_env, src1, src2);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_asrtgt_d(DisasContext *ctx, arg_asrtgt_d * a)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
gen_helper_asrtgt_d(cpu_env, src1, src2);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_rdtime(DisasContext *ctx, arg_rr *a,
|
||||
bool word, bool high)
|
||||
{
|
||||
TCGv dst1 = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv dst2 = gpr_dst(ctx, a->rj, EXT_NONE);
|
||||
|
||||
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||
gen_io_start();
|
||||
}
|
||||
gen_helper_rdtime_d(dst1, cpu_env);
|
||||
if (word) {
|
||||
tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32);
|
||||
}
|
||||
tcg_gen_ld_i64(dst2, cpu_env, offsetof(CPULoongArchState, CSR_TID));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_rdtimel_w(DisasContext *ctx, arg_rdtimel_w *a)
|
||||
{
|
||||
return gen_rdtime(ctx, a, 1, 0);
|
||||
}
|
||||
|
||||
static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a)
|
||||
{
|
||||
return gen_rdtime(ctx, a, 1, 1);
|
||||
}
|
||||
|
||||
static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a)
|
||||
{
|
||||
return gen_rdtime(ctx, a, 0, 0);
|
||||
}
|
||||
|
||||
static bool trans_cpucfg(DisasContext *ctx, arg_cpucfg *a)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
gen_helper_cpucfg(dest, cpu_env, src1);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_crc(DisasContext *ctx, arg_rrr *a,
|
||||
void (*func)(TCGv, TCGv, TCGv, TCGv),
|
||||
TCGv tsz)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_SIGN);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
func(dest, src2, src1, tsz);
|
||||
gen_set_gpr(a->rd, dest, EXT_SIGN);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(crc_w_b_w, gen_crc, gen_helper_crc32, tcg_constant_tl(1))
|
||||
TRANS(crc_w_h_w, gen_crc, gen_helper_crc32, tcg_constant_tl(2))
|
||||
TRANS(crc_w_w_w, gen_crc, gen_helper_crc32, tcg_constant_tl(4))
|
||||
TRANS(crc_w_d_w, gen_crc, gen_helper_crc32, tcg_constant_tl(8))
|
||||
TRANS(crcc_w_b_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(1))
|
||||
TRANS(crcc_w_h_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(2))
|
||||
TRANS(crcc_w_w_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(4))
|
||||
TRANS(crcc_w_d_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(8))
|
|
@ -0,0 +1,105 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static bool gen_fff(DisasContext *ctx, arg_fff *a,
|
||||
void (*func)(TCGv, TCGv_env, TCGv, TCGv))
|
||||
{
|
||||
func(cpu_fpr[a->fd], cpu_env, cpu_fpr[a->fj], cpu_fpr[a->fk]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_ff(DisasContext *ctx, arg_ff *a,
|
||||
void (*func)(TCGv, TCGv_env, TCGv))
|
||||
{
|
||||
func(cpu_fpr[a->fd], cpu_env, cpu_fpr[a->fj]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_muladd(DisasContext *ctx, arg_ffff *a,
|
||||
void (*func)(TCGv, TCGv_env, TCGv, TCGv, TCGv, TCGv_i32),
|
||||
int flag)
|
||||
{
|
||||
TCGv_i32 tflag = tcg_constant_i32(flag);
|
||||
func(cpu_fpr[a->fd], cpu_env, cpu_fpr[a->fj],
|
||||
cpu_fpr[a->fk], cpu_fpr[a->fa], tflag);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fcopysign_s(DisasContext *ctx, arg_fcopysign_s *a)
|
||||
{
|
||||
tcg_gen_deposit_i64(cpu_fpr[a->fd], cpu_fpr[a->fk], cpu_fpr[a->fj], 0, 31);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fcopysign_d(DisasContext *ctx, arg_fcopysign_d *a)
|
||||
{
|
||||
tcg_gen_deposit_i64(cpu_fpr[a->fd], cpu_fpr[a->fk], cpu_fpr[a->fj], 0, 63);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fabs_s(DisasContext *ctx, arg_fabs_s *a)
|
||||
{
|
||||
tcg_gen_andi_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], MAKE_64BIT_MASK(0, 31));
|
||||
gen_nanbox_s(cpu_fpr[a->fd], cpu_fpr[a->fd]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fabs_d(DisasContext *ctx, arg_fabs_d *a)
|
||||
{
|
||||
tcg_gen_andi_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], MAKE_64BIT_MASK(0, 63));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fneg_s(DisasContext *ctx, arg_fneg_s *a)
|
||||
{
|
||||
tcg_gen_xori_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], 0x80000000);
|
||||
gen_nanbox_s(cpu_fpr[a->fd], cpu_fpr[a->fd]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fneg_d(DisasContext *ctx, arg_fneg_d *a)
|
||||
{
|
||||
tcg_gen_xori_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], 0x8000000000000000LL);
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(fadd_s, gen_fff, gen_helper_fadd_s)
|
||||
TRANS(fadd_d, gen_fff, gen_helper_fadd_d)
|
||||
TRANS(fsub_s, gen_fff, gen_helper_fsub_s)
|
||||
TRANS(fsub_d, gen_fff, gen_helper_fsub_d)
|
||||
TRANS(fmul_s, gen_fff, gen_helper_fmul_s)
|
||||
TRANS(fmul_d, gen_fff, gen_helper_fmul_d)
|
||||
TRANS(fdiv_s, gen_fff, gen_helper_fdiv_s)
|
||||
TRANS(fdiv_d, gen_fff, gen_helper_fdiv_d)
|
||||
TRANS(fmax_s, gen_fff, gen_helper_fmax_s)
|
||||
TRANS(fmax_d, gen_fff, gen_helper_fmax_d)
|
||||
TRANS(fmin_s, gen_fff, gen_helper_fmin_s)
|
||||
TRANS(fmin_d, gen_fff, gen_helper_fmin_d)
|
||||
TRANS(fmaxa_s, gen_fff, gen_helper_fmaxa_s)
|
||||
TRANS(fmaxa_d, gen_fff, gen_helper_fmaxa_d)
|
||||
TRANS(fmina_s, gen_fff, gen_helper_fmina_s)
|
||||
TRANS(fmina_d, gen_fff, gen_helper_fmina_d)
|
||||
TRANS(fscaleb_s, gen_fff, gen_helper_fscaleb_s)
|
||||
TRANS(fscaleb_d, gen_fff, gen_helper_fscaleb_d)
|
||||
TRANS(fsqrt_s, gen_ff, gen_helper_fsqrt_s)
|
||||
TRANS(fsqrt_d, gen_ff, gen_helper_fsqrt_d)
|
||||
TRANS(frecip_s, gen_ff, gen_helper_frecip_s)
|
||||
TRANS(frecip_d, gen_ff, gen_helper_frecip_d)
|
||||
TRANS(frsqrt_s, gen_ff, gen_helper_frsqrt_s)
|
||||
TRANS(frsqrt_d, gen_ff, gen_helper_frsqrt_d)
|
||||
TRANS(flogb_s, gen_ff, gen_helper_flogb_s)
|
||||
TRANS(flogb_d, gen_ff, gen_helper_flogb_d)
|
||||
TRANS(fclass_s, gen_ff, gen_helper_fclass_s)
|
||||
TRANS(fclass_d, gen_ff, gen_helper_fclass_d)
|
||||
TRANS(fmadd_s, gen_muladd, gen_helper_fmuladd_s, 0)
|
||||
TRANS(fmadd_d, gen_muladd, gen_helper_fmuladd_d, 0)
|
||||
TRANS(fmsub_s, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_c)
|
||||
TRANS(fmsub_d, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_c)
|
||||
TRANS(fnmadd_s, gen_muladd, gen_helper_fmuladd_s,
|
||||
float_muladd_negate_product | float_muladd_negate_c)
|
||||
TRANS(fnmadd_d, gen_muladd, gen_helper_fmuladd_d,
|
||||
float_muladd_negate_product | float_muladd_negate_c)
|
||||
TRANS(fnmsub_s, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_product)
|
||||
TRANS(fnmsub_d, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_product)
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
/* bit0(signaling/quiet) bit1(lt) bit2(eq) bit3(un) bit4(neq) */
|
||||
static uint32_t get_fcmp_flags(int cond)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (cond & 0x1) {
|
||||
flags |= FCMP_LT;
|
||||
}
|
||||
if (cond & 0x2) {
|
||||
flags |= FCMP_EQ;
|
||||
}
|
||||
if (cond & 0x4) {
|
||||
flags |= FCMP_UN;
|
||||
}
|
||||
if (cond & 0x8) {
|
||||
flags |= FCMP_GT | FCMP_LT;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a)
|
||||
{
|
||||
TCGv var = tcg_temp_new();
|
||||
uint32_t flags;
|
||||
void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32);
|
||||
|
||||
fn = (a->fcond & 1 ? gen_helper_fcmp_s_s : gen_helper_fcmp_c_s);
|
||||
flags = get_fcmp_flags(a->fcond >> 1);
|
||||
|
||||
fn(var, cpu_env, cpu_fpr[a->fj], cpu_fpr[a->fk], tcg_constant_i32(flags));
|
||||
|
||||
tcg_gen_st8_tl(var, cpu_env, offsetof(CPULoongArchState, cf[a->cd]));
|
||||
tcg_temp_free(var);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fcmp_cond_d(DisasContext *ctx, arg_fcmp_cond_d *a)
|
||||
{
|
||||
TCGv var = tcg_temp_new();
|
||||
uint32_t flags;
|
||||
void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32);
|
||||
fn = (a->fcond & 1 ? gen_helper_fcmp_s_d : gen_helper_fcmp_c_d);
|
||||
flags = get_fcmp_flags(a->fcond >> 1);
|
||||
|
||||
fn(var, cpu_env, cpu_fpr[a->fj], cpu_fpr[a->fk], tcg_constant_i32(flags));
|
||||
|
||||
tcg_gen_st8_tl(var, cpu_env, offsetof(CPULoongArchState, cf[a->cd]));
|
||||
|
||||
tcg_temp_free(var);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
TRANS(fcvt_s_d, gen_ff, gen_helper_fcvt_s_d)
|
||||
TRANS(fcvt_d_s, gen_ff, gen_helper_fcvt_d_s)
|
||||
TRANS(ftintrm_w_s, gen_ff, gen_helper_ftintrm_w_s)
|
||||
TRANS(ftintrm_w_d, gen_ff, gen_helper_ftintrm_w_d)
|
||||
TRANS(ftintrm_l_s, gen_ff, gen_helper_ftintrm_l_s)
|
||||
TRANS(ftintrm_l_d, gen_ff, gen_helper_ftintrm_l_d)
|
||||
TRANS(ftintrp_w_s, gen_ff, gen_helper_ftintrp_w_s)
|
||||
TRANS(ftintrp_w_d, gen_ff, gen_helper_ftintrp_w_d)
|
||||
TRANS(ftintrp_l_s, gen_ff, gen_helper_ftintrp_l_s)
|
||||
TRANS(ftintrp_l_d, gen_ff, gen_helper_ftintrp_l_d)
|
||||
TRANS(ftintrz_w_s, gen_ff, gen_helper_ftintrz_w_s)
|
||||
TRANS(ftintrz_w_d, gen_ff, gen_helper_ftintrz_w_d)
|
||||
TRANS(ftintrz_l_s, gen_ff, gen_helper_ftintrz_l_s)
|
||||
TRANS(ftintrz_l_d, gen_ff, gen_helper_ftintrz_l_d)
|
||||
TRANS(ftintrne_w_s, gen_ff, gen_helper_ftintrne_w_s)
|
||||
TRANS(ftintrne_w_d, gen_ff, gen_helper_ftintrne_w_d)
|
||||
TRANS(ftintrne_l_s, gen_ff, gen_helper_ftintrne_l_s)
|
||||
TRANS(ftintrne_l_d, gen_ff, gen_helper_ftintrne_l_d)
|
||||
TRANS(ftint_w_s, gen_ff, gen_helper_ftint_w_s)
|
||||
TRANS(ftint_w_d, gen_ff, gen_helper_ftint_w_d)
|
||||
TRANS(ftint_l_s, gen_ff, gen_helper_ftint_l_s)
|
||||
TRANS(ftint_l_d, gen_ff, gen_helper_ftint_l_d)
|
||||
TRANS(ffint_s_w, gen_ff, gen_helper_ffint_s_w)
|
||||
TRANS(ffint_s_l, gen_ff, gen_helper_ffint_s_l)
|
||||
TRANS(ffint_d_w, gen_ff, gen_helper_ffint_d_w)
|
||||
TRANS(ffint_d_l, gen_ff, gen_helper_ffint_d_l)
|
||||
TRANS(frint_s, gen_ff, gen_helper_frint_s)
|
||||
TRANS(frint_d, gen_ff, gen_helper_frint_d)
|
|
@ -0,0 +1,153 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static void maybe_nanbox_load(TCGv freg, MemOp mop)
|
||||
{
|
||||
if ((mop & MO_SIZE) == MO_32) {
|
||||
gen_nanbox_s(freg, freg);
|
||||
}
|
||||
}
|
||||
|
||||
static bool gen_fload_i(DisasContext *ctx, arg_fr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv temp = NULL;
|
||||
|
||||
if (a->imm) {
|
||||
temp = tcg_temp_new();
|
||||
tcg_gen_addi_tl(temp, addr, a->imm);
|
||||
addr = temp;
|
||||
}
|
||||
|
||||
tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
maybe_nanbox_load(cpu_fpr[a->fd], mop);
|
||||
|
||||
if (temp) {
|
||||
tcg_temp_free(temp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_fstore_i(DisasContext *ctx, arg_fr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv temp = NULL;
|
||||
|
||||
if (a->imm) {
|
||||
temp = tcg_temp_new();
|
||||
tcg_gen_addi_tl(temp, addr, a->imm);
|
||||
addr = temp;
|
||||
}
|
||||
|
||||
tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
|
||||
if (temp) {
|
||||
tcg_temp_free(temp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_floadx(DisasContext *ctx, arg_frr *a, MemOp mop)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
maybe_nanbox_load(cpu_fpr[a->fd], mop);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_fstorex(DisasContext *ctx, arg_frr *a, MemOp mop)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_fload_gt(DisasContext *ctx, arg_frr *a, MemOp mop)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
gen_helper_asrtgt_d(cpu_env, src1, src2);
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
maybe_nanbox_load(cpu_fpr[a->fd], mop);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_fstore_gt(DisasContext *ctx, arg_frr *a, MemOp mop)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
gen_helper_asrtgt_d(cpu_env, src1, src2);
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_fload_le(DisasContext *ctx, arg_frr *a, MemOp mop)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
gen_helper_asrtle_d(cpu_env, src1, src2);
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
maybe_nanbox_load(cpu_fpr[a->fd], mop);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_fstore_le(DisasContext *ctx, arg_frr *a, MemOp mop)
|
||||
{
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
gen_helper_asrtle_d(cpu_env, src1, src2);
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(fld_s, gen_fload_i, MO_TEUL)
|
||||
TRANS(fst_s, gen_fstore_i, MO_TEUL)
|
||||
TRANS(fld_d, gen_fload_i, MO_TEUQ)
|
||||
TRANS(fst_d, gen_fstore_i, MO_TEUQ)
|
||||
TRANS(fldx_s, gen_floadx, MO_TEUL)
|
||||
TRANS(fldx_d, gen_floadx, MO_TEUQ)
|
||||
TRANS(fstx_s, gen_fstorex, MO_TEUL)
|
||||
TRANS(fstx_d, gen_fstorex, MO_TEUQ)
|
||||
TRANS(fldgt_s, gen_fload_gt, MO_TEUL)
|
||||
TRANS(fldgt_d, gen_fload_gt, MO_TEUQ)
|
||||
TRANS(fldle_s, gen_fload_le, MO_TEUL)
|
||||
TRANS(fldle_d, gen_fload_le, MO_TEUQ)
|
||||
TRANS(fstgt_s, gen_fstore_gt, MO_TEUL)
|
||||
TRANS(fstgt_d, gen_fstore_gt, MO_TEUQ)
|
||||
TRANS(fstle_s, gen_fstore_le, MO_TEUL)
|
||||
TRANS(fstle_d, gen_fstore_le, MO_TEUQ)
|
|
@ -0,0 +1,157 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static const uint32_t fcsr_mask[4] = {
|
||||
UINT32_MAX, FCSR0_M1, FCSR0_M2, FCSR0_M3
|
||||
};
|
||||
|
||||
static bool trans_fsel(DisasContext *ctx, arg_fsel *a)
|
||||
{
|
||||
TCGv zero = tcg_constant_tl(0);
|
||||
TCGv cond = tcg_temp_new();
|
||||
|
||||
tcg_gen_ld8u_tl(cond, cpu_env, offsetof(CPULoongArchState, cf[a->ca]));
|
||||
tcg_gen_movcond_tl(TCG_COND_EQ, cpu_fpr[a->fd], cond, zero,
|
||||
cpu_fpr[a->fj], cpu_fpr[a->fk]);
|
||||
tcg_temp_free(cond);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_f2f(DisasContext *ctx, arg_ff *a,
|
||||
void (*func)(TCGv, TCGv), bool nanbox)
|
||||
{
|
||||
TCGv dest = cpu_fpr[a->fd];
|
||||
TCGv src = cpu_fpr[a->fj];
|
||||
|
||||
func(dest, src);
|
||||
if (nanbox) {
|
||||
gen_nanbox_s(cpu_fpr[a->fd], cpu_fpr[a->fd]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_r2f(DisasContext *ctx, arg_fr *a,
|
||||
void (*func)(TCGv, TCGv))
|
||||
{
|
||||
TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
func(cpu_fpr[a->fd], src);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_f2r(DisasContext *ctx, arg_rf *a,
|
||||
void (*func)(TCGv, TCGv))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
|
||||
func(dest, cpu_fpr[a->fj]);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_movgr2fcsr(DisasContext *ctx, arg_movgr2fcsr *a)
|
||||
{
|
||||
uint32_t mask = fcsr_mask[a->fcsrd];
|
||||
TCGv Rj = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
if (mask == UINT32_MAX) {
|
||||
tcg_gen_extrl_i64_i32(cpu_fcsr0, Rj);
|
||||
} else {
|
||||
TCGv_i32 temp = tcg_temp_new_i32();
|
||||
|
||||
tcg_gen_extrl_i64_i32(temp, Rj);
|
||||
tcg_gen_andi_i32(temp, temp, mask);
|
||||
tcg_gen_andi_i32(cpu_fcsr0, cpu_fcsr0, ~mask);
|
||||
tcg_gen_or_i32(cpu_fcsr0, cpu_fcsr0, temp);
|
||||
tcg_temp_free_i32(temp);
|
||||
|
||||
/*
|
||||
* Install the new rounding mode to fpu_status, if changed.
|
||||
* Note that FCSR3 is exactly the rounding mode field.
|
||||
*/
|
||||
if (mask != FCSR0_M3) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
gen_helper_set_rounding_mode(cpu_env, cpu_fcsr0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a)
|
||||
{
|
||||
TCGv_i32 temp = tcg_temp_new_i32();
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
|
||||
tcg_gen_andi_i32(temp, cpu_fcsr0, fcsr_mask[a->fcsrs]);
|
||||
tcg_gen_ext_i32_i64(dest, temp);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
tcg_temp_free_i32(temp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gen_movgr2fr_w(TCGv dest, TCGv src)
|
||||
{
|
||||
tcg_gen_deposit_i64(dest, dest, src, 0, 32);
|
||||
}
|
||||
|
||||
static void gen_movgr2frh_w(TCGv dest, TCGv src)
|
||||
{
|
||||
tcg_gen_deposit_i64(dest, dest, src, 32, 32);
|
||||
}
|
||||
|
||||
static void gen_movfrh2gr_s(TCGv dest, TCGv src)
|
||||
{
|
||||
tcg_gen_sextract_tl(dest, src, 32, 32);
|
||||
}
|
||||
|
||||
static bool trans_movfr2cf(DisasContext *ctx, arg_movfr2cf *a)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
||||
tcg_gen_andi_tl(t0, cpu_fpr[a->fj], 0x1);
|
||||
tcg_gen_st8_tl(t0, cpu_env, offsetof(CPULoongArchState, cf[a->cd & 0x7]));
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_movcf2fr(DisasContext *ctx, arg_movcf2fr *a)
|
||||
{
|
||||
tcg_gen_ld8u_tl(cpu_fpr[a->fd], cpu_env,
|
||||
offsetof(CPULoongArchState, cf[a->cj & 0x7]));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_movgr2cf(DisasContext *ctx, arg_movgr2cf *a)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
||||
tcg_gen_andi_tl(t0, gpr_src(ctx, a->rj, EXT_NONE), 0x1);
|
||||
tcg_gen_st8_tl(t0, cpu_env, offsetof(CPULoongArchState, cf[a->cd & 0x7]));
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_movcf2gr(DisasContext *ctx, arg_movcf2gr *a)
|
||||
{
|
||||
tcg_gen_ld8u_tl(gpr_dst(ctx, a->rd, EXT_NONE), cpu_env,
|
||||
offsetof(CPULoongArchState, cf[a->cj & 0x7]));
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(fmov_s, gen_f2f, tcg_gen_mov_tl, true)
|
||||
TRANS(fmov_d, gen_f2f, tcg_gen_mov_tl, false)
|
||||
TRANS(movgr2fr_w, gen_r2f, gen_movgr2fr_w)
|
||||
TRANS(movgr2fr_d, gen_r2f, tcg_gen_mov_tl)
|
||||
TRANS(movgr2frh_w, gen_r2f, gen_movgr2frh_w)
|
||||
TRANS(movfr2gr_s, gen_f2r, tcg_gen_ext32s_tl)
|
||||
TRANS(movfr2gr_d, gen_f2r, tcg_gen_mov_tl)
|
||||
TRANS(movfrh2gr_s, gen_f2r, gen_movfrh2gr_s)
|
|
@ -0,0 +1,229 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static bool gen_load(DisasContext *ctx, arg_rr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv temp = NULL;
|
||||
|
||||
if (a->imm) {
|
||||
temp = tcg_temp_new();
|
||||
tcg_gen_addi_tl(temp, addr, a->imm);
|
||||
addr = temp;
|
||||
}
|
||||
|
||||
tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
if (temp) {
|
||||
tcg_temp_free(temp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_store(DisasContext *ctx, arg_rr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv data = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv temp = NULL;
|
||||
|
||||
if (a->imm) {
|
||||
temp = tcg_temp_new();
|
||||
tcg_gen_addi_tl(temp, addr, a->imm);
|
||||
addr = temp;
|
||||
}
|
||||
|
||||
tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop);
|
||||
|
||||
if (temp) {
|
||||
tcg_temp_free(temp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_loadx(DisasContext *ctx, arg_rrr *a, MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_storex(DisasContext *ctx, arg_rrr *a, MemOp mop)
|
||||
{
|
||||
TCGv data = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
TCGv addr = tcg_temp_new();
|
||||
|
||||
tcg_gen_add_tl(addr, src1, src2);
|
||||
tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop);
|
||||
tcg_temp_free(addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_load_gt(DisasContext *ctx, arg_rrr *a, MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
gen_helper_asrtgt_d(cpu_env, src1, src2);
|
||||
tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_load_le(DisasContext *ctx, arg_rrr *a, MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
gen_helper_asrtle_d(cpu_env, src1, src2);
|
||||
tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_store_gt(DisasContext *ctx, arg_rrr *a, MemOp mop)
|
||||
{
|
||||
TCGv data = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
gen_helper_asrtgt_d(cpu_env, src1, src2);
|
||||
tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_store_le(DisasContext *ctx, arg_rrr *a, MemOp mop)
|
||||
{
|
||||
TCGv data = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
gen_helper_asrtle_d(cpu_env, src1, src2);
|
||||
tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_preld(DisasContext *ctx, arg_preld *a)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_dbar(DisasContext *ctx, arg_dbar * a)
|
||||
{
|
||||
tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_ibar(DisasContext *ctx, arg_ibar *a)
|
||||
{
|
||||
ctx->base.is_jmp = DISAS_STOP;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_ldptr(DisasContext *ctx, arg_rr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv temp = NULL;
|
||||
|
||||
if (a->imm) {
|
||||
temp = tcg_temp_new();
|
||||
tcg_gen_addi_tl(temp, addr, a->imm);
|
||||
addr = temp;
|
||||
}
|
||||
|
||||
tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
if (temp) {
|
||||
tcg_temp_free(temp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_stptr(DisasContext *ctx, arg_rr_i *a, MemOp mop)
|
||||
{
|
||||
TCGv data = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv temp = NULL;
|
||||
|
||||
if (a->imm) {
|
||||
temp = tcg_temp_new();
|
||||
tcg_gen_addi_tl(temp, addr, a->imm);
|
||||
addr = temp;
|
||||
}
|
||||
|
||||
tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop);
|
||||
|
||||
if (temp) {
|
||||
tcg_temp_free(temp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(ld_b, gen_load, MO_SB)
|
||||
TRANS(ld_h, gen_load, MO_TESW)
|
||||
TRANS(ld_w, gen_load, MO_TESL)
|
||||
TRANS(ld_d, gen_load, MO_TEUQ)
|
||||
TRANS(st_b, gen_store, MO_UB)
|
||||
TRANS(st_h, gen_store, MO_TEUW)
|
||||
TRANS(st_w, gen_store, MO_TEUL)
|
||||
TRANS(st_d, gen_store, MO_TEUQ)
|
||||
TRANS(ld_bu, gen_load, MO_UB)
|
||||
TRANS(ld_hu, gen_load, MO_TEUW)
|
||||
TRANS(ld_wu, gen_load, MO_TEUL)
|
||||
TRANS(ldx_b, gen_loadx, MO_SB)
|
||||
TRANS(ldx_h, gen_loadx, MO_TESW)
|
||||
TRANS(ldx_w, gen_loadx, MO_TESL)
|
||||
TRANS(ldx_d, gen_loadx, MO_TEUQ)
|
||||
TRANS(stx_b, gen_storex, MO_UB)
|
||||
TRANS(stx_h, gen_storex, MO_TEUW)
|
||||
TRANS(stx_w, gen_storex, MO_TEUL)
|
||||
TRANS(stx_d, gen_storex, MO_TEUQ)
|
||||
TRANS(ldx_bu, gen_loadx, MO_UB)
|
||||
TRANS(ldx_hu, gen_loadx, MO_TEUW)
|
||||
TRANS(ldx_wu, gen_loadx, MO_TEUL)
|
||||
TRANS(ldptr_w, gen_ldptr, MO_TESL)
|
||||
TRANS(stptr_w, gen_stptr, MO_TEUL)
|
||||
TRANS(ldptr_d, gen_ldptr, MO_TEUQ)
|
||||
TRANS(stptr_d, gen_stptr, MO_TEUQ)
|
||||
TRANS(ldgt_b, gen_load_gt, MO_SB)
|
||||
TRANS(ldgt_h, gen_load_gt, MO_TESW)
|
||||
TRANS(ldgt_w, gen_load_gt, MO_TESL)
|
||||
TRANS(ldgt_d, gen_load_gt, MO_TEUQ)
|
||||
TRANS(ldle_b, gen_load_le, MO_SB)
|
||||
TRANS(ldle_h, gen_load_le, MO_TESW)
|
||||
TRANS(ldle_w, gen_load_le, MO_TESL)
|
||||
TRANS(ldle_d, gen_load_le, MO_TEUQ)
|
||||
TRANS(stgt_b, gen_store_gt, MO_UB)
|
||||
TRANS(stgt_h, gen_store_gt, MO_TEUW)
|
||||
TRANS(stgt_w, gen_store_gt, MO_TEUL)
|
||||
TRANS(stgt_d, gen_store_gt, MO_TEUQ)
|
||||
TRANS(stle_b, gen_store_le, MO_UB)
|
||||
TRANS(stle_h, gen_store_le, MO_TEUW)
|
||||
TRANS(stle_w, gen_store_le, MO_TEUL)
|
||||
TRANS(stle_d, gen_store_le, MO_TEUQ)
|
|
@ -0,0 +1,466 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*
|
||||
* LoongArch translation routines for the privileged instructions.
|
||||
*/
|
||||
|
||||
#include "cpu-csr.h"
|
||||
|
||||
typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
|
||||
typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
|
||||
|
||||
typedef struct {
|
||||
int offset;
|
||||
int flags;
|
||||
GenCSRRead readfn;
|
||||
GenCSRWrite writefn;
|
||||
} CSRInfo;
|
||||
|
||||
enum {
|
||||
CSRFL_READONLY = (1 << 0),
|
||||
CSRFL_EXITTB = (1 << 1),
|
||||
CSRFL_IO = (1 << 2),
|
||||
};
|
||||
|
||||
#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \
|
||||
[LOONGARCH_CSR_##NAME] = { \
|
||||
.offset = offsetof(CPULoongArchState, CSR_##NAME), \
|
||||
.flags = FL, .readfn = RD, .writefn = WR \
|
||||
}
|
||||
|
||||
#define CSR_OFF_ARRAY(NAME, N) \
|
||||
[LOONGARCH_CSR_##NAME(N)] = { \
|
||||
.offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \
|
||||
.flags = 0, .readfn = NULL, .writefn = NULL \
|
||||
}
|
||||
|
||||
#define CSR_OFF_FLAGS(NAME, FL) \
|
||||
CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
|
||||
|
||||
#define CSR_OFF(NAME) \
|
||||
CSR_OFF_FLAGS(NAME, 0)
|
||||
|
||||
static const CSRInfo csr_info[] = {
|
||||
CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB),
|
||||
CSR_OFF(PRMD),
|
||||
CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB),
|
||||
CSR_OFF_FLAGS(MISC, CSRFL_READONLY),
|
||||
CSR_OFF(ECFG),
|
||||
CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat),
|
||||
CSR_OFF(ERA),
|
||||
CSR_OFF(BADV),
|
||||
CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
|
||||
CSR_OFF(EENTRY),
|
||||
CSR_OFF(TLBIDX),
|
||||
CSR_OFF(TLBEHI),
|
||||
CSR_OFF(TLBELO0),
|
||||
CSR_OFF(TLBELO1),
|
||||
CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid),
|
||||
CSR_OFF(PGDL),
|
||||
CSR_OFF(PGDH),
|
||||
CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL),
|
||||
CSR_OFF(PWCL),
|
||||
CSR_OFF(PWCH),
|
||||
CSR_OFF(STLBPS),
|
||||
CSR_OFF(RVACFG),
|
||||
[LOONGARCH_CSR_CPUID] = {
|
||||
.offset = (int)offsetof(CPUState, cpu_index)
|
||||
- (int)offsetof(LoongArchCPU, env),
|
||||
.flags = CSRFL_READONLY,
|
||||
.readfn = NULL,
|
||||
.writefn = NULL
|
||||
},
|
||||
CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY),
|
||||
CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY),
|
||||
CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY),
|
||||
CSR_OFF_ARRAY(SAVE, 0),
|
||||
CSR_OFF_ARRAY(SAVE, 1),
|
||||
CSR_OFF_ARRAY(SAVE, 2),
|
||||
CSR_OFF_ARRAY(SAVE, 3),
|
||||
CSR_OFF_ARRAY(SAVE, 4),
|
||||
CSR_OFF_ARRAY(SAVE, 5),
|
||||
CSR_OFF_ARRAY(SAVE, 6),
|
||||
CSR_OFF_ARRAY(SAVE, 7),
|
||||
CSR_OFF_ARRAY(SAVE, 8),
|
||||
CSR_OFF_ARRAY(SAVE, 9),
|
||||
CSR_OFF_ARRAY(SAVE, 10),
|
||||
CSR_OFF_ARRAY(SAVE, 11),
|
||||
CSR_OFF_ARRAY(SAVE, 12),
|
||||
CSR_OFF_ARRAY(SAVE, 13),
|
||||
CSR_OFF_ARRAY(SAVE, 14),
|
||||
CSR_OFF_ARRAY(SAVE, 15),
|
||||
CSR_OFF(TID),
|
||||
CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg),
|
||||
CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL),
|
||||
CSR_OFF(CNTC),
|
||||
CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr),
|
||||
CSR_OFF(LLBCTL),
|
||||
CSR_OFF(IMPCTL1),
|
||||
CSR_OFF(IMPCTL2),
|
||||
CSR_OFF(TLBRENTRY),
|
||||
CSR_OFF(TLBRBADV),
|
||||
CSR_OFF(TLBRERA),
|
||||
CSR_OFF(TLBRSAVE),
|
||||
CSR_OFF(TLBRELO0),
|
||||
CSR_OFF(TLBRELO1),
|
||||
CSR_OFF(TLBREHI),
|
||||
CSR_OFF(TLBRPRMD),
|
||||
CSR_OFF(MERRCTL),
|
||||
CSR_OFF(MERRINFO1),
|
||||
CSR_OFF(MERRINFO2),
|
||||
CSR_OFF(MERRENTRY),
|
||||
CSR_OFF(MERRERA),
|
||||
CSR_OFF(MERRSAVE),
|
||||
CSR_OFF(CTAG),
|
||||
CSR_OFF_ARRAY(DMW, 0),
|
||||
CSR_OFF_ARRAY(DMW, 1),
|
||||
CSR_OFF_ARRAY(DMW, 2),
|
||||
CSR_OFF_ARRAY(DMW, 3),
|
||||
CSR_OFF(DBG),
|
||||
CSR_OFF(DERA),
|
||||
CSR_OFF(DSAVE),
|
||||
};
|
||||
|
||||
static bool check_plv(DisasContext *ctx)
|
||||
{
|
||||
if (ctx->base.tb->flags == MMU_USER_IDX) {
|
||||
generate_exception(ctx, EXCCODE_IPE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const CSRInfo *get_csr(unsigned csr_num)
|
||||
{
|
||||
const CSRInfo *csr;
|
||||
|
||||
if (csr_num >= ARRAY_SIZE(csr_info)) {
|
||||
return NULL;
|
||||
}
|
||||
csr = &csr_info[csr_num];
|
||||
if (csr->offset == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return csr;
|
||||
}
|
||||
|
||||
static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
|
||||
{
|
||||
if ((csr->flags & CSRFL_READONLY) && write) {
|
||||
return false;
|
||||
}
|
||||
if ((csr->flags & CSRFL_IO) &&
|
||||
(tb_cflags(ctx->base.tb) & CF_USE_ICOUNT)) {
|
||||
gen_io_start();
|
||||
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
|
||||
} else if ((csr->flags & CSRFL_EXITTB) && write) {
|
||||
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
|
||||
{
|
||||
TCGv dest;
|
||||
const CSRInfo *csr;
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
csr = get_csr(a->csr);
|
||||
if (csr == NULL) {
|
||||
/* CSR is undefined: read as 0. */
|
||||
dest = tcg_constant_tl(0);
|
||||
} else {
|
||||
check_csr_flags(ctx, csr, false);
|
||||
dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
if (csr->readfn) {
|
||||
csr->readfn(dest, cpu_env);
|
||||
} else {
|
||||
tcg_gen_ld_tl(dest, cpu_env, csr->offset);
|
||||
}
|
||||
}
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
|
||||
{
|
||||
TCGv dest, src1;
|
||||
const CSRInfo *csr;
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
csr = get_csr(a->csr);
|
||||
if (csr == NULL) {
|
||||
/* CSR is undefined: write ignored, read old_value as 0. */
|
||||
gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
|
||||
return true;
|
||||
}
|
||||
if (!check_csr_flags(ctx, csr, true)) {
|
||||
/* CSR is readonly: trap. */
|
||||
return false;
|
||||
}
|
||||
src1 = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
if (csr->writefn) {
|
||||
dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
csr->writefn(dest, cpu_env, src1);
|
||||
} else {
|
||||
dest = temp_new(ctx);
|
||||
tcg_gen_ld_tl(dest, cpu_env, csr->offset);
|
||||
tcg_gen_st_tl(src1, cpu_env, csr->offset);
|
||||
}
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
|
||||
{
|
||||
TCGv src1, mask, oldv, newv, temp;
|
||||
const CSRInfo *csr;
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
csr = get_csr(a->csr);
|
||||
if (csr == NULL) {
|
||||
/* CSR is undefined: write ignored, read old_value as 0. */
|
||||
gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!check_csr_flags(ctx, csr, true)) {
|
||||
/* CSR is readonly: trap. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* So far only readonly csrs have readfn. */
|
||||
assert(csr->readfn == NULL);
|
||||
|
||||
src1 = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
mask = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
oldv = tcg_temp_new();
|
||||
newv = tcg_temp_new();
|
||||
temp = tcg_temp_new();
|
||||
|
||||
tcg_gen_ld_tl(oldv, cpu_env, csr->offset);
|
||||
tcg_gen_and_tl(newv, src1, mask);
|
||||
tcg_gen_andc_tl(temp, oldv, mask);
|
||||
tcg_gen_or_tl(newv, newv, temp);
|
||||
|
||||
if (csr->writefn) {
|
||||
csr->writefn(oldv, cpu_env, newv);
|
||||
} else {
|
||||
tcg_gen_st_tl(newv, cpu_env, csr->offset);
|
||||
}
|
||||
gen_set_gpr(a->rd, oldv, EXT_NONE);
|
||||
|
||||
tcg_temp_free(temp);
|
||||
tcg_temp_free(newv);
|
||||
tcg_temp_free(oldv);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
|
||||
void (*func)(TCGv, TCGv_ptr, TCGv))
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
func(dest, cpu_env, src1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
|
||||
void (*func)(TCGv_ptr, TCGv, TCGv))
|
||||
{
|
||||
TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
|
||||
TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
func(cpu_env, addr, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(iocsrrd_b, gen_iocsrrd, gen_helper_iocsrrd_b)
|
||||
TRANS(iocsrrd_h, gen_iocsrrd, gen_helper_iocsrrd_h)
|
||||
TRANS(iocsrrd_w, gen_iocsrrd, gen_helper_iocsrrd_w)
|
||||
TRANS(iocsrrd_d, gen_iocsrrd, gen_helper_iocsrrd_d)
|
||||
TRANS(iocsrwr_b, gen_iocsrwr, gen_helper_iocsrwr_b)
|
||||
TRANS(iocsrwr_h, gen_iocsrwr, gen_helper_iocsrwr_h)
|
||||
TRANS(iocsrwr_w, gen_iocsrwr, gen_helper_iocsrwr_w)
|
||||
TRANS(iocsrwr_d, gen_iocsrwr, gen_helper_iocsrwr_d)
|
||||
|
||||
static void check_mmu_idx(DisasContext *ctx)
|
||||
{
|
||||
if (ctx->mem_idx != MMU_DA_IDX) {
|
||||
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
|
||||
ctx->base.is_jmp = DISAS_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_tlbsrch(cpu_env);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_tlbrd(cpu_env);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_tlbwr(cpu_env);
|
||||
check_mmu_idx(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_tlbfill(cpu_env);
|
||||
check_mmu_idx(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_tlbclr(cpu_env);
|
||||
check_mmu_idx(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_tlbflush(cpu_env);
|
||||
check_mmu_idx(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
|
||||
{
|
||||
TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (a->imm) {
|
||||
case 0:
|
||||
case 1:
|
||||
gen_helper_invtlb_all(cpu_env);
|
||||
break;
|
||||
case 2:
|
||||
gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1));
|
||||
break;
|
||||
case 3:
|
||||
gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0));
|
||||
break;
|
||||
case 4:
|
||||
gen_helper_invtlb_all_asid(cpu_env, rj);
|
||||
break;
|
||||
case 5:
|
||||
gen_helper_invtlb_page_asid(cpu_env, rj, rk);
|
||||
break;
|
||||
case 6:
|
||||
gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
ctx->base.is_jmp = DISAS_STOP;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
|
||||
{
|
||||
/* Treat the cacop as a nop */
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
|
||||
{
|
||||
TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->imm), mem_idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
|
||||
{
|
||||
TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
|
||||
TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->imm), mem_idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
gen_helper_ertn(cpu_env);
|
||||
ctx->base.is_jmp = DISAS_EXIT;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
generate_exception(ctx, EXCCODE_DBP);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_idle(DisasContext *ctx, arg_idle *a)
|
||||
{
|
||||
if (check_plv(ctx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
|
||||
gen_helper_idle(cpu_env);
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static void gen_sll_w(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, src2, 0x1f);
|
||||
tcg_gen_shl_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_srl_w(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, src2, 0x1f);
|
||||
tcg_gen_shr_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_sra_w(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, src2, 0x1f);
|
||||
tcg_gen_sar_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_sll_d(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, src2, 0x3f);
|
||||
tcg_gen_shl_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_srl_d(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, src2, 0x3f);
|
||||
tcg_gen_shr_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_sra_d(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, src2, 0x3f);
|
||||
tcg_gen_sar_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_rotr_w(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv_i32 t1 = tcg_temp_new_i32();
|
||||
TCGv_i32 t2 = tcg_temp_new_i32();
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
||||
tcg_gen_andi_tl(t0, src2, 0x1f);
|
||||
|
||||
tcg_gen_trunc_tl_i32(t1, src1);
|
||||
tcg_gen_trunc_tl_i32(t2, t0);
|
||||
|
||||
tcg_gen_rotr_i32(t1, t1, t2);
|
||||
tcg_gen_ext_i32_tl(dest, t1);
|
||||
|
||||
tcg_temp_free_i32(t1);
|
||||
tcg_temp_free_i32(t2);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, src2, 0x3f);
|
||||
tcg_gen_rotr_tl(dest, src1, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a)
|
||||
{
|
||||
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
|
||||
TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO);
|
||||
|
||||
tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm);
|
||||
gen_set_gpr(a->rd, dest, EXT_NONE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(sll_w, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w)
|
||||
TRANS(srl_w, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_srl_w)
|
||||
TRANS(sra_w, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w)
|
||||
TRANS(sll_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d)
|
||||
TRANS(srl_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d)
|
||||
TRANS(sra_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d)
|
||||
TRANS(rotr_w, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w)
|
||||
TRANS(rotr_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d)
|
||||
TRANS(slli_w, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl)
|
||||
TRANS(slli_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl)
|
||||
TRANS(srli_w, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl)
|
||||
TRANS(srli_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl)
|
||||
TRANS(srai_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl)
|
||||
TRANS(rotri_w, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w)
|
||||
TRANS(rotri_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl)
|
|
@ -0,0 +1,486 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# LoongArch instruction decode definitions.
|
||||
#
|
||||
# Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
#
|
||||
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
%i14s2 10:s14 !function=shl_2
|
||||
%sa2p1 15:2 !function=plus_1
|
||||
%offs21 0:s5 10:16 !function=shl_2
|
||||
%offs16 10:s16 !function=shl_2
|
||||
%offs26 0:s10 10:16 !function=shl_2
|
||||
|
||||
#
|
||||
# Argument sets
|
||||
#
|
||||
&i imm
|
||||
&r_i rd imm
|
||||
&rr rd rj
|
||||
&rr_jk rj rk
|
||||
&rrr rd rj rk
|
||||
&rr_i rd rj imm
|
||||
&hint_r_i hint rj imm
|
||||
&rrr_sa rd rj rk sa
|
||||
&rr_ms_ls rd rj ms ls
|
||||
&ff fd fj
|
||||
&fff fd fj fk
|
||||
&ffff fd fj fk fa
|
||||
&cff_fcond cd fj fk fcond
|
||||
&fffc fd fj fk ca
|
||||
&fr fd rj
|
||||
&rf rd fj
|
||||
&fcsrd_r fcsrd rj
|
||||
&r_fcsrs rd fcsrs
|
||||
&cf cd fj
|
||||
&fc fd cj
|
||||
&cr cd rj
|
||||
&rc rd cj
|
||||
&frr fd rj rk
|
||||
&fr_i fd rj imm
|
||||
&r_offs rj offs
|
||||
&c_offs cj offs
|
||||
&offs offs
|
||||
&rr_offs rj rd offs
|
||||
&r_csr rd csr
|
||||
&rr_csr rd rj csr
|
||||
&empty
|
||||
&i_rr imm rj rk
|
||||
&cop_r_i cop rj imm
|
||||
&j_i rj imm
|
||||
|
||||
#
|
||||
# Formats
|
||||
#
|
||||
@i15 .... ........ ..... imm:15 &i
|
||||
@rr .... ........ ..... ..... rj:5 rd:5 &rr
|
||||
@rr_jk .... ........ ..... rk:5 rj:5 ..... &rr_jk
|
||||
@rrr .... ........ ..... rk:5 rj:5 rd:5 &rrr
|
||||
@r_i20 .... ... imm:s20 rd:5 &r_i
|
||||
@rr_ui5 .... ........ ..... imm:5 rj:5 rd:5 &rr_i
|
||||
@rr_ui6 .... ........ .... imm:6 rj:5 rd:5 &rr_i
|
||||
@rr_ui8 .. ........ .... imm:8 rj:5 rd:5 &rr_i
|
||||
@rr_i12 .... ...... imm:s12 rj:5 rd:5 &rr_i
|
||||
@rr_ui12 .... ...... imm:12 rj:5 rd:5 &rr_i
|
||||
@rr_i14s2 .... .... .............. rj:5 rd:5 &rr_i imm=%i14s2
|
||||
@rr_i16 .... .. imm:s16 rj:5 rd:5 &rr_i
|
||||
@hint_r_i12 .... ...... imm:s12 rj:5 hint:5 &hint_r_i
|
||||
@rrr_sa2p1 .... ........ ... .. rk:5 rj:5 rd:5 &rrr_sa sa=%sa2p1
|
||||
@rrr_sa2 .... ........ ... sa:2 rk:5 rj:5 rd:5 &rrr_sa
|
||||
@rrr_sa3 .... ........ .. sa:3 rk:5 rj:5 rd:5 &rrr_sa
|
||||
@rr_2bw .... ....... ms:5 . ls:5 rj:5 rd:5 &rr_ms_ls
|
||||
@rr_2bd .... ...... ms:6 ls:6 rj:5 rd:5 &rr_ms_ls
|
||||
@ff .... ........ ..... ..... fj:5 fd:5 &ff
|
||||
@fff .... ........ ..... fk:5 fj:5 fd:5 &fff
|
||||
@ffff .... ........ fa:5 fk:5 fj:5 fd:5 &ffff
|
||||
@cff_fcond .... ........ fcond:5 fk:5 fj:5 .. cd:3 &cff_fcond
|
||||
@fffc .... ........ .. ca:3 fk:5 fj:5 fd:5 &fffc
|
||||
@fr .... ........ ..... ..... rj:5 fd:5 &fr
|
||||
@rf .... ........ ..... ..... fj:5 rd:5 &rf
|
||||
@fcsrd_r .... ........ ..... ..... rj:5 fcsrd:5 &fcsrd_r
|
||||
@r_fcsrs .... ........ ..... ..... fcsrs:5 rd:5 &r_fcsrs
|
||||
@cf .... ........ ..... ..... fj:5 .. cd:3 &cf
|
||||
@fc .... ........ ..... ..... .. cj:3 fd:5 &fc
|
||||
@cr .... ........ ..... ..... rj:5 .. cd:3 &cr
|
||||
@rc .... ........ ..... ..... .. cj:3 rd:5 &rc
|
||||
@frr .... ........ ..... rk:5 rj:5 fd:5 &frr
|
||||
@fr_i12 .... ...... imm:s12 rj:5 fd:5 &fr_i
|
||||
@r_offs21 .... .. ................ rj:5 ..... &r_offs offs=%offs21
|
||||
@c_offs21 .... .. ................ .. cj:3 ..... &c_offs offs=%offs21
|
||||
@offs26 .... .. .......................... &offs offs=%offs26
|
||||
@rr_offs16 .... .. ................ rj:5 rd:5 &rr_offs offs=%offs16
|
||||
@r_csr .... .... csr:14 ..... rd:5 &r_csr
|
||||
@rr_csr .... .... csr:14 rj:5 rd:5 &rr_csr
|
||||
@empty .... ........ ..... ..... ..... ..... &empty
|
||||
@i_rr ...... ...... ..... rk:5 rj:5 imm:5 &i_rr
|
||||
@cop_r_i .... ...... imm:s12 rj:5 cop:5 &cop_r_i
|
||||
@j_i .... ........ .. imm:8 rj:5 ..... &j_i
|
||||
|
||||
#
|
||||
# Fixed point arithmetic operation instruction
|
||||
#
|
||||
add_w 0000 00000001 00000 ..... ..... ..... @rrr
|
||||
add_d 0000 00000001 00001 ..... ..... ..... @rrr
|
||||
sub_w 0000 00000001 00010 ..... ..... ..... @rrr
|
||||
sub_d 0000 00000001 00011 ..... ..... ..... @rrr
|
||||
slt 0000 00000001 00100 ..... ..... ..... @rrr
|
||||
sltu 0000 00000001 00101 ..... ..... ..... @rrr
|
||||
slti 0000 001000 ............ ..... ..... @rr_i12
|
||||
sltui 0000 001001 ............ ..... ..... @rr_i12
|
||||
nor 0000 00000001 01000 ..... ..... ..... @rrr
|
||||
and 0000 00000001 01001 ..... ..... ..... @rrr
|
||||
or 0000 00000001 01010 ..... ..... ..... @rrr
|
||||
xor 0000 00000001 01011 ..... ..... ..... @rrr
|
||||
orn 0000 00000001 01100 ..... ..... ..... @rrr
|
||||
andn 0000 00000001 01101 ..... ..... ..... @rrr
|
||||
mul_w 0000 00000001 11000 ..... ..... ..... @rrr
|
||||
mulh_w 0000 00000001 11001 ..... ..... ..... @rrr
|
||||
mulh_wu 0000 00000001 11010 ..... ..... ..... @rrr
|
||||
mul_d 0000 00000001 11011 ..... ..... ..... @rrr
|
||||
mulh_d 0000 00000001 11100 ..... ..... ..... @rrr
|
||||
mulh_du 0000 00000001 11101 ..... ..... ..... @rrr
|
||||
mulw_d_w 0000 00000001 11110 ..... ..... ..... @rrr
|
||||
mulw_d_wu 0000 00000001 11111 ..... ..... ..... @rrr
|
||||
div_w 0000 00000010 00000 ..... ..... ..... @rrr
|
||||
mod_w 0000 00000010 00001 ..... ..... ..... @rrr
|
||||
div_wu 0000 00000010 00010 ..... ..... ..... @rrr
|
||||
mod_wu 0000 00000010 00011 ..... ..... ..... @rrr
|
||||
div_d 0000 00000010 00100 ..... ..... ..... @rrr
|
||||
mod_d 0000 00000010 00101 ..... ..... ..... @rrr
|
||||
div_du 0000 00000010 00110 ..... ..... ..... @rrr
|
||||
mod_du 0000 00000010 00111 ..... ..... ..... @rrr
|
||||
alsl_w 0000 00000000 010 .. ..... ..... ..... @rrr_sa2p1
|
||||
alsl_wu 0000 00000000 011 .. ..... ..... ..... @rrr_sa2p1
|
||||
alsl_d 0000 00000010 110 .. ..... ..... ..... @rrr_sa2p1
|
||||
lu12i_w 0001 010 .................... ..... @r_i20
|
||||
lu32i_d 0001 011 .................... ..... @r_i20
|
||||
lu52i_d 0000 001100 ............ ..... ..... @rr_i12
|
||||
pcaddi 0001 100 .................... ..... @r_i20
|
||||
pcalau12i 0001 101 .................... ..... @r_i20
|
||||
pcaddu12i 0001 110 .................... ..... @r_i20
|
||||
pcaddu18i 0001 111 .................... ..... @r_i20
|
||||
addi_w 0000 001010 ............ ..... ..... @rr_i12
|
||||
addi_d 0000 001011 ............ ..... ..... @rr_i12
|
||||
addu16i_d 0001 00 ................ ..... ..... @rr_i16
|
||||
andi 0000 001101 ............ ..... ..... @rr_ui12
|
||||
ori 0000 001110 ............ ..... ..... @rr_ui12
|
||||
xori 0000 001111 ............ ..... ..... @rr_ui12
|
||||
|
||||
#
|
||||
# Fixed point shift operation instruction
|
||||
#
|
||||
sll_w 0000 00000001 01110 ..... ..... ..... @rrr
|
||||
srl_w 0000 00000001 01111 ..... ..... ..... @rrr
|
||||
sra_w 0000 00000001 10000 ..... ..... ..... @rrr
|
||||
sll_d 0000 00000001 10001 ..... ..... ..... @rrr
|
||||
srl_d 0000 00000001 10010 ..... ..... ..... @rrr
|
||||
sra_d 0000 00000001 10011 ..... ..... ..... @rrr
|
||||
rotr_w 0000 00000001 10110 ..... ..... ..... @rrr
|
||||
rotr_d 0000 00000001 10111 ..... ..... ..... @rrr
|
||||
slli_w 0000 00000100 00001 ..... ..... ..... @rr_ui5
|
||||
slli_d 0000 00000100 0001 ...... ..... ..... @rr_ui6
|
||||
srli_w 0000 00000100 01001 ..... ..... ..... @rr_ui5
|
||||
srli_d 0000 00000100 0101 ...... ..... ..... @rr_ui6
|
||||
srai_w 0000 00000100 10001 ..... ..... ..... @rr_ui5
|
||||
srai_d 0000 00000100 1001 ...... ..... ..... @rr_ui6
|
||||
rotri_w 0000 00000100 11001 ..... ..... ..... @rr_ui5
|
||||
rotri_d 0000 00000100 1101 ...... ..... ..... @rr_ui6
|
||||
|
||||
#
|
||||
# Fixed point bit operation instruction
|
||||
#
|
||||
ext_w_h 0000 00000000 00000 10110 ..... ..... @rr
|
||||
ext_w_b 0000 00000000 00000 10111 ..... ..... @rr
|
||||
clo_w 0000 00000000 00000 00100 ..... ..... @rr
|
||||
clz_w 0000 00000000 00000 00101 ..... ..... @rr
|
||||
cto_w 0000 00000000 00000 00110 ..... ..... @rr
|
||||
ctz_w 0000 00000000 00000 00111 ..... ..... @rr
|
||||
clo_d 0000 00000000 00000 01000 ..... ..... @rr
|
||||
clz_d 0000 00000000 00000 01001 ..... ..... @rr
|
||||
cto_d 0000 00000000 00000 01010 ..... ..... @rr
|
||||
ctz_d 0000 00000000 00000 01011 ..... ..... @rr
|
||||
revb_2h 0000 00000000 00000 01100 ..... ..... @rr
|
||||
revb_4h 0000 00000000 00000 01101 ..... ..... @rr
|
||||
revb_2w 0000 00000000 00000 01110 ..... ..... @rr
|
||||
revb_d 0000 00000000 00000 01111 ..... ..... @rr
|
||||
revh_2w 0000 00000000 00000 10000 ..... ..... @rr
|
||||
revh_d 0000 00000000 00000 10001 ..... ..... @rr
|
||||
bitrev_4b 0000 00000000 00000 10010 ..... ..... @rr
|
||||
bitrev_8b 0000 00000000 00000 10011 ..... ..... @rr
|
||||
bitrev_w 0000 00000000 00000 10100 ..... ..... @rr
|
||||
bitrev_d 0000 00000000 00000 10101 ..... ..... @rr
|
||||
bytepick_w 0000 00000000 100 .. ..... ..... ..... @rrr_sa2
|
||||
bytepick_d 0000 00000000 11 ... ..... ..... ..... @rrr_sa3
|
||||
maskeqz 0000 00000001 00110 ..... ..... ..... @rrr
|
||||
masknez 0000 00000001 00111 ..... ..... ..... @rrr
|
||||
bstrins_w 0000 0000011 ..... 0 ..... ..... ..... @rr_2bw
|
||||
bstrpick_w 0000 0000011 ..... 1 ..... ..... ..... @rr_2bw
|
||||
bstrins_d 0000 000010 ...... ...... ..... ..... @rr_2bd
|
||||
bstrpick_d 0000 000011 ...... ...... ..... ..... @rr_2bd
|
||||
|
||||
#
|
||||
# Fixed point load/store instruction
|
||||
#
|
||||
ld_b 0010 100000 ............ ..... ..... @rr_i12
|
||||
ld_h 0010 100001 ............ ..... ..... @rr_i12
|
||||
ld_w 0010 100010 ............ ..... ..... @rr_i12
|
||||
ld_d 0010 100011 ............ ..... ..... @rr_i12
|
||||
st_b 0010 100100 ............ ..... ..... @rr_i12
|
||||
st_h 0010 100101 ............ ..... ..... @rr_i12
|
||||
st_w 0010 100110 ............ ..... ..... @rr_i12
|
||||
st_d 0010 100111 ............ ..... ..... @rr_i12
|
||||
ld_bu 0010 101000 ............ ..... ..... @rr_i12
|
||||
ld_hu 0010 101001 ............ ..... ..... @rr_i12
|
||||
ld_wu 0010 101010 ............ ..... ..... @rr_i12
|
||||
ldx_b 0011 10000000 00000 ..... ..... ..... @rrr
|
||||
ldx_h 0011 10000000 01000 ..... ..... ..... @rrr
|
||||
ldx_w 0011 10000000 10000 ..... ..... ..... @rrr
|
||||
ldx_d 0011 10000000 11000 ..... ..... ..... @rrr
|
||||
stx_b 0011 10000001 00000 ..... ..... ..... @rrr
|
||||
stx_h 0011 10000001 01000 ..... ..... ..... @rrr
|
||||
stx_w 0011 10000001 10000 ..... ..... ..... @rrr
|
||||
stx_d 0011 10000001 11000 ..... ..... ..... @rrr
|
||||
ldx_bu 0011 10000010 00000 ..... ..... ..... @rrr
|
||||
ldx_hu 0011 10000010 01000 ..... ..... ..... @rrr
|
||||
ldx_wu 0011 10000010 10000 ..... ..... ..... @rrr
|
||||
preld 0010 101011 ............ ..... ..... @hint_r_i12
|
||||
dbar 0011 10000111 00100 ............... @i15
|
||||
ibar 0011 10000111 00101 ............... @i15
|
||||
ldptr_w 0010 0100 .............. ..... ..... @rr_i14s2
|
||||
stptr_w 0010 0101 .............. ..... ..... @rr_i14s2
|
||||
ldptr_d 0010 0110 .............. ..... ..... @rr_i14s2
|
||||
stptr_d 0010 0111 .............. ..... ..... @rr_i14s2
|
||||
ldgt_b 0011 10000111 10000 ..... ..... ..... @rrr
|
||||
ldgt_h 0011 10000111 10001 ..... ..... ..... @rrr
|
||||
ldgt_w 0011 10000111 10010 ..... ..... ..... @rrr
|
||||
ldgt_d 0011 10000111 10011 ..... ..... ..... @rrr
|
||||
ldle_b 0011 10000111 10100 ..... ..... ..... @rrr
|
||||
ldle_h 0011 10000111 10101 ..... ..... ..... @rrr
|
||||
ldle_w 0011 10000111 10110 ..... ..... ..... @rrr
|
||||
ldle_d 0011 10000111 10111 ..... ..... ..... @rrr
|
||||
stgt_b 0011 10000111 11000 ..... ..... ..... @rrr
|
||||
stgt_h 0011 10000111 11001 ..... ..... ..... @rrr
|
||||
stgt_w 0011 10000111 11010 ..... ..... ..... @rrr
|
||||
stgt_d 0011 10000111 11011 ..... ..... ..... @rrr
|
||||
stle_b 0011 10000111 11100 ..... ..... ..... @rrr
|
||||
stle_h 0011 10000111 11101 ..... ..... ..... @rrr
|
||||
stle_w 0011 10000111 11110 ..... ..... ..... @rrr
|
||||
stle_d 0011 10000111 11111 ..... ..... ..... @rrr
|
||||
|
||||
#
|
||||
# Fixed point atomic instruction
|
||||
#
|
||||
ll_w 0010 0000 .............. ..... ..... @rr_i14s2
|
||||
sc_w 0010 0001 .............. ..... ..... @rr_i14s2
|
||||
ll_d 0010 0010 .............. ..... ..... @rr_i14s2
|
||||
sc_d 0010 0011 .............. ..... ..... @rr_i14s2
|
||||
amswap_w 0011 10000110 00000 ..... ..... ..... @rrr
|
||||
amswap_d 0011 10000110 00001 ..... ..... ..... @rrr
|
||||
amadd_w 0011 10000110 00010 ..... ..... ..... @rrr
|
||||
amadd_d 0011 10000110 00011 ..... ..... ..... @rrr
|
||||
amand_w 0011 10000110 00100 ..... ..... ..... @rrr
|
||||
amand_d 0011 10000110 00101 ..... ..... ..... @rrr
|
||||
amor_w 0011 10000110 00110 ..... ..... ..... @rrr
|
||||
amor_d 0011 10000110 00111 ..... ..... ..... @rrr
|
||||
amxor_w 0011 10000110 01000 ..... ..... ..... @rrr
|
||||
amxor_d 0011 10000110 01001 ..... ..... ..... @rrr
|
||||
ammax_w 0011 10000110 01010 ..... ..... ..... @rrr
|
||||
ammax_d 0011 10000110 01011 ..... ..... ..... @rrr
|
||||
ammin_w 0011 10000110 01100 ..... ..... ..... @rrr
|
||||
ammin_d 0011 10000110 01101 ..... ..... ..... @rrr
|
||||
ammax_wu 0011 10000110 01110 ..... ..... ..... @rrr
|
||||
ammax_du 0011 10000110 01111 ..... ..... ..... @rrr
|
||||
ammin_wu 0011 10000110 10000 ..... ..... ..... @rrr
|
||||
ammin_du 0011 10000110 10001 ..... ..... ..... @rrr
|
||||
amswap_db_w 0011 10000110 10010 ..... ..... ..... @rrr
|
||||
amswap_db_d 0011 10000110 10011 ..... ..... ..... @rrr
|
||||
amadd_db_w 0011 10000110 10100 ..... ..... ..... @rrr
|
||||
amadd_db_d 0011 10000110 10101 ..... ..... ..... @rrr
|
||||
amand_db_w 0011 10000110 10110 ..... ..... ..... @rrr
|
||||
amand_db_d 0011 10000110 10111 ..... ..... ..... @rrr
|
||||
amor_db_w 0011 10000110 11000 ..... ..... ..... @rrr
|
||||
amor_db_d 0011 10000110 11001 ..... ..... ..... @rrr
|
||||
amxor_db_w 0011 10000110 11010 ..... ..... ..... @rrr
|
||||
amxor_db_d 0011 10000110 11011 ..... ..... ..... @rrr
|
||||
ammax_db_w 0011 10000110 11100 ..... ..... ..... @rrr
|
||||
ammax_db_d 0011 10000110 11101 ..... ..... ..... @rrr
|
||||
ammin_db_w 0011 10000110 11110 ..... ..... ..... @rrr
|
||||
ammin_db_d 0011 10000110 11111 ..... ..... ..... @rrr
|
||||
ammax_db_wu 0011 10000111 00000 ..... ..... ..... @rrr
|
||||
ammax_db_du 0011 10000111 00001 ..... ..... ..... @rrr
|
||||
ammin_db_wu 0011 10000111 00010 ..... ..... ..... @rrr
|
||||
ammin_db_du 0011 10000111 00011 ..... ..... ..... @rrr
|
||||
|
||||
#
|
||||
# Fixed point extra instruction
|
||||
#
|
||||
crc_w_b_w 0000 00000010 01000 ..... ..... ..... @rrr
|
||||
crc_w_h_w 0000 00000010 01001 ..... ..... ..... @rrr
|
||||
crc_w_w_w 0000 00000010 01010 ..... ..... ..... @rrr
|
||||
crc_w_d_w 0000 00000010 01011 ..... ..... ..... @rrr
|
||||
crcc_w_b_w 0000 00000010 01100 ..... ..... ..... @rrr
|
||||
crcc_w_h_w 0000 00000010 01101 ..... ..... ..... @rrr
|
||||
crcc_w_w_w 0000 00000010 01110 ..... ..... ..... @rrr
|
||||
crcc_w_d_w 0000 00000010 01111 ..... ..... ..... @rrr
|
||||
break 0000 00000010 10100 ............... @i15
|
||||
syscall 0000 00000010 10110 ............... @i15
|
||||
asrtle_d 0000 00000000 00010 ..... ..... 00000 @rr_jk
|
||||
asrtgt_d 0000 00000000 00011 ..... ..... 00000 @rr_jk
|
||||
rdtimel_w 0000 00000000 00000 11000 ..... ..... @rr
|
||||
rdtimeh_w 0000 00000000 00000 11001 ..... ..... @rr
|
||||
rdtime_d 0000 00000000 00000 11010 ..... ..... @rr
|
||||
cpucfg 0000 00000000 00000 11011 ..... ..... @rr
|
||||
|
||||
#
|
||||
# Floating point arithmetic operation instruction
|
||||
#
|
||||
fadd_s 0000 00010000 00001 ..... ..... ..... @fff
|
||||
fadd_d 0000 00010000 00010 ..... ..... ..... @fff
|
||||
fsub_s 0000 00010000 00101 ..... ..... ..... @fff
|
||||
fsub_d 0000 00010000 00110 ..... ..... ..... @fff
|
||||
fmul_s 0000 00010000 01001 ..... ..... ..... @fff
|
||||
fmul_d 0000 00010000 01010 ..... ..... ..... @fff
|
||||
fdiv_s 0000 00010000 01101 ..... ..... ..... @fff
|
||||
fdiv_d 0000 00010000 01110 ..... ..... ..... @fff
|
||||
fmadd_s 0000 10000001 ..... ..... ..... ..... @ffff
|
||||
fmadd_d 0000 10000010 ..... ..... ..... ..... @ffff
|
||||
fmsub_s 0000 10000101 ..... ..... ..... ..... @ffff
|
||||
fmsub_d 0000 10000110 ..... ..... ..... ..... @ffff
|
||||
fnmadd_s 0000 10001001 ..... ..... ..... ..... @ffff
|
||||
fnmadd_d 0000 10001010 ..... ..... ..... ..... @ffff
|
||||
fnmsub_s 0000 10001101 ..... ..... ..... ..... @ffff
|
||||
fnmsub_d 0000 10001110 ..... ..... ..... ..... @ffff
|
||||
fmax_s 0000 00010000 10001 ..... ..... ..... @fff
|
||||
fmax_d 0000 00010000 10010 ..... ..... ..... @fff
|
||||
fmin_s 0000 00010000 10101 ..... ..... ..... @fff
|
||||
fmin_d 0000 00010000 10110 ..... ..... ..... @fff
|
||||
fmaxa_s 0000 00010000 11001 ..... ..... ..... @fff
|
||||
fmaxa_d 0000 00010000 11010 ..... ..... ..... @fff
|
||||
fmina_s 0000 00010000 11101 ..... ..... ..... @fff
|
||||
fmina_d 0000 00010000 11110 ..... ..... ..... @fff
|
||||
fabs_s 0000 00010001 01000 00001 ..... ..... @ff
|
||||
fabs_d 0000 00010001 01000 00010 ..... ..... @ff
|
||||
fneg_s 0000 00010001 01000 00101 ..... ..... @ff
|
||||
fneg_d 0000 00010001 01000 00110 ..... ..... @ff
|
||||
fsqrt_s 0000 00010001 01000 10001 ..... ..... @ff
|
||||
fsqrt_d 0000 00010001 01000 10010 ..... ..... @ff
|
||||
frecip_s 0000 00010001 01000 10101 ..... ..... @ff
|
||||
frecip_d 0000 00010001 01000 10110 ..... ..... @ff
|
||||
frsqrt_s 0000 00010001 01000 11001 ..... ..... @ff
|
||||
frsqrt_d 0000 00010001 01000 11010 ..... ..... @ff
|
||||
fscaleb_s 0000 00010001 00001 ..... ..... ..... @fff
|
||||
fscaleb_d 0000 00010001 00010 ..... ..... ..... @fff
|
||||
flogb_s 0000 00010001 01000 01001 ..... ..... @ff
|
||||
flogb_d 0000 00010001 01000 01010 ..... ..... @ff
|
||||
fcopysign_s 0000 00010001 00101 ..... ..... ..... @fff
|
||||
fcopysign_d 0000 00010001 00110 ..... ..... ..... @fff
|
||||
fclass_s 0000 00010001 01000 01101 ..... ..... @ff
|
||||
fclass_d 0000 00010001 01000 01110 ..... ..... @ff
|
||||
|
||||
#
|
||||
# Floating point compare instruction
|
||||
#
|
||||
fcmp_cond_s 0000 11000001 ..... ..... ..... 00 ... @cff_fcond
|
||||
fcmp_cond_d 0000 11000010 ..... ..... ..... 00 ... @cff_fcond
|
||||
|
||||
#
|
||||
# Floating point conversion instruction
|
||||
#
|
||||
fcvt_s_d 0000 00010001 10010 00110 ..... ..... @ff
|
||||
fcvt_d_s 0000 00010001 10010 01001 ..... ..... @ff
|
||||
ftintrm_w_s 0000 00010001 10100 00001 ..... ..... @ff
|
||||
ftintrm_w_d 0000 00010001 10100 00010 ..... ..... @ff
|
||||
ftintrm_l_s 0000 00010001 10100 01001 ..... ..... @ff
|
||||
ftintrm_l_d 0000 00010001 10100 01010 ..... ..... @ff
|
||||
ftintrp_w_s 0000 00010001 10100 10001 ..... ..... @ff
|
||||
ftintrp_w_d 0000 00010001 10100 10010 ..... ..... @ff
|
||||
ftintrp_l_s 0000 00010001 10100 11001 ..... ..... @ff
|
||||
ftintrp_l_d 0000 00010001 10100 11010 ..... ..... @ff
|
||||
ftintrz_w_s 0000 00010001 10101 00001 ..... ..... @ff
|
||||
ftintrz_w_d 0000 00010001 10101 00010 ..... ..... @ff
|
||||
ftintrz_l_s 0000 00010001 10101 01001 ..... ..... @ff
|
||||
ftintrz_l_d 0000 00010001 10101 01010 ..... ..... @ff
|
||||
ftintrne_w_s 0000 00010001 10101 10001 ..... ..... @ff
|
||||
ftintrne_w_d 0000 00010001 10101 10010 ..... ..... @ff
|
||||
ftintrne_l_s 0000 00010001 10101 11001 ..... ..... @ff
|
||||
ftintrne_l_d 0000 00010001 10101 11010 ..... ..... @ff
|
||||
ftint_w_s 0000 00010001 10110 00001 ..... ..... @ff
|
||||
ftint_w_d 0000 00010001 10110 00010 ..... ..... @ff
|
||||
ftint_l_s 0000 00010001 10110 01001 ..... ..... @ff
|
||||
ftint_l_d 0000 00010001 10110 01010 ..... ..... @ff
|
||||
ffint_s_w 0000 00010001 11010 00100 ..... ..... @ff
|
||||
ffint_s_l 0000 00010001 11010 00110 ..... ..... @ff
|
||||
ffint_d_w 0000 00010001 11010 01000 ..... ..... @ff
|
||||
ffint_d_l 0000 00010001 11010 01010 ..... ..... @ff
|
||||
frint_s 0000 00010001 11100 10001 ..... ..... @ff
|
||||
frint_d 0000 00010001 11100 10010 ..... ..... @ff
|
||||
|
||||
#
|
||||
# Floating point move instruction
|
||||
#
|
||||
fmov_s 0000 00010001 01001 00101 ..... ..... @ff
|
||||
fmov_d 0000 00010001 01001 00110 ..... ..... @ff
|
||||
fsel 0000 11010000 00 ... ..... ..... ..... @fffc
|
||||
movgr2fr_w 0000 00010001 01001 01001 ..... ..... @fr
|
||||
movgr2fr_d 0000 00010001 01001 01010 ..... ..... @fr
|
||||
movgr2frh_w 0000 00010001 01001 01011 ..... ..... @fr
|
||||
movfr2gr_s 0000 00010001 01001 01101 ..... ..... @rf
|
||||
movfr2gr_d 0000 00010001 01001 01110 ..... ..... @rf
|
||||
movfrh2gr_s 0000 00010001 01001 01111 ..... ..... @rf
|
||||
movgr2fcsr 0000 00010001 01001 10000 ..... ..... @fcsrd_r
|
||||
movfcsr2gr 0000 00010001 01001 10010 ..... ..... @r_fcsrs
|
||||
movfr2cf 0000 00010001 01001 10100 ..... 00 ... @cf
|
||||
movcf2fr 0000 00010001 01001 10101 00 ... ..... @fc
|
||||
movgr2cf 0000 00010001 01001 10110 ..... 00 ... @cr
|
||||
movcf2gr 0000 00010001 01001 10111 00 ... ..... @rc
|
||||
|
||||
#
|
||||
# Floating point load/store instruction
|
||||
#
|
||||
fld_s 0010 101100 ............ ..... ..... @fr_i12
|
||||
fst_s 0010 101101 ............ ..... ..... @fr_i12
|
||||
fld_d 0010 101110 ............ ..... ..... @fr_i12
|
||||
fst_d 0010 101111 ............ ..... ..... @fr_i12
|
||||
fldx_s 0011 10000011 00000 ..... ..... ..... @frr
|
||||
fldx_d 0011 10000011 01000 ..... ..... ..... @frr
|
||||
fstx_s 0011 10000011 10000 ..... ..... ..... @frr
|
||||
fstx_d 0011 10000011 11000 ..... ..... ..... @frr
|
||||
fldgt_s 0011 10000111 01000 ..... ..... ..... @frr
|
||||
fldgt_d 0011 10000111 01001 ..... ..... ..... @frr
|
||||
fldle_s 0011 10000111 01010 ..... ..... ..... @frr
|
||||
fldle_d 0011 10000111 01011 ..... ..... ..... @frr
|
||||
fstgt_s 0011 10000111 01100 ..... ..... ..... @frr
|
||||
fstgt_d 0011 10000111 01101 ..... ..... ..... @frr
|
||||
fstle_s 0011 10000111 01110 ..... ..... ..... @frr
|
||||
fstle_d 0011 10000111 01111 ..... ..... ..... @frr
|
||||
|
||||
#
|
||||
# Branch instructions
|
||||
#
|
||||
beqz 0100 00 ................ ..... ..... @r_offs21
|
||||
bnez 0100 01 ................ ..... ..... @r_offs21
|
||||
bceqz 0100 10 ................ 00 ... ..... @c_offs21
|
||||
bcnez 0100 10 ................ 01 ... ..... @c_offs21
|
||||
jirl 0100 11 ................ ..... ..... @rr_offs16
|
||||
b 0101 00 .......................... @offs26
|
||||
bl 0101 01 .......................... @offs26
|
||||
beq 0101 10 ................ ..... ..... @rr_offs16
|
||||
bne 0101 11 ................ ..... ..... @rr_offs16
|
||||
blt 0110 00 ................ ..... ..... @rr_offs16
|
||||
bge 0110 01 ................ ..... ..... @rr_offs16
|
||||
bltu 0110 10 ................ ..... ..... @rr_offs16
|
||||
bgeu 0110 11 ................ ..... ..... @rr_offs16
|
||||
|
||||
#
|
||||
# Core instructions
|
||||
#
|
||||
{
|
||||
csrrd 0000 0100 .............. 00000 ..... @r_csr
|
||||
csrwr 0000 0100 .............. 00001 ..... @r_csr
|
||||
csrxchg 0000 0100 .............. ..... ..... @rr_csr
|
||||
}
|
||||
|
||||
iocsrrd_b 0000 01100100 10000 00000 ..... ..... @rr
|
||||
iocsrrd_h 0000 01100100 10000 00001 ..... ..... @rr
|
||||
iocsrrd_w 0000 01100100 10000 00010 ..... ..... @rr
|
||||
iocsrrd_d 0000 01100100 10000 00011 ..... ..... @rr
|
||||
iocsrwr_b 0000 01100100 10000 00100 ..... ..... @rr
|
||||
iocsrwr_h 0000 01100100 10000 00101 ..... ..... @rr
|
||||
iocsrwr_w 0000 01100100 10000 00110 ..... ..... @rr
|
||||
iocsrwr_d 0000 01100100 10000 00111 ..... ..... @rr
|
||||
tlbsrch 0000 01100100 10000 01010 00000 00000 @empty
|
||||
tlbrd 0000 01100100 10000 01011 00000 00000 @empty
|
||||
tlbwr 0000 01100100 10000 01100 00000 00000 @empty
|
||||
tlbfill 0000 01100100 10000 01101 00000 00000 @empty
|
||||
tlbclr 0000 01100100 10000 01000 00000 00000 @empty
|
||||
tlbflush 0000 01100100 10000 01001 00000 00000 @empty
|
||||
invtlb 0000 01100100 10011 ..... ..... ..... @i_rr
|
||||
cacop 0000 011000 ............ ..... ..... @cop_r_i
|
||||
lddir 0000 01100100 00 ........ ..... ..... @rr_ui8
|
||||
ldpte 0000 01100100 01 ........ ..... 00000 @j_i
|
||||
ertn 0000 01100100 10000 01110 00000 00000 @empty
|
||||
idle 0000 01100100 10001 ............... @i15
|
||||
dbcl 0000 00000010 10101 ............... @i15
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch CPU -- internal functions and types
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef LOONGARCH_INTERNALS_H
|
||||
#define LOONGARCH_INTERNALS_H
|
||||
|
||||
#define FCMP_LT 0b0001 /* fp0 < fp1 */
|
||||
#define FCMP_EQ 0b0010 /* fp0 = fp1 */
|
||||
#define FCMP_UN 0b0100 /* unordered */
|
||||
#define FCMP_GT 0b1000 /* fp0 > fp1 */
|
||||
|
||||
#define TARGET_PHYS_MASK MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS)
|
||||
#define TARGET_VIRT_MASK MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS)
|
||||
|
||||
/* Global bit used for lddir/ldpte */
|
||||
#define LOONGARCH_PAGE_HUGE_SHIFT 6
|
||||
/* Global bit for huge page */
|
||||
#define LOONGARCH_HGLOBAL_SHIFT 12
|
||||
|
||||
void loongarch_translate_init(void);
|
||||
|
||||
void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
|
||||
|
||||
void G_NORETURN do_raise_exception(CPULoongArchState *env,
|
||||
uint32_t exception,
|
||||
uintptr_t pc);
|
||||
|
||||
const char *loongarch_exception_name(int32_t exception);
|
||||
|
||||
void restore_fp_status(CPULoongArchState *env);
|
||||
|
||||
extern const VMStateDescription vmstate_loongarch_cpu;
|
||||
|
||||
void loongarch_cpu_set_irq(void *opaque, int irq, int level);
|
||||
|
||||
void loongarch_constant_timer_cb(void *opaque);
|
||||
uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
|
||||
uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
|
||||
void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
|
||||
uint64_t value);
|
||||
|
||||
bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
bool probe, uintptr_t retaddr);
|
||||
|
||||
hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
||||
|
||||
int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n);
|
||||
int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n);
|
||||
void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*
|
||||
* Helpers for IOCSR reads/writes
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "cpu.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "tcg/tcg-ldst.h"
|
||||
|
||||
uint64_t helper_iocsrrd_b(CPULoongArchState *env, target_ulong r_addr)
|
||||
{
|
||||
return address_space_ldub(&env->address_space_iocsr, r_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
uint64_t helper_iocsrrd_h(CPULoongArchState *env, target_ulong r_addr)
|
||||
{
|
||||
return address_space_lduw(&env->address_space_iocsr, r_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
uint64_t helper_iocsrrd_w(CPULoongArchState *env, target_ulong r_addr)
|
||||
{
|
||||
return address_space_ldl(&env->address_space_iocsr, r_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
uint64_t helper_iocsrrd_d(CPULoongArchState *env, target_ulong r_addr)
|
||||
{
|
||||
return address_space_ldq(&env->address_space_iocsr, r_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
void helper_iocsrwr_b(CPULoongArchState *env, target_ulong w_addr,
|
||||
target_ulong val)
|
||||
{
|
||||
address_space_stb(&env->address_space_iocsr, w_addr,
|
||||
val, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
void helper_iocsrwr_h(CPULoongArchState *env, target_ulong w_addr,
|
||||
target_ulong val)
|
||||
{
|
||||
address_space_stw(&env->address_space_iocsr, w_addr,
|
||||
val, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
void helper_iocsrwr_w(CPULoongArchState *env, target_ulong w_addr,
|
||||
target_ulong val)
|
||||
{
|
||||
address_space_stl(&env->address_space_iocsr, w_addr,
|
||||
val, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
||||
|
||||
void helper_iocsrwr_d(CPULoongArchState *env, target_ulong w_addr,
|
||||
target_ulong val)
|
||||
{
|
||||
address_space_stq(&env->address_space_iocsr, w_addr,
|
||||
val, MEMTXATTRS_UNSPECIFIED, NULL);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch Machine State
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "migration/cpu.h"
|
||||
#include "internals.h"
|
||||
|
||||
/* TLB state */
|
||||
const VMStateDescription vmstate_tlb = {
|
||||
.name = "cpu/tlb",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(tlb_misc, LoongArchTLB),
|
||||
VMSTATE_UINT64(tlb_entry0, LoongArchTLB),
|
||||
VMSTATE_UINT64(tlb_entry1, LoongArchTLB),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
/* LoongArch CPU state */
|
||||
|
||||
const VMStateDescription vmstate_loongarch_cpu = {
|
||||
.name = "cpu",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
|
||||
VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32),
|
||||
VMSTATE_UINTTL(env.pc, LoongArchCPU),
|
||||
VMSTATE_UINT64_ARRAY(env.fpr, LoongArchCPU, 32),
|
||||
VMSTATE_UINT32(env.fcsr0, LoongArchCPU),
|
||||
VMSTATE_BOOL_ARRAY(env.cf, LoongArchCPU, 8),
|
||||
|
||||
/* Remaining CSRs */
|
||||
VMSTATE_UINT64(env.CSR_CRMD, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PRMD, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_EUEN, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_MISC, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_ECFG, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_ESTAT, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_ERA, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_BADV, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_BADI, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_EENTRY, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBIDX, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBEHI, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBELO0, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBELO1, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_ASID, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PGDL, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PGDH, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PGD, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PWCL, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PWCH, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_STLBPS, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_RVACFG, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PRCFG1, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PRCFG2, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_PRCFG3, LoongArchCPU),
|
||||
VMSTATE_UINT64_ARRAY(env.CSR_SAVE, LoongArchCPU, 16),
|
||||
VMSTATE_UINT64(env.CSR_TID, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TCFG, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TVAL, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_CNTC, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TICLR, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_LLBCTL, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_IMPCTL1, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_IMPCTL2, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBRENTRY, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBRBADV, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBRERA, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBRSAVE, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBRELO0, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBRELO1, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBREHI, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_TLBRPRMD, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_MERRCTL, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_MERRINFO1, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_MERRINFO2, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_MERRENTRY, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_MERRERA, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_MERRSAVE, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_CTAG, LoongArchCPU),
|
||||
VMSTATE_UINT64_ARRAY(env.CSR_DMW, LoongArchCPU, 4),
|
||||
|
||||
/* Debug CSRs */
|
||||
VMSTATE_UINT64(env.CSR_DBG, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU),
|
||||
VMSTATE_UINT64(env.CSR_DSAVE, LoongArchCPU),
|
||||
/* TLB */
|
||||
VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX,
|
||||
0, vmstate_tlb, LoongArchTLB),
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
gen = decodetree.process('insns.decode')
|
||||
|
||||
loongarch_ss = ss.source_set()
|
||||
loongarch_ss.add(files(
|
||||
'cpu.c',
|
||||
'disas.c',
|
||||
))
|
||||
loongarch_tcg_ss = ss.source_set()
|
||||
loongarch_tcg_ss.add(gen)
|
||||
loongarch_tcg_ss.add(files(
|
||||
'fpu_helper.c',
|
||||
'op_helper.c',
|
||||
'translate.c',
|
||||
'gdbstub.c',
|
||||
))
|
||||
loongarch_tcg_ss.add(zlib)
|
||||
|
||||
loongarch_softmmu_ss = ss.source_set()
|
||||
loongarch_softmmu_ss.add(files(
|
||||
'machine.c',
|
||||
'tlb_helper.c',
|
||||
'constant_timer.c',
|
||||
'csr_helper.c',
|
||||
'iocsr_helper.c',
|
||||
))
|
||||
|
||||
loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
|
||||
|
||||
target_arch += {'loongarch': loongarch_ss}
|
||||
target_softmmu_arch += {'loongarch': loongarch_softmmu_ss}
|
|
@ -0,0 +1,133 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch emulation helpers for QEMU.
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "cpu.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "internals.h"
|
||||
#include "qemu/crc32c.h"
|
||||
#include <zlib.h>
|
||||
#include "cpu-csr.h"
|
||||
|
||||
/* Exceptions helpers */
|
||||
void helper_raise_exception(CPULoongArchState *env, uint32_t exception)
|
||||
{
|
||||
do_raise_exception(env, exception, GETPC());
|
||||
}
|
||||
|
||||
target_ulong helper_bitrev_w(target_ulong rj)
|
||||
{
|
||||
return (int32_t)revbit32(rj);
|
||||
}
|
||||
|
||||
target_ulong helper_bitrev_d(target_ulong rj)
|
||||
{
|
||||
return revbit64(rj);
|
||||
}
|
||||
|
||||
target_ulong helper_bitswap(target_ulong v)
|
||||
{
|
||||
v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) |
|
||||
((v & (target_ulong)0x5555555555555555ULL) << 1);
|
||||
v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) |
|
||||
((v & (target_ulong)0x3333333333333333ULL) << 2);
|
||||
v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) |
|
||||
((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4);
|
||||
return v;
|
||||
}
|
||||
|
||||
/* loongarch assert op */
|
||||
void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk)
|
||||
{
|
||||
if (rj > rk) {
|
||||
do_raise_exception(env, EXCCODE_ADEM, GETPC());
|
||||
}
|
||||
}
|
||||
|
||||
void helper_asrtgt_d(CPULoongArchState *env, target_ulong rj, target_ulong rk)
|
||||
{
|
||||
if (rj <= rk) {
|
||||
do_raise_exception(env, EXCCODE_ADEM, GETPC());
|
||||
}
|
||||
}
|
||||
|
||||
target_ulong helper_crc32(target_ulong val, target_ulong m, uint64_t sz)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1);
|
||||
|
||||
m &= mask;
|
||||
stq_le_p(buf, m);
|
||||
return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff);
|
||||
}
|
||||
|
||||
target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1);
|
||||
m &= mask;
|
||||
stq_le_p(buf, m);
|
||||
return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff);
|
||||
}
|
||||
|
||||
target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
|
||||
{
|
||||
return rj > 21 ? 0 : env->cpucfg[rj];
|
||||
}
|
||||
|
||||
uint64_t helper_rdtime_d(CPULoongArchState *env)
|
||||
{
|
||||
uint64_t plv;
|
||||
LoongArchCPU *cpu = env_archcpu(env);
|
||||
|
||||
plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
|
||||
if (extract64(env->CSR_MISC, R_CSR_MISC_DRDTL_SHIFT + plv, 1)) {
|
||||
do_raise_exception(env, EXCCODE_IPE, GETPC());
|
||||
}
|
||||
|
||||
return cpu_loongarch_get_constant_timer_counter(cpu);
|
||||
}
|
||||
|
||||
void helper_ertn(CPULoongArchState *env)
|
||||
{
|
||||
uint64_t csr_pplv, csr_pie;
|
||||
if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
|
||||
csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV);
|
||||
csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE);
|
||||
|
||||
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 0);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 1);
|
||||
env->pc = env->CSR_TLBRERA;
|
||||
qemu_log_mask(CPU_LOG_INT, "%s: TLBRERA " TARGET_FMT_lx "\n",
|
||||
__func__, env->CSR_TLBRERA);
|
||||
} else {
|
||||
csr_pplv = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV);
|
||||
csr_pie = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE);
|
||||
|
||||
env->pc = env->CSR_ERA;
|
||||
qemu_log_mask(CPU_LOG_INT, "%s: ERA " TARGET_FMT_lx "\n",
|
||||
__func__, env->CSR_ERA);
|
||||
}
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv);
|
||||
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie);
|
||||
|
||||
env->lladdr = 1;
|
||||
}
|
||||
|
||||
void helper_idle(CPULoongArchState *env)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
cs->halted = 1;
|
||||
do_raise_exception(env, EXCP_HLT, 0);
|
||||
}
|
|
@ -0,0 +1,763 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* QEMU LoongArch TLB helpers
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/guest-random.h"
|
||||
|
||||
#include "cpu.h"
|
||||
#include "internals.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/log.h"
|
||||
#include "cpu-csr.h"
|
||||
|
||||
enum {
|
||||
TLBRET_MATCH = 0,
|
||||
TLBRET_BADADDR = 1,
|
||||
TLBRET_NOMATCH = 2,
|
||||
TLBRET_INVALID = 3,
|
||||
TLBRET_DIRTY = 4,
|
||||
TLBRET_RI = 5,
|
||||
TLBRET_XI = 6,
|
||||
TLBRET_PE = 7,
|
||||
};
|
||||
|
||||
static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
|
||||
int *prot, target_ulong address,
|
||||
int access_type, int index, int mmu_idx)
|
||||
{
|
||||
LoongArchTLB *tlb = &env->tlb[index];
|
||||
uint64_t plv = mmu_idx;
|
||||
uint64_t tlb_entry, tlb_ppn;
|
||||
uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
|
||||
|
||||
if (index >= LOONGARCH_STLB) {
|
||||
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
|
||||
} else {
|
||||
tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
|
||||
}
|
||||
n = (address >> tlb_ps) & 0x1;/* Odd or even */
|
||||
|
||||
tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
|
||||
tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V);
|
||||
tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D);
|
||||
tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
|
||||
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY, PPN);
|
||||
tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY, NX);
|
||||
tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY, NR);
|
||||
tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY, RPLV);
|
||||
|
||||
/* Check access rights */
|
||||
if (!tlb_v) {
|
||||
return TLBRET_INVALID;
|
||||
}
|
||||
|
||||
if (access_type == MMU_INST_FETCH && tlb_nx) {
|
||||
return TLBRET_XI;
|
||||
}
|
||||
|
||||
if (access_type == MMU_DATA_LOAD && tlb_nr) {
|
||||
return TLBRET_RI;
|
||||
}
|
||||
|
||||
if (((tlb_rplv == 0) && (plv > tlb_plv)) ||
|
||||
((tlb_rplv == 1) && (plv != tlb_plv))) {
|
||||
return TLBRET_PE;
|
||||
}
|
||||
|
||||
if ((access_type == MMU_DATA_STORE) && !tlb_d) {
|
||||
return TLBRET_DIRTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* tlb_entry contains ppn[47:12] while 16KiB ppn is [47:15]
|
||||
* need adjust.
|
||||
*/
|
||||
*physical = (tlb_ppn << R_TLBENTRY_PPN_SHIFT) |
|
||||
(address & MAKE_64BIT_MASK(0, tlb_ps));
|
||||
*prot = PAGE_READ;
|
||||
if (tlb_d) {
|
||||
*prot |= PAGE_WRITE;
|
||||
}
|
||||
if (!tlb_nx) {
|
||||
*prot |= PAGE_EXEC;
|
||||
}
|
||||
return TLBRET_MATCH;
|
||||
}
|
||||
|
||||
/*
|
||||
* One tlb entry holds an adjacent odd/even pair, the vpn is the
|
||||
* content of the virtual page number divided by 2. So the
|
||||
* compare vpn is bit[47:15] for 16KiB page. while the vppn
|
||||
* field in tlb entry contains bit[47:13], so need adjust.
|
||||
* virt_vpn = vaddr[47:13]
|
||||
*/
|
||||
static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr,
|
||||
int *index)
|
||||
{
|
||||
LoongArchTLB *tlb;
|
||||
uint16_t csr_asid, tlb_asid, stlb_idx;
|
||||
uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps;
|
||||
int i, compare_shift;
|
||||
uint64_t vpn, tlb_vppn;
|
||||
|
||||
csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
|
||||
stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
|
||||
vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1);
|
||||
stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */
|
||||
compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
|
||||
|
||||
/* Search STLB */
|
||||
for (i = 0; i < 8; ++i) {
|
||||
tlb = &env->tlb[i * 256 + stlb_idx];
|
||||
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
|
||||
if (tlb_e) {
|
||||
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
|
||||
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
|
||||
if ((tlb_g == 1 || tlb_asid == csr_asid) &&
|
||||
(vpn == (tlb_vppn >> compare_shift))) {
|
||||
*index = i * 256 + stlb_idx;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Search MTLB */
|
||||
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) {
|
||||
tlb = &env->tlb[i];
|
||||
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
|
||||
if (tlb_e) {
|
||||
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
|
||||
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
|
||||
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
|
||||
vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
|
||||
if ((tlb_g == 1 || tlb_asid == csr_asid) &&
|
||||
(vpn == (tlb_vppn >> compare_shift))) {
|
||||
*index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
|
||||
int *prot, target_ulong address,
|
||||
MMUAccessType access_type, int mmu_idx)
|
||||
{
|
||||
int index, match;
|
||||
|
||||
match = loongarch_tlb_search(env, address, &index);
|
||||
if (match) {
|
||||
return loongarch_map_tlb_entry(env, physical, prot,
|
||||
address, access_type, index, mmu_idx);
|
||||
}
|
||||
|
||||
return TLBRET_NOMATCH;
|
||||
}
|
||||
|
||||
static int get_physical_address(CPULoongArchState *env, hwaddr *physical,
|
||||
int *prot, target_ulong address,
|
||||
MMUAccessType access_type, int mmu_idx)
|
||||
{
|
||||
int user_mode = mmu_idx == MMU_USER_IDX;
|
||||
int kernel_mode = mmu_idx == MMU_KERNEL_IDX;
|
||||
uint32_t plv, base_c, base_v;
|
||||
int64_t addr_high;
|
||||
uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA);
|
||||
uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
|
||||
|
||||
/* Check PG and DA */
|
||||
if (da & !pg) {
|
||||
*physical = address & TARGET_PHYS_MASK;
|
||||
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
return TLBRET_MATCH;
|
||||
}
|
||||
|
||||
plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT);
|
||||
base_v = address >> TARGET_VIRT_ADDR_SPACE_BITS;
|
||||
/* Check direct map window */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
base_c = env->CSR_DMW[i] >> TARGET_VIRT_ADDR_SPACE_BITS;
|
||||
if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) {
|
||||
*physical = dmw_va2pa(address);
|
||||
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
return TLBRET_MATCH;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check valid extension */
|
||||
addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16);
|
||||
if (!(addr_high == 0 || addr_high == -1)) {
|
||||
return TLBRET_BADADDR;
|
||||
}
|
||||
|
||||
/* Mapped address */
|
||||
return loongarch_map_address(env, physical, prot, address,
|
||||
access_type, mmu_idx);
|
||||
}
|
||||
|
||||
hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
hwaddr phys_addr;
|
||||
int prot;
|
||||
|
||||
if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
|
||||
cpu_mmu_index(env, false)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return phys_addr;
|
||||
}
|
||||
|
||||
static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
|
||||
MMUAccessType access_type, int tlb_error)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
switch (tlb_error) {
|
||||
default:
|
||||
case TLBRET_BADADDR:
|
||||
cs->exception_index = EXCCODE_ADEM;
|
||||
break;
|
||||
case TLBRET_NOMATCH:
|
||||
/* No TLB match for a mapped address */
|
||||
if (access_type == MMU_DATA_LOAD) {
|
||||
cs->exception_index = EXCCODE_PIL;
|
||||
} else if (access_type == MMU_DATA_STORE) {
|
||||
cs->exception_index = EXCCODE_PIS;
|
||||
} else if (access_type == MMU_INST_FETCH) {
|
||||
cs->exception_index = EXCCODE_PIF;
|
||||
}
|
||||
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1);
|
||||
break;
|
||||
case TLBRET_INVALID:
|
||||
/* TLB match with no valid bit */
|
||||
if (access_type == MMU_DATA_LOAD) {
|
||||
cs->exception_index = EXCCODE_PIL;
|
||||
} else if (access_type == MMU_DATA_STORE) {
|
||||
cs->exception_index = EXCCODE_PIS;
|
||||
} else if (access_type == MMU_INST_FETCH) {
|
||||
cs->exception_index = EXCCODE_PIF;
|
||||
}
|
||||
break;
|
||||
case TLBRET_DIRTY:
|
||||
/* TLB match but 'D' bit is cleared */
|
||||
cs->exception_index = EXCCODE_PME;
|
||||
break;
|
||||
case TLBRET_XI:
|
||||
/* Execute-Inhibit Exception */
|
||||
cs->exception_index = EXCCODE_PNX;
|
||||
break;
|
||||
case TLBRET_RI:
|
||||
/* Read-Inhibit Exception */
|
||||
cs->exception_index = EXCCODE_PNR;
|
||||
break;
|
||||
case TLBRET_PE:
|
||||
/* Privileged Exception */
|
||||
cs->exception_index = EXCCODE_PPI;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tlb_error == TLBRET_NOMATCH) {
|
||||
env->CSR_TLBRBADV = address;
|
||||
env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN,
|
||||
extract64(address, 13, 35));
|
||||
} else {
|
||||
if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
|
||||
env->CSR_BADV = address;
|
||||
}
|
||||
env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void invalidate_tlb_entry(CPULoongArchState *env, int index)
|
||||
{
|
||||
target_ulong addr, mask, pagesize;
|
||||
uint8_t tlb_ps;
|
||||
LoongArchTLB *tlb = &env->tlb[index];
|
||||
|
||||
int mmu_idx = cpu_mmu_index(env, false);
|
||||
uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V);
|
||||
uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V);
|
||||
uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
|
||||
|
||||
if (index >= LOONGARCH_STLB) {
|
||||
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
|
||||
} else {
|
||||
tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
|
||||
}
|
||||
pagesize = 1 << tlb_ps;
|
||||
mask = MAKE_64BIT_MASK(0, tlb_ps + 1);
|
||||
|
||||
if (tlb_v0) {
|
||||
addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */
|
||||
tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
|
||||
mmu_idx, TARGET_LONG_BITS);
|
||||
}
|
||||
|
||||
if (tlb_v1) {
|
||||
addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */
|
||||
tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
|
||||
mmu_idx, TARGET_LONG_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
static void invalidate_tlb(CPULoongArchState *env, int index)
|
||||
{
|
||||
LoongArchTLB *tlb;
|
||||
uint16_t csr_asid, tlb_asid, tlb_g;
|
||||
|
||||
csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
|
||||
tlb = &env->tlb[index];
|
||||
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
if (tlb_g == 0 && tlb_asid != csr_asid) {
|
||||
return;
|
||||
}
|
||||
invalidate_tlb_entry(env, index);
|
||||
}
|
||||
|
||||
static void fill_tlb_entry(CPULoongArchState *env, int index)
|
||||
{
|
||||
LoongArchTLB *tlb = &env->tlb[index];
|
||||
uint64_t lo0, lo1, csr_vppn;
|
||||
uint16_t csr_asid;
|
||||
uint8_t csr_ps;
|
||||
|
||||
if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
|
||||
csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
|
||||
csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN);
|
||||
lo0 = env->CSR_TLBRELO0;
|
||||
lo1 = env->CSR_TLBRELO1;
|
||||
} else {
|
||||
csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
|
||||
csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI, VPPN);
|
||||
lo0 = env->CSR_TLBELO0;
|
||||
lo1 = env->CSR_TLBELO1;
|
||||
}
|
||||
|
||||
if (csr_ps == 0) {
|
||||
qemu_log_mask(CPU_LOG_MMU, "page size is 0\n");
|
||||
}
|
||||
|
||||
/* Only MTLB has the ps fields */
|
||||
if (index >= LOONGARCH_STLB) {
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
|
||||
}
|
||||
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
|
||||
csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
|
||||
|
||||
tlb->tlb_entry0 = lo0;
|
||||
tlb->tlb_entry1 = lo1;
|
||||
}
|
||||
|
||||
/* Return an random value between low and high */
|
||||
static uint32_t get_random_tlb(uint32_t low, uint32_t high)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
qemu_guest_getrandom_nofail(&val, sizeof(val));
|
||||
return val % (high - low + 1) + low;
|
||||
}
|
||||
|
||||
void helper_tlbsrch(CPULoongArchState *env)
|
||||
{
|
||||
int index, match;
|
||||
|
||||
if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
|
||||
match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index);
|
||||
} else {
|
||||
match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index);
|
||||
env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
|
||||
}
|
||||
|
||||
void helper_tlbrd(CPULoongArchState *env)
|
||||
{
|
||||
LoongArchTLB *tlb;
|
||||
int index;
|
||||
uint8_t tlb_ps, tlb_e;
|
||||
|
||||
index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
|
||||
tlb = &env->tlb[index];
|
||||
|
||||
if (index >= LOONGARCH_STLB) {
|
||||
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
|
||||
} else {
|
||||
tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
|
||||
}
|
||||
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
|
||||
|
||||
if (!tlb_e) {
|
||||
/* Invalid TLB entry */
|
||||
env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
|
||||
env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0);
|
||||
env->CSR_TLBEHI = 0;
|
||||
env->CSR_TLBELO0 = 0;
|
||||
env->CSR_TLBELO1 = 0;
|
||||
env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0);
|
||||
} else {
|
||||
/* Valid TLB entry */
|
||||
env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
|
||||
env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
|
||||
PS, (tlb_ps & 0x3f));
|
||||
env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) <<
|
||||
R_TLB_MISC_VPPN_SHIFT;
|
||||
env->CSR_TLBELO0 = tlb->tlb_entry0;
|
||||
env->CSR_TLBELO1 = tlb->tlb_entry1;
|
||||
}
|
||||
}
|
||||
|
||||
void helper_tlbwr(CPULoongArchState *env)
|
||||
{
|
||||
int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
|
||||
|
||||
invalidate_tlb(env, index);
|
||||
|
||||
if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) {
|
||||
env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc,
|
||||
TLB_MISC, E, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
fill_tlb_entry(env, index);
|
||||
}
|
||||
|
||||
void helper_tlbfill(CPULoongArchState *env)
|
||||
{
|
||||
uint64_t address, entryhi;
|
||||
int index, set, stlb_idx;
|
||||
uint16_t pagesize, stlb_ps;
|
||||
|
||||
if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
|
||||
entryhi = env->CSR_TLBREHI;
|
||||
pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
|
||||
} else {
|
||||
entryhi = env->CSR_TLBEHI;
|
||||
pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
|
||||
}
|
||||
|
||||
stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
|
||||
|
||||
if (pagesize == stlb_ps) {
|
||||
/* Only write into STLB bits [47:13] */
|
||||
address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_VPPN_SHIFT);
|
||||
|
||||
/* Choose one set ramdomly */
|
||||
set = get_random_tlb(0, 7);
|
||||
|
||||
/* Index in one set */
|
||||
stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */
|
||||
|
||||
index = set * 256 + stlb_idx;
|
||||
} else {
|
||||
/* Only write into MTLB */
|
||||
index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1);
|
||||
}
|
||||
|
||||
invalidate_tlb(env, index);
|
||||
fill_tlb_entry(env, index);
|
||||
}
|
||||
|
||||
void helper_tlbclr(CPULoongArchState *env)
|
||||
{
|
||||
LoongArchTLB *tlb;
|
||||
int i, index;
|
||||
uint16_t csr_asid, tlb_asid, tlb_g;
|
||||
|
||||
csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
|
||||
index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
|
||||
|
||||
if (index < LOONGARCH_STLB) {
|
||||
/* STLB. One line per operation */
|
||||
for (i = 0; i < 8; i++) {
|
||||
tlb = &env->tlb[i * 256 + (index % 256)];
|
||||
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
if (!tlb_g && tlb_asid == csr_asid) {
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
|
||||
}
|
||||
}
|
||||
} else if (index < LOONGARCH_TLB_MAX) {
|
||||
/* All MTLB entries */
|
||||
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
|
||||
tlb = &env->tlb[i];
|
||||
tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
if (!tlb_g && tlb_asid == csr_asid) {
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
|
||||
void helper_tlbflush(CPULoongArchState *env)
|
||||
{
|
||||
int i, index;
|
||||
|
||||
index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
|
||||
|
||||
if (index < LOONGARCH_STLB) {
|
||||
/* STLB. One line per operation */
|
||||
for (i = 0; i < 8; i++) {
|
||||
int s_idx = i * 256 + (index % 256);
|
||||
env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc,
|
||||
TLB_MISC, E, 0);
|
||||
}
|
||||
} else if (index < LOONGARCH_TLB_MAX) {
|
||||
/* All MTLB entries */
|
||||
for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
|
||||
env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
|
||||
TLB_MISC, E, 0);
|
||||
}
|
||||
}
|
||||
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
|
||||
void helper_invtlb_all(CPULoongArchState *env)
|
||||
{
|
||||
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
|
||||
env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
|
||||
TLB_MISC, E, 0);
|
||||
}
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
|
||||
void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g)
|
||||
{
|
||||
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
|
||||
LoongArchTLB *tlb = &env->tlb[i];
|
||||
uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
|
||||
if (tlb_g == g) {
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
|
||||
}
|
||||
}
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
|
||||
void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
|
||||
{
|
||||
uint16_t asid = info & R_CSR_ASID_ASID_MASK;
|
||||
|
||||
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
|
||||
LoongArchTLB *tlb = &env->tlb[i];
|
||||
uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
|
||||
if (!tlb_g && (tlb_asid == asid)) {
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
|
||||
}
|
||||
}
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
|
||||
void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info,
|
||||
target_ulong addr)
|
||||
{
|
||||
uint16_t asid = info & 0x3ff;
|
||||
|
||||
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
|
||||
LoongArchTLB *tlb = &env->tlb[i];
|
||||
uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
uint64_t vpn, tlb_vppn;
|
||||
uint8_t tlb_ps, compare_shift;
|
||||
|
||||
if (i >= LOONGARCH_STLB) {
|
||||
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
|
||||
} else {
|
||||
tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
|
||||
}
|
||||
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
|
||||
vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
|
||||
compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
|
||||
|
||||
if (!tlb_g && (tlb_asid == asid) &&
|
||||
(vpn == (tlb_vppn >> compare_shift))) {
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
|
||||
}
|
||||
}
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
|
||||
void helper_invtlb_page_asid_or_g(CPULoongArchState *env,
|
||||
target_ulong info, target_ulong addr)
|
||||
{
|
||||
uint16_t asid = info & 0x3ff;
|
||||
|
||||
for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
|
||||
LoongArchTLB *tlb = &env->tlb[i];
|
||||
uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
|
||||
uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
|
||||
uint64_t vpn, tlb_vppn;
|
||||
uint8_t tlb_ps, compare_shift;
|
||||
|
||||
if (i >= LOONGARCH_STLB) {
|
||||
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
|
||||
} else {
|
||||
tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
|
||||
}
|
||||
tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
|
||||
vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
|
||||
compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
|
||||
|
||||
if ((tlb_g || (tlb_asid == asid)) &&
|
||||
(vpn == (tlb_vppn >> compare_shift))) {
|
||||
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
|
||||
}
|
||||
}
|
||||
tlb_flush(env_cpu(env));
|
||||
}
|
||||
|
||||
bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
bool probe, uintptr_t retaddr)
|
||||
{
|
||||
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
|
||||
CPULoongArchState *env = &cpu->env;
|
||||
hwaddr physical;
|
||||
int prot;
|
||||
int ret = TLBRET_BADADDR;
|
||||
|
||||
/* Data access */
|
||||
ret = get_physical_address(env, &physical, &prot, address,
|
||||
access_type, mmu_idx);
|
||||
|
||||
if (ret == TLBRET_MATCH) {
|
||||
tlb_set_page(cs, address & TARGET_PAGE_MASK,
|
||||
physical & TARGET_PAGE_MASK, prot,
|
||||
mmu_idx, TARGET_PAGE_SIZE);
|
||||
qemu_log_mask(CPU_LOG_MMU,
|
||||
"%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx
|
||||
" prot %d\n", __func__, address, physical, prot);
|
||||
return true;
|
||||
} else {
|
||||
qemu_log_mask(CPU_LOG_MMU,
|
||||
"%s address=%" VADDR_PRIx " ret %d\n", __func__, address,
|
||||
ret);
|
||||
}
|
||||
if (probe) {
|
||||
return false;
|
||||
}
|
||||
raise_mmu_exception(env, address, access_type, ret);
|
||||
cpu_loop_exit_restore(cs, retaddr);
|
||||
}
|
||||
|
||||
target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
|
||||
target_ulong level, uint32_t mem_idx)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
target_ulong badvaddr, index, phys, ret;
|
||||
int shift;
|
||||
uint64_t dir_base, dir_width;
|
||||
bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
|
||||
|
||||
badvaddr = env->CSR_TLBRBADV;
|
||||
base = base & TARGET_PHYS_MASK;
|
||||
|
||||
/* 0:64bit, 1:128bit, 2:192bit, 3:256bit */
|
||||
shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
|
||||
shift = (shift + 1) * 3;
|
||||
|
||||
if (huge) {
|
||||
return base;
|
||||
}
|
||||
switch (level) {
|
||||
case 1:
|
||||
dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
|
||||
dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
|
||||
break;
|
||||
case 2:
|
||||
dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE);
|
||||
dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH);
|
||||
break;
|
||||
case 3:
|
||||
dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
|
||||
dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
|
||||
break;
|
||||
case 4:
|
||||
dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE);
|
||||
dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH);
|
||||
break;
|
||||
default:
|
||||
do_raise_exception(env, EXCCODE_INE, GETPC());
|
||||
return 0;
|
||||
}
|
||||
index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
|
||||
phys = base | index << shift;
|
||||
ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
|
||||
uint32_t mem_idx)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
|
||||
int shift;
|
||||
bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
|
||||
uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
|
||||
uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
|
||||
|
||||
base = base & TARGET_PHYS_MASK;
|
||||
|
||||
if (huge) {
|
||||
/* Huge Page. base is paddr */
|
||||
tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT);
|
||||
/* Move Global bit */
|
||||
tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >>
|
||||
LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT |
|
||||
(tmp0 & (~(1 << R_TLBENTRY_G_SHIFT)));
|
||||
ps = ptbase + ptwidth - 1;
|
||||
if (odd) {
|
||||
tmp0 += (1 << ps);
|
||||
}
|
||||
} else {
|
||||
/* 0:64bit, 1:128bit, 2:192bit, 3:256bit */
|
||||
shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
|
||||
shift = (shift + 1) * 3;
|
||||
badv = env->CSR_TLBRBADV;
|
||||
|
||||
ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
|
||||
ptindex = ptindex & ~0x1; /* clear bit 0 */
|
||||
ptoffset0 = ptindex << shift;
|
||||
ptoffset1 = (ptindex + 1) << shift;
|
||||
|
||||
phys = base | (odd ? ptoffset1 : ptoffset0);
|
||||
tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
|
||||
ps = ptbase;
|
||||
}
|
||||
|
||||
if (odd) {
|
||||
env->CSR_TLBRELO1 = tmp0;
|
||||
} else {
|
||||
env->CSR_TLBRELO0 = tmp0;
|
||||
}
|
||||
env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch emulation for QEMU - main translation routines.
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/helper-gen.h"
|
||||
|
||||
#include "exec/translator.h"
|
||||
#include "exec/log.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "fpu/softfloat.h"
|
||||
#include "translate.h"
|
||||
#include "internals.h"
|
||||
|
||||
/* Global register indices */
|
||||
TCGv cpu_gpr[32], cpu_pc;
|
||||
static TCGv cpu_lladdr, cpu_llval;
|
||||
TCGv_i32 cpu_fcsr0;
|
||||
TCGv_i64 cpu_fpr[32];
|
||||
|
||||
#include "exec/gen-icount.h"
|
||||
|
||||
#define DISAS_STOP DISAS_TARGET_0
|
||||
#define DISAS_EXIT DISAS_TARGET_1
|
||||
#define DISAS_EXIT_UPDATE DISAS_TARGET_2
|
||||
|
||||
static inline int plus_1(DisasContext *ctx, int x)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
static inline int shl_2(DisasContext *ctx, int x)
|
||||
{
|
||||
return x << 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* LoongArch the upper 32 bits are undefined ("can be any value").
|
||||
* QEMU chooses to nanbox, because it is most likely to show guest bugs early.
|
||||
*/
|
||||
static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in)
|
||||
{
|
||||
tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32));
|
||||
}
|
||||
|
||||
void generate_exception(DisasContext *ctx, int excp)
|
||||
{
|
||||
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
|
||||
gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp));
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
}
|
||||
|
||||
static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||
{
|
||||
if (translator_use_goto_tb(&ctx->base, dest)) {
|
||||
tcg_gen_goto_tb(n);
|
||||
tcg_gen_movi_tl(cpu_pc, dest);
|
||||
tcg_gen_exit_tb(ctx->base.tb, n);
|
||||
} else {
|
||||
tcg_gen_movi_tl(cpu_pc, dest);
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
static void loongarch_tr_init_disas_context(DisasContextBase *dcbase,
|
||||
CPUState *cs)
|
||||
{
|
||||
int64_t bound;
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
|
||||
ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK;
|
||||
ctx->mem_idx = ctx->base.tb->flags;
|
||||
|
||||
/* Bound the number of insns to execute to those left on the page. */
|
||||
bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
|
||||
ctx->base.max_insns = MIN(ctx->base.max_insns, bound);
|
||||
|
||||
ctx->ntemp = 0;
|
||||
memset(ctx->temp, 0, sizeof(ctx->temp));
|
||||
|
||||
ctx->zero = tcg_constant_tl(0);
|
||||
}
|
||||
|
||||
static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
}
|
||||
|
||||
static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
|
||||
tcg_gen_insn_start(ctx->base.pc_next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrappers for getting reg values.
|
||||
*
|
||||
* The $zero register does not have cpu_gpr[0] allocated -- we supply the
|
||||
* constant zero as a source, and an uninitialized sink as destination.
|
||||
*
|
||||
* Further, we may provide an extension for word operations.
|
||||
*/
|
||||
static TCGv temp_new(DisasContext *ctx)
|
||||
{
|
||||
assert(ctx->ntemp < ARRAY_SIZE(ctx->temp));
|
||||
return ctx->temp[ctx->ntemp++] = tcg_temp_new();
|
||||
}
|
||||
|
||||
static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext)
|
||||
{
|
||||
TCGv t;
|
||||
|
||||
if (reg_num == 0) {
|
||||
return ctx->zero;
|
||||
}
|
||||
|
||||
switch (src_ext) {
|
||||
case EXT_NONE:
|
||||
return cpu_gpr[reg_num];
|
||||
case EXT_SIGN:
|
||||
t = temp_new(ctx);
|
||||
tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]);
|
||||
return t;
|
||||
case EXT_ZERO:
|
||||
t = temp_new(ctx);
|
||||
tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]);
|
||||
return t;
|
||||
}
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext)
|
||||
{
|
||||
if (reg_num == 0 || dst_ext) {
|
||||
return temp_new(ctx);
|
||||
}
|
||||
return cpu_gpr[reg_num];
|
||||
}
|
||||
|
||||
static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext)
|
||||
{
|
||||
if (reg_num != 0) {
|
||||
switch (dst_ext) {
|
||||
case EXT_NONE:
|
||||
tcg_gen_mov_tl(cpu_gpr[reg_num], t);
|
||||
break;
|
||||
case EXT_SIGN:
|
||||
tcg_gen_ext32s_tl(cpu_gpr[reg_num], t);
|
||||
break;
|
||||
case EXT_ZERO:
|
||||
tcg_gen_ext32u_tl(cpu_gpr[reg_num], t);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "decode-insns.c.inc"
|
||||
#include "insn_trans/trans_arith.c.inc"
|
||||
#include "insn_trans/trans_shift.c.inc"
|
||||
#include "insn_trans/trans_bit.c.inc"
|
||||
#include "insn_trans/trans_memory.c.inc"
|
||||
#include "insn_trans/trans_atomic.c.inc"
|
||||
#include "insn_trans/trans_extra.c.inc"
|
||||
#include "insn_trans/trans_farith.c.inc"
|
||||
#include "insn_trans/trans_fcmp.c.inc"
|
||||
#include "insn_trans/trans_fcnv.c.inc"
|
||||
#include "insn_trans/trans_fmov.c.inc"
|
||||
#include "insn_trans/trans_fmemory.c.inc"
|
||||
#include "insn_trans/trans_branch.c.inc"
|
||||
#include "insn_trans/trans_privileged.c.inc"
|
||||
|
||||
static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
CPULoongArchState *env = cs->env_ptr;
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
|
||||
ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
|
||||
|
||||
if (!decode(ctx, ctx->opcode)) {
|
||||
qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. "
|
||||
TARGET_FMT_lx ": 0x%x\n",
|
||||
ctx->base.pc_next, ctx->opcode);
|
||||
generate_exception(ctx, EXCCODE_INE);
|
||||
}
|
||||
|
||||
for (int i = ctx->ntemp - 1; i >= 0; --i) {
|
||||
tcg_temp_free(ctx->temp[i]);
|
||||
ctx->temp[i] = NULL;
|
||||
}
|
||||
ctx->ntemp = 0;
|
||||
|
||||
ctx->base.pc_next += 4;
|
||||
}
|
||||
|
||||
static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
|
||||
switch (ctx->base.is_jmp) {
|
||||
case DISAS_STOP:
|
||||
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
break;
|
||||
case DISAS_TOO_MANY:
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next);
|
||||
break;
|
||||
case DISAS_NORETURN:
|
||||
break;
|
||||
case DISAS_EXIT_UPDATE:
|
||||
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
|
||||
QEMU_FALLTHROUGH;
|
||||
case DISAS_EXIT:
|
||||
tcg_gen_exit_tb(NULL, 0);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void loongarch_tr_disas_log(const DisasContextBase *dcbase,
|
||||
CPUState *cpu, FILE *logfile)
|
||||
{
|
||||
qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first));
|
||||
target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
|
||||
}
|
||||
|
||||
static const TranslatorOps loongarch_tr_ops = {
|
||||
.init_disas_context = loongarch_tr_init_disas_context,
|
||||
.tb_start = loongarch_tr_tb_start,
|
||||
.insn_start = loongarch_tr_insn_start,
|
||||
.translate_insn = loongarch_tr_translate_insn,
|
||||
.tb_stop = loongarch_tr_tb_stop,
|
||||
.disas_log = loongarch_tr_disas_log,
|
||||
};
|
||||
|
||||
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
|
||||
{
|
||||
DisasContext ctx;
|
||||
|
||||
translator_loop(&loongarch_tr_ops, &ctx.base, cs, tb, max_insns);
|
||||
}
|
||||
|
||||
void loongarch_translate_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
cpu_gpr[0] = NULL;
|
||||
for (i = 1; i < 32; i++) {
|
||||
cpu_gpr[i] = tcg_global_mem_new(cpu_env,
|
||||
offsetof(CPULoongArchState, gpr[i]),
|
||||
regnames[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
int off = offsetof(CPULoongArchState, fpr[i]);
|
||||
cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, off, fregnames[i]);
|
||||
}
|
||||
|
||||
cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc");
|
||||
cpu_fcsr0 = tcg_global_mem_new_i32(cpu_env,
|
||||
offsetof(CPULoongArchState, fcsr0), "fcsr0");
|
||||
cpu_lladdr = tcg_global_mem_new(cpu_env,
|
||||
offsetof(CPULoongArchState, lladdr), "lladdr");
|
||||
cpu_llval = tcg_global_mem_new(cpu_env,
|
||||
offsetof(CPULoongArchState, llval), "llval");
|
||||
}
|
||||
|
||||
void restore_state_to_opc(CPULoongArchState *env, TranslationBlock *tb,
|
||||
target_ulong *data)
|
||||
{
|
||||
env->pc = data[0];
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* LoongArch translation routines.
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#ifndef TARGET_LOONGARCH_TRANSLATE_H
|
||||
#define TARGET_LOONGARCH_TRANSLATE_H
|
||||
|
||||
#include "exec/translator.h"
|
||||
|
||||
#define TRANS(NAME, FUNC, ...) \
|
||||
static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \
|
||||
{ return FUNC(ctx, a, __VA_ARGS__); }
|
||||
|
||||
/*
|
||||
* If an operation is being performed on less than TARGET_LONG_BITS,
|
||||
* it may require the inputs to be sign- or zero-extended; which will
|
||||
* depend on the exact operation being performed.
|
||||
*/
|
||||
typedef enum {
|
||||
EXT_NONE,
|
||||
EXT_SIGN,
|
||||
EXT_ZERO,
|
||||
} DisasExtend;
|
||||
|
||||
typedef struct DisasContext {
|
||||
DisasContextBase base;
|
||||
target_ulong page_start;
|
||||
uint32_t opcode;
|
||||
int mem_idx;
|
||||
TCGv zero;
|
||||
/* Space for 3 operands plus 1 extra for address computation. */
|
||||
TCGv temp[4];
|
||||
uint8_t ntemp;
|
||||
} DisasContext;
|
||||
|
||||
void generate_exception(DisasContext *ctx, int excp);
|
||||
|
||||
extern TCGv cpu_gpr[32], cpu_pc;
|
||||
extern TCGv_i32 cpu_fscr0;
|
||||
extern TCGv_i64 cpu_fpr[32];
|
||||
|
||||
#endif
|
|
@ -5,6 +5,7 @@ subdir('cris')
|
|||
subdir('hexagon')
|
||||
subdir('hppa')
|
||||
subdir('i386')
|
||||
subdir('loongarch')
|
||||
subdir('m68k')
|
||||
subdir('microblaze')
|
||||
subdir('mips')
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Loongarch64 system tests
|
||||
#
|
||||
|
||||
LOONGARCH64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/loongarch64/system
|
||||
VPATH+=$(LOONGARCH64_SYSTEM_SRC)
|
||||
|
||||
# These objects provide the basic boot code and helper functions for all tests
|
||||
CRT_OBJS=boot.o
|
||||
|
||||
LOONGARCH64_TEST_SRCS=$(wildcard $(LOONGARCH64_SYSTEM_SRC)/*.c)
|
||||
LOONGARCH64_TESTS = $(patsubst $(LOONGARCH64_SYSTEM_SRC)/%.c, %, $(LOONGARCH64_TEST_SRCS))
|
||||
|
||||
CRT_PATH=$(LOONGARCH64_SYSTEM_SRC)
|
||||
LINK_SCRIPT=$(LOONGARCH64_SYSTEM_SRC)/kernel.ld
|
||||
LDFLAGS=-Wl,-T$(LINK_SCRIPT)
|
||||
TESTS+=$(LOONGARCH64_TESTS) $(MULTIARCH_TESTS)
|
||||
CFLAGS+=-nostdlib -g -O1 -march=loongarch64 -mabi=lp64d $(MINILIB_INC)
|
||||
LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc
|
||||
|
||||
# building head blobs
|
||||
.PRECIOUS: $(CRT_OBJS)
|
||||
|
||||
%.o: $(CRT_PATH)/%.S
|
||||
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@
|
||||
|
||||
# Build and link the tests
|
||||
%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
|
||||
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
|
||||
|
||||
memory: CFLAGS+=-DCHECK_UNALIGNED=0
|
||||
# Running
|
||||
QEMU_OPTS+=-serial chardev:output -kernel
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Minimal LoongArch system boot code.
|
||||
*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include "regdef.h"
|
||||
|
||||
.global _start
|
||||
.align 16
|
||||
_start:
|
||||
la.local t0, stack_end
|
||||
move sp, t0
|
||||
bl main
|
||||
|
||||
.type _start 2
|
||||
.size _start, .-_start
|
||||
|
||||
.global _exit
|
||||
.align 16
|
||||
_exit:
|
||||
2: /* QEMU ACPI poweroff */
|
||||
li.w t0, 0xff
|
||||
li.w t1, 0x10080010
|
||||
st.w t0, t1, 0
|
||||
idle 0
|
||||
bl 2b
|
||||
|
||||
.type _exit 2
|
||||
.size _exit, .-_exit
|
||||
|
||||
.global __sys_outc
|
||||
__sys_outc:
|
||||
li.d t1, 1000000
|
||||
loop:
|
||||
lu12i.w t2, 0x1fe00
|
||||
ori t0, t2, 0x1e5
|
||||
ld.bu t0, t0, 0
|
||||
andi t0, t0, 0x20
|
||||
ext.w.b t0, t0
|
||||
bnez t0, in
|
||||
addi.w t1, t1, -1
|
||||
bnez t1, loop
|
||||
in:
|
||||
ext.w.b a0, a0
|
||||
lu12i.w t0, 0x1fe00
|
||||
ori t0, t0, 0x1e0
|
||||
st.b a0, t0, 0
|
||||
jirl $r0, ra, 0
|
||||
|
||||
.data
|
||||
.align 4
|
||||
stack:
|
||||
.space 65536
|
||||
stack_end:
|
|
@ -0,0 +1,30 @@
|
|||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Linux kernel legacy start address. */
|
||||
. = 0x9000000000200000;
|
||||
_text = .;
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
}
|
||||
_etext = .;
|
||||
|
||||
. = ALIGN(8192);
|
||||
_data = .;
|
||||
.got : {
|
||||
*(.got)
|
||||
}
|
||||
.data : {
|
||||
*(.sdata)
|
||||
*(.data)
|
||||
}
|
||||
_edata = .;
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
_end = .;
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2021 Loongson Technology Corporation Limited
|
||||
*/
|
||||
#ifndef _ASM_REGDEF_H
|
||||
#define _ASM_REGDEF_H
|
||||
|
||||
#define zero $r0 /* wired zero */
|
||||
#define ra $r1 /* return address */
|
||||
#define tp $r2
|
||||
#define sp $r3 /* stack pointer */
|
||||
#define v0 $r4 /* return value - caller saved */
|
||||
#define v1 $r5
|
||||
#define a0 $r4 /* argument registers */
|
||||
#define a1 $r5
|
||||
#define a2 $r6
|
||||
#define a3 $r7
|
||||
#define a4 $r8
|
||||
#define a5 $r9
|
||||
#define a6 $r10
|
||||
#define a7 $r11
|
||||
#define t0 $r12 /* caller saved */
|
||||
#define t1 $r13
|
||||
#define t2 $r14
|
||||
#define t3 $r15
|
||||
#define t4 $r16
|
||||
#define t5 $r17
|
||||
#define t6 $r18
|
||||
#define t7 $r19
|
||||
#define t8 $r20
|
||||
/* $r21: Temporarily reserved */
|
||||
#define fp $r22 /* frame pointer */
|
||||
#define s0 $r23 /* callee saved */
|
||||
#define s1 $r24
|
||||
#define s2 $r25
|
||||
#define s3 $r26
|
||||
#define s4 $r27
|
||||
#define s5 $r28
|
||||
#define s6 $r29
|
||||
#define s7 $r30
|
||||
#define s8 $r31
|
||||
|
||||
#define gr0 $r0
|
||||
#define gr1 $r1
|
||||
#define gr2 $r2
|
||||
#define gr3 $r3
|
||||
#define gr4 $r4
|
||||
#define gr5 $r5
|
||||
#define gr6 $r6
|
||||
#define gr7 $r7
|
||||
#define gr8 $r8
|
||||
#define gr9 $r9
|
||||
#define gr10 $r10
|
||||
#define gr11 $r11
|
||||
#define gr12 $r12
|
||||
#define gr13 $r13
|
||||
#define gr14 $r14
|
||||
#define gr15 $r15
|
||||
#define gr16 $r16
|
||||
#define gr17 $r17
|
||||
#define gr18 $r18
|
||||
#define gr19 $r19
|
||||
#define gr20 $r20
|
||||
#define gr21 $r21
|
||||
#define gr22 $r22
|
||||
#define gr23 $r23
|
||||
#define gr24 $r24
|
||||
#define gr25 $r25
|
||||
#define gr26 $r26
|
||||
#define gr27 $r27
|
||||
#define gr28 $r28
|
||||
#define gr29 $r29
|
||||
#define gr30 $r30
|
||||
#define gr31 $r31
|
||||
|
||||
#define STT_NOTYPE 0
|
||||
#define STT_OBJECT 1
|
||||
#define STT_FUNC 2
|
||||
#define STT_SECTION 3
|
||||
#define STT_FILE 4
|
||||
#define STT_COMMON 5
|
||||
#define STT_TLS 6
|
||||
|
||||
#define ASM_NL ;
|
||||
|
||||
#endif /* _ASM_REGDEF_H */
|
Loading…
Reference in New Issue