@@ -284,6 +284,9 @@ typedef struct CPUArchState {
uint64_t CSR_DSAVE;
LoongArchTLB tlb[LOONGARCH_TLB_MAX];
+
+ AddressSpace address_space_iocsr;
+ MemoryRegion system_iocsr;
} CPULoongArchState;
/**
@@ -529,6 +529,14 @@ 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)
#define output_fcmp(C, PREFIX, SUFFIX) \
{ \
@@ -101,3 +101,5 @@ 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_3(csr_update, void, env, tl, i64)
+DEF_HELPER_3(iocsr_read, i64, env, tl, i32)
+DEF_HELPER_4(iocsr_write, void, env, tl, tl, i32)
@@ -175,3 +175,99 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
return true;
}
+
+static bool trans_iocsrrd_b(DisasContext *ctx, arg_iocsrrd_b *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(1));
+ return true;
+}
+
+static bool trans_iocsrrd_h(DisasContext *ctx, arg_iocsrrd_h *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(2));
+ return true;
+}
+
+static bool trans_iocsrrd_w(DisasContext *ctx, arg_iocsrrd_w *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(4));
+ return true;
+}
+
+static bool trans_iocsrrd_d(DisasContext *ctx, arg_iocsrrd_d *a)
+{
+ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(8));
+ return true;
+}
+
+static bool trans_iocsrwr_b(DisasContext *ctx, arg_iocsrwr_b *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(1));
+ return true;
+}
+
+static bool trans_iocsrwr_h(DisasContext *ctx, arg_iocsrwr_h *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(2));
+ return true;
+}
+
+static bool trans_iocsrwr_w(DisasContext *ctx, arg_iocsrwr_w *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(4));
+ return true;
+}
+
+static bool trans_iocsrwr_d(DisasContext *ctx, arg_iocsrwr_d *a)
+{
+ TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+ if (check_plv(ctx)) {
+ return false;
+ }
+ gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(8));
+ return true;
+}
@@ -450,3 +450,12 @@ bgeu 0110 11 ................ ..... ..... @rr_offs16
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
new file mode 100644
@@ -0,0 +1,139 @@
+/* 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 "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 "hw/loongarch/loongarch.h"
+#include "tcg/tcg-ldst.h"
+
+uint64_t helper_iocsr_read(CPULoongArchState *env, target_ulong r_addr,
+ uint32_t size)
+{
+ int cpuid = env_cpu(env)->cpu_index;
+ CPUState *cs = qemu_get_cpu(cpuid);
+ env = cs->env_ptr;
+ uint64_t ret = 0;
+
+ /*
+ * Adjust the per core address such as 0x10xx(IPI)/0x18xx(EXTIOI)
+ */
+ if (((r_addr & 0xff00) == 0x1000) || ((r_addr & 0xff00) == 0x1800)) {
+ r_addr = r_addr + ((target_ulong)(cpuid & 0x3) << 8);
+ }
+
+ switch (size) {
+ case 1:
+ ret = address_space_ldub(&env->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 2:
+ ret = address_space_lduw(&env->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 4:
+ ret = address_space_ldl(&env->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 8:
+ ret = address_space_ldq(&env->address_space_iocsr, r_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return ret;
+}
+
+void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
+ target_ulong val, uint32_t size)
+{
+ int cpuid = env_cpu(env)->cpu_index;
+ CPUState *cs = qemu_get_cpu(cpuid);
+ int mask, i;
+ env = cs->env_ptr;
+
+ /*
+ * For IPI send, Mailbox send and ANY send, adjust the addr and
+ * val accordingly. The IOCSR writes are turned to different
+ * MMIO writes respectively
+ */
+ switch (w_addr) {
+ case 0x1040: /* IPI send */
+ cpuid = (val >> 16) & 0x3ff;
+ val = 1UL << (val & 0x1f);
+ if (val) {
+ qemu_mutex_lock_iothread();
+ cs = qemu_get_cpu(cpuid);
+ env = cs->env_ptr;
+ LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+ loongarch_cpu_set_irq(cpu, IRQ_IPI, 1);
+ qemu_mutex_unlock_iothread();
+ }
+ break;
+ case 0x1048: /* Mail Send */
+ cpuid = (val >> 16) & 0x3ff;
+ w_addr = 0x1020 + (val & 0x1c);
+ val = val >> 32;
+ mask = (val >> 27) & 0xf;
+ size = 4;
+ env = (qemu_get_cpu(cpuid))->env_ptr;
+ break;
+ case 0x1158: /* ANY send */
+ cpuid = (val >> 16) & 0x3ff;
+ w_addr = val & 0xffff;
+ val = val >> 32;
+ mask = (val >> 27) & 0xf;
+ size = 1;
+ env = (qemu_get_cpu(cpuid))->env_ptr;
+
+ for (i = 0; i < 4; i++) {
+ if (!((mask >> i) & 1)) {
+ address_space_stb(&env->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+ w_addr = w_addr + 1;
+ val = val >> 8;
+ }
+ return;
+ default:
+ break;
+ }
+
+ if (((w_addr & 0xff00) == 0x1000) || ((w_addr & 0xff00) == 0x1800)) {
+ w_addr = w_addr + ((target_ulong)(cpuid & 0x3) << 8);
+ }
+
+ switch (size) {
+ case 1:
+ address_space_stb(&env->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 2:
+ address_space_stw(&env->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 4:
+ address_space_stl(&env->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 8:
+ address_space_stq(&env->address_space_iocsr, w_addr,
+ val, MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
@@ -20,6 +20,7 @@ loongarch_softmmu_ss.add(files(
'tlb_helper.c',
'constant_timer.c',
'csr_helper.c',
+ 'iocsr_helper.c',
))
loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])