target/loongarch: Update matched ptw bit A/D with PTW supported

With hardware PTE supported, bit A will be set if there is read access
or instruction fetch, and bit D will be set with write access.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Song Gao <gaosong@loongson.cn>
This commit is contained in:
Bibo Mao 2025-08-27 15:04:33 +08:00
parent 1158eb13f1
commit 80470b73de
2 changed files with 117 additions and 2 deletions

View file

@ -61,6 +61,32 @@ static inline bool pte_write(CPULoongArchState *env, uint64_t entry)
return !!writable;
}
/*
* The folloing functions should be called with PTW enable checked
* With hardware PTW enabled
* Bit D will be set by hardware with write access
* Bit A will be set by hardware with read/intruction fetch access
*/
static inline uint64_t pte_mkaccess(uint64_t entry)
{
return FIELD_DP64(entry, TLBENTRY, V, 1);
}
static inline uint64_t pte_mkdirty(uint64_t entry)
{
return FIELD_DP64(entry, TLBENTRY, D, 1);
}
static inline bool pte_access(uint64_t entry)
{
return !!FIELD_EX64(entry, TLBENTRY, V);
}
static inline bool pte_dirty(uint64_t entry)
{
return !!FIELD_EX64(entry, TLBENTRY, D);
}
bool check_ps(CPULoongArchState *ent, uint8_t ps);
TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx);

View file

@ -107,15 +107,52 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context,
return TLBRET_MATCH;
}
static MemTxResult loongarch_cmpxchg_phys(CPUState *cs, hwaddr phys,
uint64_t old, uint64_t new)
{
hwaddr addr1, l = 8;
MemoryRegion *mr;
uint8_t *ram_ptr;
uint64_t old1;
MemTxResult ret;
rcu_read_lock();
mr = address_space_translate(cs->as, phys, &addr1, &l,
false, MEMTXATTRS_UNSPECIFIED);
if (!memory_region_is_ram(mr)) {
/*
* Misconfigured PTE in ROM (AD bits are not preset) or
* PTE is in IO space and can't be updated atomically.
*/
rcu_read_unlock();
return MEMTX_ACCESS_ERROR;
}
ram_ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
old1 = qatomic_cmpxchg((uint64_t *)ram_ptr, cpu_to_le64(old),
cpu_to_le64(new));
old1 = le64_to_cpu(old1);
if (old1 == old) {
ret = MEMTX_OK;
} else {
ret = MEMTX_DECODE_ERROR;
}
rcu_read_unlock();
return ret;
}
TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
int access_type, int mmu_idx, int debug)
{
CPUState *cs = env_cpu(env);
target_ulong index = 0, phys = 0;
uint64_t dir_base, dir_width;
uint64_t base;
uint64_t base, pte;
int level;
vaddr address;
TLBRet ret;
MemTxResult ret1;
address = context->addr;
if ((address >> 63) & 0x1) {
@ -149,7 +186,9 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
}
}
restart:
/* pte */
pte = base;
if (level > 0) {
/* Huge Page. base is pte */
base = FIELD_DP64(base, TLBENTRY, LEVEL, 0);
@ -171,7 +210,57 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
context->ps = dir_base;
context->pte = base;
return loongarch_check_pte(env, context, access_type, mmu_idx);
ret = loongarch_check_pte(env, context, access_type, mmu_idx);
if (debug) {
return ret;
}
/*
* Update bit A/D with hardware PTW supported
*
* Need atomic compchxg operation with pte update, other vCPUs may
* update pte at the same time.
*/
if (ret == TLBRET_MATCH && cpu_has_ptw(env)) {
if (access_type == MMU_DATA_STORE && pte_dirty(base)) {
return ret;
}
if (access_type != MMU_DATA_STORE && pte_access(base)) {
return ret;
}
base = pte_mkaccess(pte);
if (access_type == MMU_DATA_STORE) {
base = pte_mkdirty(base);
}
ret1 = loongarch_cmpxchg_phys(cs, phys, pte, base);
/* PTE updated by other CPU, reload PTE entry */
if (ret1 == MEMTX_DECODE_ERROR) {
base = ldq_phys(cs->as, phys);
goto restart;
}
base = context->pte_buddy[index];
base = pte_mkaccess(base);
if (access_type == MMU_DATA_STORE) {
base = pte_mkdirty(base);
}
context->pte_buddy[index] = base;
/* Bit A/D need be updated with both Even/Odd page with huge pte */
if (level > 0) {
index = 1 - index;
base = context->pte_buddy[index];
base = pte_mkaccess(base);
if (access_type == MMU_DATA_STORE) {
base = pte_mkdirty(base);
}
context->pte_buddy[index] = base;
}
}
return ret;
}
static TLBRet loongarch_map_address(CPULoongArchState *env,