/* 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 "qemu/int128.h" #include "exec/cpu-defs.h" #include "fpu/softfloat-types.h" #include "hw/registerfields.h" #include "qemu/timer.h" #ifndef CONFIG_USER_ONLY #include "exec/memory.h" #endif #include "cpu-csr.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 VERSION_REG 0x0 #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) \ do { \ (REG) = FIELD_DP32(REG, FCSR0, CAUSE, V); \ } while (0) #define UPDATE_FP_CAUSE(REG, V) \ do { \ (REG) |= FIELD_DP32(0, FCSR0, CAUSE, V); \ } while (0) #define GET_FP_ENABLES(REG) FIELD_EX32(REG, FCSR0, ENABLES) #define SET_FP_ENABLES(REG, V) \ do { \ (REG) = FIELD_DP32(REG, FCSR0, ENABLES, V); \ } while (0) #define GET_FP_FLAGS(REG) FIELD_EX32(REG, FCSR0, FLAGS) #define SET_FP_FLAGS(REG, V) \ do { \ (REG) = FIELD_DP32(REG, FCSR0, FLAGS, V); \ } while (0) #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 EXCODE(code, subcode) ( ((subcode) << 6) | (code) ) #define EXCODE_MCODE(code) ( (code) & 0x3f ) #define EXCODE_SUBCODE(code) ( (code) >> 6 ) #define EXCCODE_EXTERNAL_INT 64 /* plus external interrupt number */ #define EXCCODE_INT EXCODE(0, 0) #define EXCCODE_PIL EXCODE(1, 0) #define EXCCODE_PIS EXCODE(2, 0) #define EXCCODE_PIF EXCODE(3, 0) #define EXCCODE_PME EXCODE(4, 0) #define EXCCODE_PNR EXCODE(5, 0) #define EXCCODE_PNX EXCODE(6, 0) #define EXCCODE_PPI EXCODE(7, 0) #define EXCCODE_ADEF EXCODE(8, 0) /* Different exception subcode */ #define EXCCODE_ADEM EXCODE(8, 1) #define EXCCODE_ALE EXCODE(9, 0) #define EXCCODE_BCE EXCODE(10, 0) #define EXCCODE_SYS EXCODE(11, 0) #define EXCCODE_BRK EXCODE(12, 0) #define EXCCODE_INE EXCODE(13, 0) #define EXCCODE_IPE EXCODE(14, 0) #define EXCCODE_FPD EXCODE(15, 0) #define EXCCODE_SXD EXCODE(16, 0) #define EXCCODE_ASXD EXCODE(17, 0) #define EXCCODE_FPE EXCODE(18, 0) /* Different exception subcode */ #define EXCCODE_VFPE EXCODE(18, 1) #define EXCCODE_WPEF EXCODE(19, 0) /* Different exception subcode */ #define EXCCODE_WPEM EXCODE(19, 1) #define EXCCODE_BTD EXCODE(20, 0) #define EXCCODE_BTE EXCODE(21, 0) #define EXCCODE_DBP EXCODE(26, 0) /* 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[1].arch */ #define CPUCFG1_ARCH_LA32R 0 #define CPUCFG1_ARCH_LA32 1 #define CPUCFG1_ARCH_LA64 2 /* 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) #define LSX_LEN (128) #define LASX_LEN (256) typedef union VReg { int8_t B[LASX_LEN / 8]; int16_t H[LASX_LEN / 16]; int32_t W[LASX_LEN / 32]; int64_t D[LASX_LEN / 64]; uint8_t UB[LASX_LEN / 8]; uint16_t UH[LASX_LEN / 16]; uint32_t UW[LASX_LEN / 32]; uint64_t UD[LASX_LEN / 64]; Int128 Q[LASX_LEN / 128]; } VReg; typedef union fpr_t fpr_t; union fpr_t { VReg vreg; }; 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; fpr_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; /* 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; uint64_t CSR_CPUID; #ifndef CONFIG_USER_ONLY LoongArchTLB tlb[LOONGARCH_TLB_MAX]; AddressSpace address_space_iocsr; MemoryRegion system_iocsr; MemoryRegion iocsr_mem; bool load_elf; uint64_t elf_address; /* Store ipistate to access from this struct */ DeviceState *ipistate; #endif } CPULoongArchState; /** * LoongArchCPU: * @env: #CPULoongArchState * * A LoongArch CPU. */ struct ArchCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ CPULoongArchState env; QEMUTimer timer; uint32_t phy_id; /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; }; #define TYPE_LOONGARCH_CPU "loongarch-cpu" #define TYPE_LOONGARCH32_CPU "loongarch32-cpu" #define TYPE_LOONGARCH64_CPU "loongarch64-cpu" OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass, LOONGARCH_CPU) /** * LoongArchCPUClass: * @parent_realize: The parent class' realize handler. * @parent_phases: The parent class' reset phase handlers. * * A LoongArch CPU model. */ struct LoongArchCPUClass { /*< private >*/ CPUClass parent_class; /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; }; /* * 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_PLV_KERNEL 0 #define MMU_PLV_USER 3 #define MMU_IDX_KERNEL MMU_PLV_KERNEL #define MMU_IDX_USER MMU_PLV_USER #define MMU_IDX_DA 4 static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch) { #ifdef CONFIG_USER_ONLY return MMU_IDX_USER; #else if (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG)) { return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); } return MMU_IDX_DA; #endif } static inline bool is_la64(CPULoongArchState *env) { return FIELD_EX32(env->cpucfg[1], CPUCFG1, ARCH) == CPUCFG1_ARCH_LA64; } static inline bool is_va32(CPULoongArchState *env) { /* VA32 if !LA64 or VA32L[1-3] */ bool va32 = !is_la64(env); uint64_t plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); if (plv >= 1 && (FIELD_EX64(env->CSR_MISC, CSR_MISC, VA32) & (1 << plv))) { va32 = true; } return va32; } static inline void set_pc(CPULoongArchState *env, uint64_t value) { if (is_va32(env)) { env->pc = (uint32_t)value; } else { env->pc = value; } } /* * LoongArch CPUs hardware flags. */ #define HW_FLAGS_PLV_MASK R_CSR_CRMD_PLV_MASK /* 0x03 */ #define HW_FLAGS_EUEN_FPE 0x04 #define HW_FLAGS_EUEN_SXE 0x08 #define HW_FLAGS_CRMD_PG R_CSR_CRMD_PG_MASK /* 0x10 */ #define HW_FLAGS_VA32 0x20 #define HW_FLAGS_EUEN_ASXE 0x40 static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; *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; } 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 void loongarch_cpu_post_init(Object *obj); #endif /* LOONGARCH_CPU_H */