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:
Richard Henderson 2022-06-06 16:16:01 -07:00
commit 9b1f588549
75 changed files with 10158 additions and 4 deletions

View File

@ -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

View File

@ -0,0 +1,3 @@
# Default configuration for loongarch64-softmmu
CONFIG_LOONGARCH_VIRT=y

View File

@ -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

1
configure vendored
View File

@ -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-"}

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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

312
hw/intc/loongarch_extioi.c Normal file
View File

@ -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)

242
hw/intc/loongarch_ipi.c Normal file
View File

@ -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)

View File

@ -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)

431
hw/intc/loongarch_pch_pic.c Normal file
View File

@ -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)

View File

@ -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'))

View File

@ -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

16
hw/loongarch/Kconfig Normal file
View File

@ -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

382
hw/loongarch/loongson3.c Normal file
View File

@ -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)

4
hw/loongarch/meson.build Normal file
View File

@ -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}

View File

@ -50,6 +50,7 @@ subdir('avr')
subdir('cris')
subdir('hppa')
subdir('i386')
subdir('loongarch')
subdir('m68k')
subdir('microblaze')
subdir('mips')

View File

@ -27,3 +27,6 @@ config SUN4V_RTC
config GOLDFISH_RTC
bool
config LS7A_RTC
bool

528
hw/rtc/ls7a_rtc.c Normal file
View File

@ -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)

View File

@ -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'))

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 += {

View File

@ -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' ] } }

View File

@ -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',

View File

@ -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)

View File

@ -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

2
target/loongarch/Kconfig Normal file
View File

@ -0,0 +1,2 @@
config LOONGARCH64
bool

64
target/loongarch/README Normal file
View File

@ -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/

View File

@ -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);
}

208
target/loongarch/cpu-csr.h Normal file
View File

@ -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 */

View File

@ -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

704
target/loongarch/cpu.c Normal file
View File

@ -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;
}

391
target/loongarch/cpu.h Normal file
View File

@ -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 */

View File

@ -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;
}

757
target/loongarch/disas.c Normal file
View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

130
target/loongarch/helper.h Normal file
View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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);
}

102
target/loongarch/machine.c Normal file
View File

@ -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()
},
};

View File

@ -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}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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];
}

View File

@ -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

View File

@ -5,6 +5,7 @@ subdir('cris')
subdir('hexagon')
subdir('hppa')
subdir('i386')
subdir('loongarch')
subdir('m68k')
subdir('microblaze')
subdir('mips')

View File

@ -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

View File

@ -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:

View File

@ -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 = .;
}

View File

@ -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 */