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:
parent
1158eb13f1
commit
80470b73de
2 changed files with 117 additions and 2 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue