diff --git a/MAINTAINERS b/MAINTAINERS index 5fe8f7eca2..5580a36b68 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -212,6 +212,13 @@ S: Maintained F: target/hppa/ F: disas/hppa.c +LoongArch TCG CPUs +M: Song Gao +M: Xiaojuan Yang +S: Maintained +F: target/loongarch/ +F: tests/tcg/loongarch64/ + M68K TCG CPUs M: Laurent Vivier 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 +M: Song Gao +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 diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak new file mode 100644 index 0000000000..928bc117ef --- /dev/null +++ b/configs/devices/loongarch64-softmmu/default.mak @@ -0,0 +1,3 @@ +# Default configuration for loongarch64-softmmu + +CONFIG_LOONGARCH_VIRT=y diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak new file mode 100644 index 0000000000..7bc06c850c --- /dev/null +++ b/configs/targets/loongarch64-softmmu.mak @@ -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 diff --git a/configure b/configure index ac18ed4f3a..e69537c756 100755 --- a/configure +++ b/configure @@ -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-"} diff --git a/docs/system/loongarch/loongson3.rst b/docs/system/loongarch/loongson3.rst new file mode 100644 index 0000000000..fa3acd01c0 --- /dev/null +++ b/docs/system/loongarch/loongson3.rst @@ -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 diff --git a/gdb-xml/loongarch-base64.xml b/gdb-xml/loongarch-base64.xml new file mode 100644 index 0000000000..4962bdbd28 --- /dev/null +++ b/gdb-xml/loongarch-base64.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/loongarch-fpu64.xml b/gdb-xml/loongarch-fpu64.xml new file mode 100644 index 0000000000..e52cf89fbc --- /dev/null +++ b/gdb-xml/loongarch-fpu64.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw/Kconfig b/hw/Kconfig index 50e0952889..38233bbb0f 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -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 diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index eded1b557e..ecd2883ceb 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -87,3 +87,18 @@ config M68K_IRQC config NIOS2_VIC bool + +config LOONGARCH_IPI + bool + +config LOONGARCH_PCH_PIC + bool + select UNIMP + +config LOONGARCH_PCH_MSI + select MSI_NONBROKEN + bool + select UNIMP + +config LOONGARCH_EXTIOI + bool diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c new file mode 100644 index 0000000000..22803969bc --- /dev/null +++ b/hw/intc/loongarch_extioi.c @@ -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) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c new file mode 100644 index 0000000000..66bee93675 --- /dev/null +++ b/hw/intc/loongarch_ipi.c @@ -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) diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c new file mode 100644 index 0000000000..74bcdbdb48 --- /dev/null +++ b/hw/intc/loongarch_pch_msi.c @@ -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) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c new file mode 100644 index 0000000000..3c9814a3b4 --- /dev/null +++ b/hw/intc/loongarch_pch_pic.c @@ -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) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 8b35139f82..bcbf22ff51 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -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')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 5271590304..0a90c1cdec 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -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 + diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig new file mode 100644 index 0000000000..35b6680772 --- /dev/null +++ b/hw/loongarch/Kconfig @@ -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 diff --git a/hw/loongarch/loongson3.c b/hw/loongarch/loongson3.c new file mode 100644 index 0000000000..bd20ebbb78 --- /dev/null +++ b/hw/loongarch/loongson3.c @@ -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) diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build new file mode 100644 index 0000000000..cecb1a5d65 --- /dev/null +++ b/hw/loongarch/meson.build @@ -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} diff --git a/hw/meson.build b/hw/meson.build index 9992c5101e..c7ac7d3d75 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -50,6 +50,7 @@ subdir('avr') subdir('cris') subdir('hppa') subdir('i386') +subdir('loongarch') subdir('m68k') subdir('microblaze') subdir('mips') diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 730c272bc5..d0d8dda084 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -27,3 +27,6 @@ config SUN4V_RTC config GOLDFISH_RTC bool + +config LS7A_RTC + bool diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c new file mode 100644 index 0000000000..fe6710310f --- /dev/null +++ b/hw/rtc/ls7a_rtc.c @@ -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) diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index 7cecdee5dd..dc33973384 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -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')) diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index fc7cb7af5a..64247ecb11 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -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); diff --git a/include/exec/poison.h b/include/exec/poison.h index 9f1ca3409c..bbb82cf9ec 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -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 diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h new file mode 100644 index 0000000000..15b8c999f6 --- /dev/null +++ b/include/hw/intc/loongarch_extioi.h @@ -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 */ diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h new file mode 100644 index 0000000000..996ed7ea93 --- /dev/null +++ b/include/hw/intc/loongarch_ipi.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch ipi interrupt header files + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_IPI_H +#define HW_LOONGARCH_IPI_H + +#include "hw/sysbus.h" + +/* Mainy used by iocsr read and write */ +#define SMP_IPI_MAILBOX 0x1000ULL +#define CORE_STATUS_OFF 0x0 +#define CORE_EN_OFF 0x4 +#define CORE_SET_OFF 0x8 +#define CORE_CLEAR_OFF 0xc +#define CORE_BUF_20 0x20 +#define CORE_BUF_28 0x28 +#define CORE_BUF_30 0x30 +#define CORE_BUF_38 0x38 +#define IOCSR_IPI_SEND 0x40 +#define IOCSR_MAIL_SEND 0x48 +#define IOCSR_ANY_SEND 0x158 + +/* IPI system memory address */ +#define IPI_SYSTEM_MEM 0x1fe01000 + +#define MAX_IPI_CORE_NUM 4 +#define MAX_IPI_MBX_NUM 4 + +#define TYPE_LOONGARCH_IPI "loongarch_ipi" +OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI) + +typedef struct IPICore { + uint32_t status; + uint32_t en; + uint32_t set; + uint32_t clear; + /* 64bit buf divide into 2 32bit buf */ + uint32_t buf[MAX_IPI_MBX_NUM * 2]; + qemu_irq irq; +} IPICore; + +struct LoongArchIPI { + SysBusDevice parent_obj; + MemoryRegion ipi_iocsr_mem[MAX_IPI_CORE_NUM]; + MemoryRegion ipi_system_mem[MAX_IPI_CORE_NUM]; +}; + +#endif diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h new file mode 100644 index 0000000000..f668bfca7a --- /dev/null +++ b/include/hw/intc/loongarch_pch_msi.h @@ -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; +}; diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h new file mode 100644 index 0000000000..2d4aa9ed6f --- /dev/null +++ b/include/hw/intc/loongarch_pch_pic.h @@ -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; +}; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h new file mode 100644 index 0000000000..09a816191c --- /dev/null +++ b/include/hw/loongarch/virt.h @@ -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 diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h new file mode 100644 index 0000000000..08c5f78be2 --- /dev/null +++ b/include/hw/pci-host/ls7a.h @@ -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 diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 79c2591425..8850cb1a14 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -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; diff --git a/meson.build b/meson.build index d738391810..21cd949082 100644 --- a/meson.build +++ b/meson.build @@ -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 += { diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 06b0d2ca61..2e267fa458 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -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' ] } } diff --git a/qapi/machine.json b/qapi/machine.json index 883ce3f9ea..f750a16396 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -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', diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 12fe60c467..bb5897fc76 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -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) diff --git a/target/Kconfig b/target/Kconfig index ae7f24fc66..83da0bd293 100644 --- a/target/Kconfig +++ b/target/Kconfig @@ -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 diff --git a/target/loongarch/Kconfig b/target/loongarch/Kconfig new file mode 100644 index 0000000000..46b26b1a85 --- /dev/null +++ b/target/loongarch/Kconfig @@ -0,0 +1,2 @@ +config LOONGARCH64 + bool diff --git a/target/loongarch/README b/target/loongarch/README new file mode 100644 index 0000000000..4dcd0f1682 --- /dev/null +++ b/target/loongarch/README @@ -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/ diff --git a/target/loongarch/constant_timer.c b/target/loongarch/constant_timer.c new file mode 100644 index 0000000000..1851f53fd6 --- /dev/null +++ b/target/loongarch/constant_timer.c @@ -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); +} diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h new file mode 100644 index 0000000000..4c8ce7fed5 --- /dev/null +++ b/target/loongarch/cpu-csr.h @@ -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 */ diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h new file mode 100644 index 0000000000..414d8fff46 --- /dev/null +++ b/target/loongarch/cpu-param.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU parameters for QEMU. + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_PARAM_H +#define LOONGARCH_CPU_PARAM_H + +#define TARGET_LONG_BITS 64 +#define TARGET_PHYS_ADDR_SPACE_BITS 48 +#define TARGET_VIRT_ADDR_SPACE_BITS 48 + +#define TARGET_PAGE_BITS 14 +#define NB_MMU_MODES 5 + +#endif diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c new file mode 100644 index 0000000000..4c8f96bc3a --- /dev/null +++ b/target/loongarch/cpu.c @@ -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; +} diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h new file mode 100644 index 0000000000..71a5036c3c --- /dev/null +++ b/target/loongarch/cpu.h @@ -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 */ diff --git a/target/loongarch/csr_helper.c b/target/loongarch/csr_helper.c new file mode 100644 index 0000000000..24a9389364 --- /dev/null +++ b/target/loongarch/csr_helper.c @@ -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; +} diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c new file mode 100644 index 0000000000..858dfcc53a --- /dev/null +++ b/target/loongarch/disas.c @@ -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) diff --git a/target/loongarch/fpu_helper.c b/target/loongarch/fpu_helper.c new file mode 100644 index 0000000000..3d0cb8dd0d --- /dev/null +++ b/target/loongarch/fpu_helper.c @@ -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); +} diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c new file mode 100644 index 0000000000..0c48834201 --- /dev/null +++ b/target/loongarch/gdbstub.c @@ -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); +} diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h new file mode 100644 index 0000000000..85c11a60d4 --- /dev/null +++ b/target/loongarch/helper.h @@ -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) diff --git a/target/loongarch/insn_trans/trans_arith.c.inc b/target/loongarch/insn_trans/trans_arith.c.inc new file mode 100644 index 0000000000..8e45eadbc8 --- /dev/null +++ b/target/loongarch/insn_trans/trans_arith.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_atomic.c.inc b/target/loongarch/insn_trans/trans_atomic.c.inc new file mode 100644 index 0000000000..6763c1c301 --- /dev/null +++ b/target/loongarch/insn_trans/trans_atomic.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_bit.c.inc b/target/loongarch/insn_trans/trans_bit.c.inc new file mode 100644 index 0000000000..9337714ec4 --- /dev/null +++ b/target/loongarch/insn_trans/trans_bit.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_branch.c.inc b/target/loongarch/insn_trans/trans_branch.c.inc new file mode 100644 index 0000000000..65dbdff41e --- /dev/null +++ b/target/loongarch/insn_trans/trans_branch.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/insn_trans/trans_extra.c.inc new file mode 100644 index 0000000000..ad713cd61e --- /dev/null +++ b/target/loongarch/insn_trans/trans_extra.c.inc @@ -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)) diff --git a/target/loongarch/insn_trans/trans_farith.c.inc b/target/loongarch/insn_trans/trans_farith.c.inc new file mode 100644 index 0000000000..65ad2ffab8 --- /dev/null +++ b/target/loongarch/insn_trans/trans_farith.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_fcmp.c.inc b/target/loongarch/insn_trans/trans_fcmp.c.inc new file mode 100644 index 0000000000..93a6a2230f --- /dev/null +++ b/target/loongarch/insn_trans/trans_fcmp.c.inc @@ -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; +} diff --git a/target/loongarch/insn_trans/trans_fcnv.c.inc b/target/loongarch/insn_trans/trans_fcnv.c.inc new file mode 100644 index 0000000000..c1c6918ad1 --- /dev/null +++ b/target/loongarch/insn_trans/trans_fcnv.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_fmemory.c.inc b/target/loongarch/insn_trans/trans_fmemory.c.inc new file mode 100644 index 0000000000..74ee98f63a --- /dev/null +++ b/target/loongarch/insn_trans/trans_fmemory.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_fmov.c.inc b/target/loongarch/insn_trans/trans_fmov.c.inc new file mode 100644 index 0000000000..24753d4568 --- /dev/null +++ b/target/loongarch/insn_trans/trans_fmov.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_memory.c.inc b/target/loongarch/insn_trans/trans_memory.c.inc new file mode 100644 index 0000000000..d5eb31147c --- /dev/null +++ b/target/loongarch/insn_trans/trans_memory.c.inc @@ -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) diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/insn_trans/trans_privileged.c.inc new file mode 100644 index 0000000000..53596c4f77 --- /dev/null +++ b/target/loongarch/insn_trans/trans_privileged.c.inc @@ -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; +} diff --git a/target/loongarch/insn_trans/trans_shift.c.inc b/target/loongarch/insn_trans/trans_shift.c.inc new file mode 100644 index 0000000000..5260af2337 --- /dev/null +++ b/target/loongarch/insn_trans/trans_shift.c.inc @@ -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) diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode new file mode 100644 index 0000000000..3fdc6e148c --- /dev/null +++ b/target/loongarch/insns.decode @@ -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 diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h new file mode 100644 index 0000000000..9d50fbdd81 --- /dev/null +++ b/target/loongarch/internals.h @@ -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 diff --git a/target/loongarch/iocsr_helper.c b/target/loongarch/iocsr_helper.c new file mode 100644 index 0000000000..0e9c537dc7 --- /dev/null +++ b/target/loongarch/iocsr_helper.c @@ -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); +} diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c new file mode 100644 index 0000000000..b1e523ea72 --- /dev/null +++ b/target/loongarch/machine.c @@ -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() + }, +}; diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build new file mode 100644 index 0000000000..6376f9e84b --- /dev/null +++ b/target/loongarch/meson.build @@ -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} diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c new file mode 100644 index 0000000000..d87049851f --- /dev/null +++ b/target/loongarch/op_helper.c @@ -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 +#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); +} diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c new file mode 100644 index 0000000000..bab19c7e05 --- /dev/null +++ b/target/loongarch/tlb_helper.c @@ -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); +} diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c new file mode 100644 index 0000000000..c9afd11420 --- /dev/null +++ b/target/loongarch/translate.c @@ -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]; +} diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h new file mode 100644 index 0000000000..9cc12512d1 --- /dev/null +++ b/target/loongarch/translate.h @@ -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 diff --git a/target/meson.build b/target/meson.build index 2f6940255e..a53a60486f 100644 --- a/target/meson.build +++ b/target/meson.build @@ -5,6 +5,7 @@ subdir('cris') subdir('hexagon') subdir('hppa') subdir('i386') +subdir('loongarch') subdir('m68k') subdir('microblaze') subdir('mips') diff --git a/tests/tcg/loongarch64/Makefile.softmmu-target b/tests/tcg/loongarch64/Makefile.softmmu-target new file mode 100644 index 0000000000..908f3a8c0f --- /dev/null +++ b/tests/tcg/loongarch64/Makefile.softmmu-target @@ -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 diff --git a/tests/tcg/loongarch64/system/boot.S b/tests/tcg/loongarch64/system/boot.S new file mode 100644 index 0000000000..67eb1c04ce --- /dev/null +++ b/tests/tcg/loongarch64/system/boot.S @@ -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: diff --git a/tests/tcg/loongarch64/system/kernel.ld b/tests/tcg/loongarch64/system/kernel.ld new file mode 100644 index 0000000000..f1a7c0168c --- /dev/null +++ b/tests/tcg/loongarch64/system/kernel.ld @@ -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 = .; +} diff --git a/tests/tcg/loongarch64/system/regdef.h b/tests/tcg/loongarch64/system/regdef.h new file mode 100644 index 0000000000..faa09b2377 --- /dev/null +++ b/tests/tcg/loongarch64/system/regdef.h @@ -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 */