Merge tag 'pull-loongarch-20250829' of https://github.com/bibo-mao/qemu into staging

loongarch queue

# -----BEGIN PGP SIGNATURE-----
#
# iHUEABYKAB0WIQQNhkKjomWfgLCz0aQfewwSUazn0QUCaLEK3AAKCRAfewwSUazn
# 0ZbOAQD5zRl292WYzl6qCWe+MIx+7T3rqiq8E/MkAUPhPSF2gAD8DYCQr1u+7le6
# pwzGx5iHygCzeTjgV4KuciGGqa8y8AA=
# =OX6j
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 29 Aug 2025 12:05:16 PM AEST
# gpg:                using EDDSA key 0D8642A3A2659F80B0B3D1A41F7B0C1251ACE7D1
# gpg: Good signature from "bibo mao <maobibo@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: 7044 3A00 19C0 E97A 31C7  13C4 8E86 8FB7 A176 9D4C
#      Subkey fingerprint: 0D86 42A3 A265 9F80 B0B3  D1A4 1F7B 0C12 51AC E7D1

* tag 'pull-loongarch-20250829' of https://github.com/bibo-mao/qemu:
  target/loongarch: Use correct address when flush tlb
  target/loongarch: Use MMUContext in get_physical_address()
  target/loongarch: Use MMUContext in loongarch_map_address()
  target/loongarch: Use MMUContext in loongarch_get_addr_from_tlb
  target/loongarch: Use MMUConext in loongarch_map_tlb_entry()
  target/loongarch: Use loongarch_check_pte in loongarch_page_table_walker
  target/loongarch: Add common function loongarch_check_pte()
  target/loongarch: Use MMUAccessType in loongarch_map_tlb_entry()
  target/loongarch: Use vaddr in get_physical_address()
  target/loongarch: Add enum type TLBRet definition
  target/loongarch: Add header file cpu-mmu.h
  target/loongarch: Set page size in TLB entry with STLB
  target/loongarch: Define function loongarch_cpu_post_init as static
  target/loongarch: Move some function definition to kvm directory

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson
2025-09-01 11:15:06 +10:00
10 changed files with 270 additions and 281 deletions
+1
View File
@@ -46,6 +46,7 @@
#include "hw/block/flash.h"
#include "hw/virtio/virtio-iommu.h"
#include "qemu/error-report.h"
#include "kvm/kvm_loongarch.h"
static void virt_get_veiointc(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
+40
View File
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* LoongArch CPU parameters for QEMU.
*
* Copyright (c) 2025 Loongson Technology Corporation Limited
*/
#ifndef LOONGARCH_CPU_MMU_H
#define LOONGARCH_CPU_MMU_H
typedef enum TLBRet {
TLBRET_MATCH,
TLBRET_BADADDR,
TLBRET_NOMATCH,
TLBRET_INVALID,
TLBRET_DIRTY,
TLBRET_RI,
TLBRET_XI,
TLBRET_PE,
} TLBRet;
typedef struct MMUContext {
vaddr addr;
uint64_t pte;
hwaddr physical;
int ps; /* page size shift */
int prot;
} MMUContext;
bool check_ps(CPULoongArchState *ent, uint8_t ps);
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx);
TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
int is_debug);
void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
uint64_t *dir_width, target_ulong level);
hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
#endif /* LOONGARCH_CPU_MMU_H */
+91 -90
View File
@@ -17,6 +17,7 @@
#include "hw/qdev-properties.h"
#include "exec/translation-block.h"
#include "cpu.h"
#include "cpu-mmu.h"
#include "internals.h"
#include "fpu/softfloat-helpers.h"
#include "csr.h"
@@ -422,6 +423,96 @@ static void loongarch_la464_init_csr(Object *obj)
#endif
}
static bool loongarch_get_lsx(Object *obj, Error **errp)
{
return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF;
}
static void loongarch_set_lsx(Object *obj, bool value, Error **errp)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
uint32_t val;
cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
if (cpu->lsx == ON_OFF_AUTO_OFF) {
cpu->lasx = ON_OFF_AUTO_OFF;
if (cpu->lasx == ON_OFF_AUTO_ON) {
error_setg(errp, "Failed to disable LSX since LASX is enabled");
return;
}
}
if (kvm_enabled()) {
/* kvm feature detection in function kvm_arch_init_vcpu */
return;
}
/* LSX feature detection in TCG mode */
val = cpu->env.cpucfg[2];
if (cpu->lsx == ON_OFF_AUTO_ON) {
if (FIELD_EX32(val, CPUCFG2, LSX) == 0) {
error_setg(errp, "Failed to enable LSX in TCG mode");
return;
}
} else {
cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0);
val = cpu->env.cpucfg[2];
}
cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value);
}
static bool loongarch_get_lasx(Object *obj, Error **errp)
{
return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF;
}
static void loongarch_set_lasx(Object *obj, bool value, Error **errp)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
uint32_t val;
cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) {
error_setg(errp, "Failed to enable LASX since lSX is disabled");
return;
}
if (kvm_enabled()) {
/* kvm feature detection in function kvm_arch_init_vcpu */
return;
}
/* LASX feature detection in TCG mode */
val = cpu->env.cpucfg[2];
if (cpu->lasx == ON_OFF_AUTO_ON) {
if (FIELD_EX32(val, CPUCFG2, LASX) == 0) {
error_setg(errp, "Failed to enable LASX in TCG mode");
return;
}
}
cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value);
}
static void loongarch_cpu_post_init(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
cpu->lbt = ON_OFF_AUTO_OFF;
cpu->pmu = ON_OFF_AUTO_OFF;
cpu->lsx = ON_OFF_AUTO_AUTO;
cpu->lasx = ON_OFF_AUTO_AUTO;
object_property_add_bool(obj, "lsx", loongarch_get_lsx,
loongarch_set_lsx);
object_property_add_bool(obj, "lasx", loongarch_get_lasx,
loongarch_set_lasx);
/* lbt is enabled only in kvm mode, not supported in tcg mode */
if (kvm_enabled()) {
kvm_loongarch_cpu_post_init(cpu);
}
}
static void loongarch_la464_initfn(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
@@ -683,96 +774,6 @@ static void loongarch_cpu_unrealizefn(DeviceState *dev)
lacc->parent_unrealize(dev);
}
static bool loongarch_get_lsx(Object *obj, Error **errp)
{
return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF;
}
static void loongarch_set_lsx(Object *obj, bool value, Error **errp)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
uint32_t val;
cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
if (cpu->lsx == ON_OFF_AUTO_OFF) {
cpu->lasx = ON_OFF_AUTO_OFF;
if (cpu->lasx == ON_OFF_AUTO_ON) {
error_setg(errp, "Failed to disable LSX since LASX is enabled");
return;
}
}
if (kvm_enabled()) {
/* kvm feature detection in function kvm_arch_init_vcpu */
return;
}
/* LSX feature detection in TCG mode */
val = cpu->env.cpucfg[2];
if (cpu->lsx == ON_OFF_AUTO_ON) {
if (FIELD_EX32(val, CPUCFG2, LSX) == 0) {
error_setg(errp, "Failed to enable LSX in TCG mode");
return;
}
} else {
cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0);
val = cpu->env.cpucfg[2];
}
cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value);
}
static bool loongarch_get_lasx(Object *obj, Error **errp)
{
return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF;
}
static void loongarch_set_lasx(Object *obj, bool value, Error **errp)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
uint32_t val;
cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) {
error_setg(errp, "Failed to enable LASX since lSX is disabled");
return;
}
if (kvm_enabled()) {
/* kvm feature detection in function kvm_arch_init_vcpu */
return;
}
/* LASX feature detection in TCG mode */
val = cpu->env.cpucfg[2];
if (cpu->lasx == ON_OFF_AUTO_ON) {
if (FIELD_EX32(val, CPUCFG2, LASX) == 0) {
error_setg(errp, "Failed to enable LASX in TCG mode");
return;
}
}
cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value);
}
void loongarch_cpu_post_init(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
cpu->lbt = ON_OFF_AUTO_OFF;
cpu->pmu = ON_OFF_AUTO_OFF;
cpu->lsx = ON_OFF_AUTO_AUTO;
cpu->lasx = ON_OFF_AUTO_AUTO;
object_property_add_bool(obj, "lsx", loongarch_get_lsx,
loongarch_set_lsx);
object_property_add_bool(obj, "lasx", loongarch_get_lasx,
loongarch_set_lasx);
/* lbt is enabled only in kvm mode, not supported in tcg mode */
if (kvm_enabled()) {
kvm_loongarch_cpu_post_init(cpu);
}
}
static void loongarch_cpu_init(Object *obj)
{
#ifndef CONFIG_USER_ONLY
-11
View File
@@ -494,15 +494,4 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value)
#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
void loongarch_cpu_post_init(Object *obj);
#ifdef CONFIG_KVM
void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu);
#else
static inline void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu)
{
}
#endif
void kvm_loongarch_init_irq_routing(void);
#endif /* LOONGARCH_CPU_H */
+93 -49
View File
@@ -13,6 +13,7 @@
#include "exec/target_page.h"
#include "internals.h"
#include "cpu-csr.h"
#include "cpu-mmu.h"
#include "tcg/tcg_loongarch.h"
void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
@@ -43,15 +44,79 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
}
}
static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address)
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx)
{
uint64_t plv = mmu_idx;
uint64_t tlb_entry, tlb_ppn;
uint8_t tlb_ps, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
tlb_entry = context->pte;
tlb_ps = context->ps;
tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V);
tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D);
tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
if (is_la64(env)) {
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN);
tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX);
tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR);
tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV);
} else {
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN);
tlb_nx = 0;
tlb_nr = 0;
tlb_rplv = 0;
}
/* Remove sw bit between bit12 -- bit PS*/
tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1));
/* 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;
}
context->physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) |
(context->addr & MAKE_64BIT_MASK(0, tlb_ps));
context->prot = PAGE_READ;
if (tlb_d) {
context->prot |= PAGE_WRITE;
}
if (!tlb_nx) {
context->prot |= PAGE_EXEC;
}
return TLBRET_MATCH;
}
static TLBRet loongarch_page_table_walker(CPULoongArchState *env,
MMUContext *context,
int access_type, int mmu_idx)
{
CPUState *cs = env_cpu(env);
target_ulong index, phys;
uint64_t dir_base, dir_width;
uint64_t base;
int level;
vaddr address;
address = context->addr;
if ((address >> 63) & 0x1) {
base = env->CSR_PGDH;
} else {
@@ -93,41 +158,20 @@ static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical,
base = ldq_phys(cs->as, phys);
}
/* TODO: check plv and other bits? */
/* base is pte, in normal pte format */
if (!FIELD_EX64(base, TLBENTRY, V)) {
return TLBRET_NOMATCH;
}
if (!FIELD_EX64(base, TLBENTRY, D)) {
*prot = PAGE_READ;
} else {
*prot = PAGE_READ | PAGE_WRITE;
}
/* get TARGET_PAGE_SIZE aligned physical address */
base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1);
/* mask RPLV, NX, NR bits */
base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0);
base = FIELD_DP64(base, TLBENTRY_64, NX, 0);
base = FIELD_DP64(base, TLBENTRY_64, NR, 0);
/* mask other attribute bits */
*physical = base & TARGET_PAGE_MASK;
return 0;
context->ps = dir_base;
context->pte = base;
return loongarch_check_pte(env, context, access_type, mmu_idx);
}
static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
MMUAccessType access_type, int mmu_idx,
int is_debug)
static TLBRet loongarch_map_address(CPULoongArchState *env,
MMUContext *context,
MMUAccessType access_type, int mmu_idx,
int is_debug)
{
int ret;
TLBRet ret;
if (tcg_enabled()) {
ret = loongarch_get_addr_from_tlb(env, physical, prot, address,
access_type, mmu_idx);
ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx);
if (ret != TLBRET_NOMATCH) {
return ret;
}
@@ -139,14 +183,13 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
* legal mapping, even if the mapping is not yet in TLB. return 0 if
* there is a valid map, else none zero.
*/
return loongarch_page_table_walker(env, physical, prot, address);
return loongarch_page_table_walker(env, context, access_type, mmu_idx);
}
return TLBRET_NOMATCH;
}
static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va,
target_ulong dmw)
static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, target_ulong dmw)
{
if (is_la64(env)) {
return va & TARGET_VIRT_MASK;
@@ -157,9 +200,9 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va,
}
}
int get_physical_address(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
MMUAccessType access_type, int mmu_idx, int is_debug)
TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
int is_debug)
{
int user_mode = mmu_idx == MMU_USER_IDX;
int kernel_mode = mmu_idx == MMU_KERNEL_IDX;
@@ -167,11 +210,13 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical,
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);
vaddr address;
/* Check PG and DA */
address = context->addr;
if (da & !pg) {
*physical = address & TARGET_PHYS_MASK;
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
context->physical = address & TARGET_PHYS_MASK;
context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return TLBRET_MATCH;
}
@@ -189,8 +234,8 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical,
base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG);
}
if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) {
*physical = dmw_va2pa(env, address, env->CSR_DMW[i]);
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
context->physical = dmw_va2pa(env, address, env->CSR_DMW[i]);
context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return TLBRET_MATCH;
}
}
@@ -202,19 +247,18 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical,
}
/* Mapped address */
return loongarch_map_address(env, physical, prot, address,
access_type, mmu_idx, is_debug);
return loongarch_map_address(env, context, access_type, mmu_idx, is_debug);
}
hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
CPULoongArchState *env = cpu_env(cs);
hwaddr phys_addr;
int prot;
MMUContext context;
if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
cpu_mmu_index(cs, false), 1) != 0) {
context.addr = addr;
if (get_physical_address(env, &context, MMU_DATA_LOAD,
cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) {
return -1;
}
return phys_addr;
return context.physical;
}
-20
View File
@@ -32,19 +32,6 @@ void restore_fp_status(CPULoongArchState *env);
#endif
#ifndef CONFIG_USER_ONLY
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,
};
bool check_ps(CPULoongArchState *ent, uint8_t ps);
extern const VMStateDescription vmstate_loongarch_cpu;
void loongarch_cpu_set_irq(void *opaque, int irq, int level);
@@ -54,13 +41,6 @@ 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);
int get_physical_address(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
MMUAccessType access_type, int mmu_idx, int is_debug);
void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
uint64_t *dir_width, target_ulong level);
hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
#endif /* !CONFIG_USER_ONLY */
uint64_t read_fcc(CPULoongArchState *env);
+2 -2
View File
@@ -5,11 +5,11 @@
* Copyright (c) 2023 Loongson Technology Corporation Limited
*/
#include "cpu.h"
#ifndef QEMU_KVM_LOONGARCH_H
#define QEMU_KVM_LOONGARCH_H
void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu);
void kvm_loongarch_init_irq_routing(void);
int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level);
void kvm_arch_reset_vcpu(CPUState *cs);
+1
View File
@@ -16,6 +16,7 @@
#include "accel/tcg/cpu-ldst.h"
#include "hw/irq.h"
#include "cpu-csr.h"
#include "cpu-mmu.h"
target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val)
{
+4 -3
View File
@@ -7,6 +7,7 @@
#ifndef TARGET_LOONGARCH_TCG_LOONGARCH_H
#define TARGET_LOONGARCH_TCG_LOONGARCH_H
#include "cpu.h"
#include "cpu-mmu.h"
void loongarch_csr_translate_init(void);
@@ -14,8 +15,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
MMUAccessType access_type, int mmu_idx);
TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
MMUContext *context,
MMUAccessType access_type, int mmu_idx);
#endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */
+38 -106
View File
@@ -10,6 +10,7 @@
#include "qemu/guest-random.h"
#include "cpu.h"
#include "cpu-mmu.h"
#include "internals.h"
#include "exec/helper-proto.h"
#include "exec/cputlb.h"
@@ -28,8 +29,8 @@ bool check_ps(CPULoongArchState *env, uint8_t tlb_ps)
return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2);
}
static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
MMUAccessType access_type, int tlb_error)
static void raise_mmu_exception(CPULoongArchState *env, vaddr address,
MMUAccessType access_type, TLBRet tlb_error)
{
CPUState *cs = env_cpu(env);
@@ -110,23 +111,20 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index)
if (!tlb_e) {
return;
}
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_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
pagesize = MAKE_64BIT_MASK(tlb_ps, 1);
mask = MAKE_64BIT_MASK(0, tlb_ps + 1);
addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask;
addr = sextract64(addr, 0, TARGET_VIRT_ADDR_SPACE_BITS);
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,
tlb_flush_range_by_mmuidx(env_cpu(env), addr + pagesize, pagesize,
mmu_idx, TARGET_LONG_BITS);
}
}
@@ -173,11 +171,8 @@ static void fill_tlb_entry(CPULoongArchState *env, int index)
lo1 = env->CSR_TLBELO1;
}
/* Only MTLB has the ps fields */
if (index >= LOONGARCH_STLB) {
tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
}
/* Store page size in field PS */
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);
@@ -203,7 +198,7 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high)
* 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,
static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr,
int *index)
{
LoongArchTLB *tlb;
@@ -283,12 +278,7 @@ void helper_tlbrd(CPULoongArchState *env)
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_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
if (!tlb_e) {
@@ -476,11 +466,8 @@ void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info,
if (!tlb_e) {
continue;
}
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_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, 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;
@@ -509,11 +496,8 @@ void helper_invtlb_page_asid_or_g(CPULoongArchState *env,
if (!tlb_e) {
continue;
}
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_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, 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;
@@ -533,13 +517,15 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
CPULoongArchState *env = cpu_env(cs);
hwaddr physical;
int prot;
int ret;
MMUContext context;
TLBRet ret;
/* Data access */
ret = get_physical_address(env, &physical, &prot, address,
access_type, mmu_idx, 0);
context.addr = address;
ret = get_physical_address(env, &context, access_type, mmu_idx, 0);
if (ret == TLBRET_MATCH) {
physical = context.physical;
prot = context.prot;
tlb_set_page(cs, address & TARGET_PAGE_MASK,
physical & TARGET_PAGE_MASK, prot,
mmu_idx, TARGET_PAGE_SIZE);
@@ -664,85 +650,31 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
}
static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
int access_type, int index, int mmu_idx)
static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env,
MMUContext *context,
MMUAccessType 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;
uint8_t tlb_ps, n;
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);
if (is_la64(env)) {
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN);
tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX);
tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR);
tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV);
} else {
tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN);
tlb_nx = 0;
tlb_nr = 0;
tlb_rplv = 0;
}
/* Remove sw bit between bit12 -- bit PS*/
tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1));
/* 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;
}
*physical = (tlb_ppn << R_TLBENTRY_64_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;
tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
n = (context->addr >> tlb_ps) & 0x1;/* Odd or even */
context->pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
context->ps = tlb_ps;
return loongarch_check_pte(env, context, access_type, mmu_idx);
}
int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
MMUAccessType access_type, int mmu_idx)
TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env,
MMUContext *context,
MMUAccessType access_type, int mmu_idx)
{
int index, match;
match = loongarch_tlb_search(env, address, &index);
match = loongarch_tlb_search(env, context->addr, &index);
if (match) {
return loongarch_map_tlb_entry(env, physical, prot,
address, access_type, index, mmu_idx);
return loongarch_map_tlb_entry(env, context, access_type, index,
mmu_idx);
}
return TLBRET_NOMATCH;