mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user