pull-loongarch-20251009

-----BEGIN PGP SIGNATURE-----
 
 iLMEAAEIAB0WIQTKRzxE1qCcGJoZP81FK5aFKyaCFgUCaOeiawAKCRBFK5aFKyaC
 FlFZA/4uTme7RNIpDkcTW37ZieeRkFJXxO6EDvy/684EXUBMcJmhslXxb2vbtDUZ
 Mi2SCt4iB3oewYdDDe9glCGhRSNpARCMQp0rLivOBGWAguEld+M0sZ/Aqpk6Ovub
 zSHZKODKAADNt1lgzQ9iJx3uUBeUMdFKagIOrURPeCItLpoaKA==
 =OnvQ
 -----END PGP SIGNATURE-----

Merge tag 'pull-loongarch-20251009' of https://github.com/gaosong715/qemu into staging

pull-loongarch-20251009

# -----BEGIN PGP SIGNATURE-----
#
# iLMEAAEIAB0WIQTKRzxE1qCcGJoZP81FK5aFKyaCFgUCaOeiawAKCRBFK5aFKyaC
# FlFZA/4uTme7RNIpDkcTW37ZieeRkFJXxO6EDvy/684EXUBMcJmhslXxb2vbtDUZ
# Mi2SCt4iB3oewYdDDe9glCGhRSNpARCMQp0rLivOBGWAguEld+M0sZ/Aqpk6Ovub
# zSHZKODKAADNt1lgzQ9iJx3uUBeUMdFKagIOrURPeCItLpoaKA==
# =OnvQ
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 09 Oct 2025 04:54:19 AM PDT
# gpg:                using RSA key CA473C44D6A09C189A193FCD452B96852B268216
# gpg: Good signature from "Song Gao <gaosong@loongson.cn>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: CA47 3C44 D6A0 9C18 9A19  3FCD 452B 9685 2B26 8216

* tag 'pull-loongarch-20251009' of https://github.com/gaosong715/qemu:
  target/loongarch: Define loongarch_exception_name() as static
  target/loongarch: Move function do_raise_exception() to tcg_cpu.c
  target/loongarch: Move TCG specified functions to tcg_cpu.c
  tests/data/acpi/loongarch64: Update expected DSDT.*
  hw/loongarch/virt: Align VIRT_GED_CPUHP_ADDR to 4 bytes
  bios-tables-test-allowed-diff.h: Allow LoongArch DSDT.*

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2025-10-09 07:59:29 -07:00
commit 94474a7733
10 changed files with 331 additions and 321 deletions

View file

@ -49,9 +49,9 @@
#define VIRT_LOWMEM_SIZE 0x10000000
#define VIRT_HIGHMEM_BASE 0x80000000
#define VIRT_GED_EVT_ADDR 0x100e0000
#define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN)
#define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN)
#define VIRT_GED_CPUHP_ADDR (VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT)
#define VIRT_GED_MEM_ADDR QEMU_ALIGN_UP(VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN, 4)
#define VIRT_GED_REG_ADDR QEMU_ALIGN_UP(VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN, 4)
#define VIRT_GED_CPUHP_ADDR QEMU_ALIGN_UP(VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT, 4)
#define COMMAND_LINE_SIZE 512

View file

@ -28,11 +28,6 @@
#ifdef CONFIG_KVM
#include <linux/kvm.h>
#endif
#ifdef CONFIG_TCG
#include "accel/tcg/cpu-ldst.h"
#include "accel/tcg/cpu-ops.h"
#include "tcg/tcg.h"
#endif
#include "tcg/tcg_loongarch.h"
const char * const regnames[32] = {
@ -49,62 +44,6 @@ const char * const fregnames[32] = {
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
};
struct TypeExcp {
int32_t exccode;
const char * const name;
};
static const struct TypeExcp 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_FPD, "Floating Point Disabled"},
{EXCCODE_FPE, "Floating Point Exception"},
{EXCCODE_DBP, "Debug breakpoint"},
{EXCCODE_BCE, "Bound Check Exception"},
{EXCCODE_SXD, "128 bit vector instructions Disable exception"},
{EXCCODE_ASXD, "256 bit vector instructions Disable exception"},
{EXCP_HLT, "EXCP_HLT"},
};
const char *loongarch_exception_name(int32_t exception)
{
int i;
for (i = 0; i < ARRAY_SIZE(excp_names); i++) {
if (excp_names[i].exccode == exception) {
return excp_names[i].name;
}
}
return "Unknown";
}
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: exception: %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)
{
set_pc(cpu_env(cs), value);
@ -140,18 +79,8 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level)
}
}
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)
bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
{
uint32_t pending;
uint32_t status;
@ -163,217 +92,8 @@ static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
}
#endif
#ifdef CONFIG_TCG
#ifndef CONFIG_USER_ONLY
static void loongarch_cpu_do_interrupt(CPUState *cs)
{
CPULoongArchState *env = cpu_env(cs);
bool update_badinstr = 1;
int cause = -1;
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) {
qemu_log_mask(CPU_LOG_INT,
"%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx
" TLBRERA " TARGET_FMT_lx " exception: %d (%s)\n",
__func__, env->pc, env->CSR_ERA, env->CSR_TLBRERA,
cs->exception_index,
loongarch_exception_name(cs->exception_index));
}
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);
set_pc(env, 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:
case EXCCODE_ADEF:
cause = cs->exception_index;
update_badinstr = 0;
break;
case EXCCODE_SYS:
case EXCCODE_BRK:
case EXCCODE_INE:
case EXCCODE_IPE:
case EXCCODE_FPD:
case EXCCODE_FPE:
case EXCCODE_SXD:
case EXCCODE_ASXD:
env->CSR_BADV = env->pc;
QEMU_FALLTHROUGH;
case EXCCODE_BCE:
case EXCCODE_ADEM:
case EXCCODE_PIL:
case EXCCODE_PIS:
case EXCCODE_PME:
case EXCCODE_PNR:
case EXCCODE_PNX:
case EXCCODE_PPI:
cause = cs->exception_index;
break;
default:
qemu_log("Error: exception(%d) has not been supported\n",
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,
EXCODE_MCODE(cause));
env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ESUBCODE,
EXCODE_SUBCODE(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 (vec_size) {
vec_size = (1 << vec_size) * 4;
}
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);
set_pc(env, 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) {
set_pc(env, env->CSR_TLBRENTRY);
} else {
set_pc(env, env->CSR_EENTRY + EXCODE_MCODE(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)
{
CPULoongArchState *env = cpu_env(cs);
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) {
CPULoongArchState *env = cpu_env(cs);
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;
}
static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx,
vaddr result, vaddr base)
{
return is_va32(cpu_env(cs)) ? (uint32_t)result : result;
}
#endif
static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs)
{
CPULoongArchState *env = cpu_env(cs);
uint32_t flags;
flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK);
flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE;
flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE;
flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE;
flags |= is_va32(env) * HW_FLAGS_VA32;
return (TCGTBCPUState){ .pc = env->pc, .flags = flags };
}
static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
set_pc(cpu_env(cs), tb->pc);
}
static void loongarch_restore_state_to_opc(CPUState *cs,
const TranslationBlock *tb,
const uint64_t *data)
{
set_pc(cpu_env(cs), data[0]);
}
#endif /* CONFIG_TCG */
#ifndef CONFIG_USER_ONLY
static bool loongarch_cpu_has_work(CPUState *cs)
bool loongarch_cpu_has_work(CPUState *cs)
{
bool has_work = false;
@ -386,16 +106,6 @@ static bool loongarch_cpu_has_work(CPUState *cs)
}
#endif /* !CONFIG_USER_ONLY */
static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch)
{
CPULoongArchState *env = cpu_env(cs);
if (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG)) {
return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
}
return MMU_DA_IDX;
}
static void loongarch_la464_init_csr(Object *obj)
{
#ifndef CONFIG_USER_ONLY
@ -911,30 +621,6 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
}
}
#ifdef CONFIG_TCG
static const TCGCPUOps loongarch_tcg_ops = {
.guest_default_memory_order = 0,
.mttcg_supported = true,
.initialize = loongarch_translate_init,
.translate_code = loongarch_translate_code,
.get_tb_cpu_state = loongarch_get_tb_cpu_state,
.synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
.restore_state_to_opc = loongarch_restore_state_to_opc,
.mmu_index = loongarch_cpu_mmu_index,
#ifndef CONFIG_USER_ONLY
.tlb_fill = loongarch_cpu_tlb_fill,
.pointer_wrap = loongarch_pointer_wrap,
.cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
.cpu_exec_halt = loongarch_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = loongarch_cpu_do_interrupt,
.do_transaction_failed = loongarch_cpu_do_transaction_failed,
#endif
};
#endif /* CONFIG_TCG */
#ifndef CONFIG_USER_ONLY
#include "hw/core/sysemu-cpu-ops.h"

View file

@ -24,8 +24,6 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env,
uint32_t exception,
uintptr_t pc);
const char *loongarch_exception_name(int32_t exception);
#ifdef CONFIG_TCG
int ieee_ex_to_loongarch(int xcpt);
void restore_fp_status(CPULoongArchState *env);
@ -41,6 +39,8 @@ 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_has_work(CPUState *cs);
bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env);
#endif /* !CONFIG_USER_ONLY */
uint64_t read_fcc(CPULoongArchState *env);

View file

@ -7,6 +7,7 @@ loongarch_ss.add([zlib, gen])
loongarch_ss.add(files(
'fpu_helper.c',
'op_helper.c',
'tcg_cpu.c',
'translate.c',
'vec_helper.c',
))

View file

@ -0,0 +1,322 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* LoongArch CPU parameters for QEMU.
*
* Copyright (c) 2025 Loongson Technology Corporation Limited
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "accel/accel-cpu-target.h"
#include "accel/tcg/cpu-ldst.h"
#include "accel/tcg/cpu-ops.h"
#include "exec/translation-block.h"
#include "exec/target_page.h"
#include "tcg_loongarch.h"
#include "internals.h"
struct TypeExcp {
int32_t exccode;
const char * const name;
};
static const struct TypeExcp 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_FPD, "Floating Point Disabled"},
{EXCCODE_FPE, "Floating Point Exception"},
{EXCCODE_DBP, "Debug breakpoint"},
{EXCCODE_BCE, "Bound Check Exception"},
{EXCCODE_SXD, "128 bit vector instructions Disable exception"},
{EXCCODE_ASXD, "256 bit vector instructions Disable exception"},
{EXCP_HLT, "EXCP_HLT"},
};
static const char *loongarch_exception_name(int32_t exception)
{
int i;
for (i = 0; i < ARRAY_SIZE(excp_names); i++) {
if (excp_names[i].exccode == exception) {
return excp_names[i].name;
}
}
return "Unknown";
}
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: exception: %d (%s)\n",
__func__,
exception,
loongarch_exception_name(exception));
cs->exception_index = exception;
cpu_loop_exit_restore(cs, pc);
}
#ifndef CONFIG_USER_ONLY
static void loongarch_cpu_do_interrupt(CPUState *cs)
{
CPULoongArchState *env = cpu_env(cs);
bool update_badinstr = 1;
int cause = -1;
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) {
qemu_log_mask(CPU_LOG_INT,
"%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx
" TLBRERA " TARGET_FMT_lx " exception: %d (%s)\n",
__func__, env->pc, env->CSR_ERA, env->CSR_TLBRERA,
cs->exception_index,
loongarch_exception_name(cs->exception_index));
}
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);
set_pc(env, 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:
case EXCCODE_ADEF:
cause = cs->exception_index;
update_badinstr = 0;
break;
case EXCCODE_SYS:
case EXCCODE_BRK:
case EXCCODE_INE:
case EXCCODE_IPE:
case EXCCODE_FPD:
case EXCCODE_FPE:
case EXCCODE_SXD:
case EXCCODE_ASXD:
env->CSR_BADV = env->pc;
QEMU_FALLTHROUGH;
case EXCCODE_BCE:
case EXCCODE_ADEM:
case EXCCODE_PIL:
case EXCCODE_PIS:
case EXCCODE_PME:
case EXCCODE_PNR:
case EXCCODE_PNX:
case EXCCODE_PPI:
cause = cs->exception_index;
break;
default:
qemu_log("Error: exception(%d) has not been supported\n",
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,
EXCODE_MCODE(cause));
env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ESUBCODE,
EXCODE_SUBCODE(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 (vec_size) {
vec_size = (1 << vec_size) * 4;
}
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);
set_pc(env, 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) {
set_pc(env, env->CSR_TLBRENTRY);
} else {
set_pc(env, env->CSR_EENTRY + EXCODE_MCODE(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)
{
CPULoongArchState *env = cpu_env(cs);
if (access_type == MMU_INST_FETCH) {
do_raise_exception(env, EXCCODE_ADEF, retaddr);
} else {
do_raise_exception(env, EXCCODE_ADEM, retaddr);
}
}
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;
}
static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
if (interrupt_request & CPU_INTERRUPT_HARD) {
CPULoongArchState *env = cpu_env(cs);
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;
}
static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx,
vaddr result, vaddr base)
{
return is_va32(cpu_env(cs)) ? (uint32_t)result : result;
}
#endif
static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs)
{
CPULoongArchState *env = cpu_env(cs);
uint32_t flags;
flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK);
flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE;
flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE;
flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE;
flags |= is_va32(env) * HW_FLAGS_VA32;
return (TCGTBCPUState){ .pc = env->pc, .flags = flags };
}
static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
set_pc(cpu_env(cs), tb->pc);
}
static void loongarch_restore_state_to_opc(CPUState *cs,
const TranslationBlock *tb,
const uint64_t *data)
{
set_pc(cpu_env(cs), data[0]);
}
static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch)
{
CPULoongArchState *env = cpu_env(cs);
if (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG)) {
return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
}
return MMU_DA_IDX;
}
const TCGCPUOps loongarch_tcg_ops = {
.guest_default_memory_order = 0,
.mttcg_supported = true,
.initialize = loongarch_translate_init,
.translate_code = loongarch_translate_code,
.get_tb_cpu_state = loongarch_get_tb_cpu_state,
.synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
.restore_state_to_opc = loongarch_restore_state_to_opc,
.mmu_index = loongarch_cpu_mmu_index,
#ifndef CONFIG_USER_ONLY
.tlb_fill = loongarch_cpu_tlb_fill,
.pointer_wrap = loongarch_pointer_wrap,
.cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
.cpu_exec_halt = loongarch_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = loongarch_cpu_do_interrupt,
.do_transaction_failed = loongarch_cpu_do_transaction_failed,
#endif
};

View file

@ -9,6 +9,7 @@
#include "cpu.h"
#include "cpu-mmu.h"
extern const TCGCPUOps loongarch_tcg_ops;
void loongarch_csr_translate_init(void);
bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,