Message ID | 20241219032455.28608-1-haomiao23s@ict.ac.cn (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | feat: add loongarch page table walker support for debugger memory access | expand |
Hi Miao, Thanks for doing this. It is useful to debug VM. On 2024/12/19 上午11:24, Miao Hao wrote: > Signed-off-by: Miao Hao <haomiao23s@ict.ac.cn> > --- > target/loongarch/cpu_helper.c | 104 ++++++++++++++++++++++++++++-- > target/loongarch/internals.h | 4 +- > target/loongarch/tcg/tlb_helper.c | 4 +- > 3 files changed, 104 insertions(+), 8 deletions(-) > > diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c > index 580362ac3e..c0828a813d 100644 > --- a/target/loongarch/cpu_helper.c > +++ b/target/loongarch/cpu_helper.c > @@ -141,9 +141,95 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, > return false; > } > > +static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, > + int *prot, target_ulong address) > +{ > + CPUState *cs = env_cpu(env); > + target_ulong index, phys; > + int shift; > + uint64_t dir_base, dir_width; > + uint64_t base; > + int level; > + > + /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ > + shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); > + shift = (shift + 1) * 3; > + > + if ((address >> 63) & 0x1) { > + base = env->CSR_PGDH; > + } else { > + base = env->CSR_PGDL; > + } > + base &= TARGET_PHYS_MASK; > + > + for (level = 4; level > 0; level--) { > + get_dir_base_width(env, &dir_base, &dir_width, level); > + > + if (dir_width != 0) { how about check whether it equeal to 0 firstly like this? if (dir_width == 0) continue; > + /* get next level page directory */ > + index = (address >> dir_base) & ((1 << dir_width) - 1); > + phys = base | index << shift; Here will only load first 64bit if shift is not 0, such as 1:128bit, 2:192bit, 3:256bit > + base = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; > + if (!FIELD_EX64(base, TLBENTRY, HUGE)) { > + /* mask off page dir permission bits */ > + base &= TARGET_PAGE_MASK; > + } else { > + /* base is a huge pte */ > + break; > + } > + > + if (base == 0) { physical adddress 0 is valid and Valid bit will be checked in later. Can we remove this? > + return TLBRET_NOMATCH; > + } > + } > + } > + > + /* pte */ > + if (FIELD_EX64(base, TLBENTRY, HUGE)) { > + /* Huge Page. base is pte */ > + base = FIELD_DP64(base, TLBENTRY, LEVEL, 0); > + base = FIELD_DP64(base, TLBENTRY, HUGE, 0); > + if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) { > + base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0); > + base = FIELD_DP64(base, TLBENTRY, G, 1); > + } > + } else { > + /* Normal Page. base points to pte */ > + get_dir_base_width(env, &dir_base, &dir_width, 0); > + index = (address >> dir_base) & ((1 << dir_width) - 1); > + phys = base | index << shift; Ditto, shift may be wider than 64-bit. Regards Bibo Mao > + 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; > +} > + > static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, > int *prot, target_ulong address, > - MMUAccessType access_type, int mmu_idx) > + MMUAccessType access_type, int mmu_idx, > + int is_debug) > { > int index, match; > > @@ -151,6 +237,13 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, > if (match) { > return loongarch_map_tlb_entry(env, physical, prot, > address, access_type, index, mmu_idx); > + } else if (is_debug) { > + /* > + * For debugger memory access, we want to do the map when there is a > + * 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 TLBRET_NOMATCH; > @@ -158,7 +251,8 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, > #else > static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, > int *prot, target_ulong address, > - MMUAccessType access_type, int mmu_idx) > + MMUAccessType access_type, int mmu_idx, > + int is_debug) > { > return TLBRET_NOMATCH; > } > @@ -178,7 +272,7 @@ 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) > + 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; > @@ -222,7 +316,7 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, > > /* Mapped address */ > return loongarch_map_address(env, physical, prot, address, > - access_type, mmu_idx); > + access_type, mmu_idx, is_debug); > } > > hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) > @@ -232,7 +326,7 @@ hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) > int prot; > > if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, > - cpu_mmu_index(cs, false)) != 0) { > + cpu_mmu_index(cs, false), 1) != 0) { > return -1; > } > return phys_addr; > diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h > index 1a02427627..bc2ca30746 100644 > --- a/target/loongarch/internals.h > +++ b/target/loongarch/internals.h > @@ -56,7 +56,9 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, > int *index); > int get_physical_address(CPULoongArchState *env, hwaddr *physical, > int *prot, target_ulong address, > - MMUAccessType access_type, int mmu_idx); > + 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); > > #ifdef CONFIG_TCG > diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c > index 97f38fc391..564f336df9 100644 > --- a/target/loongarch/tcg/tlb_helper.c > +++ b/target/loongarch/tcg/tlb_helper.c > @@ -18,7 +18,7 @@ > #include "exec/log.h" > #include "cpu-csr.h" > > -static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, > +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, > uint64_t *dir_width, target_ulong level) > { > switch (level) { > @@ -485,7 +485,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, > > /* Data access */ > ret = get_physical_address(env, &physical, &prot, address, > - access_type, mmu_idx); > + access_type, mmu_idx, 0); > > if (ret == TLBRET_MATCH) { > tlb_set_page(cs, address & TARGET_PAGE_MASK, >
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 580362ac3e..c0828a813d 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -141,9 +141,95 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, return false; } +static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address) +{ + CPUState *cs = env_cpu(env); + target_ulong index, phys; + int shift; + uint64_t dir_base, dir_width; + uint64_t base; + int level; + + /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ + shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); + shift = (shift + 1) * 3; + + if ((address >> 63) & 0x1) { + base = env->CSR_PGDH; + } else { + base = env->CSR_PGDL; + } + base &= TARGET_PHYS_MASK; + + for (level = 4; level > 0; level--) { + get_dir_base_width(env, &dir_base, &dir_width, level); + + if (dir_width != 0) { + /* get next level page directory */ + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << shift; + base = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; + if (!FIELD_EX64(base, TLBENTRY, HUGE)) { + /* mask off page dir permission bits */ + base &= TARGET_PAGE_MASK; + } else { + /* base is a huge pte */ + break; + } + + if (base == 0) { + return TLBRET_NOMATCH; + } + } + } + + /* pte */ + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* Huge Page. base is pte */ + base = FIELD_DP64(base, TLBENTRY, LEVEL, 0); + base = FIELD_DP64(base, TLBENTRY, HUGE, 0); + if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) { + base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0); + base = FIELD_DP64(base, TLBENTRY, G, 1); + } + } else { + /* Normal Page. base points to pte */ + get_dir_base_width(env, &dir_base, &dir_width, 0); + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << shift; + 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; +} + static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) + MMUAccessType access_type, int mmu_idx, + int is_debug) { int index, match; @@ -151,6 +237,13 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, if (match) { return loongarch_map_tlb_entry(env, physical, prot, address, access_type, index, mmu_idx); + } else if (is_debug) { + /* + * For debugger memory access, we want to do the map when there is a + * 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 TLBRET_NOMATCH; @@ -158,7 +251,8 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, #else static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) + MMUAccessType access_type, int mmu_idx, + int is_debug) { return TLBRET_NOMATCH; } @@ -178,7 +272,7 @@ 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) + 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; @@ -222,7 +316,7 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, /* Mapped address */ return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx); + access_type, mmu_idx, is_debug); } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) @@ -232,7 +326,7 @@ hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(cs, false)) != 0) { + cpu_mmu_index(cs, false), 1) != 0) { return -1; } return phys_addr; diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 1a02427627..bc2ca30746 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -56,7 +56,9 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, int *index); int get_physical_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx); + 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); #ifdef CONFIG_TCG diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 97f38fc391..564f336df9 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -18,7 +18,7 @@ #include "exec/log.h" #include "cpu-csr.h" -static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level) { switch (level) { @@ -485,7 +485,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, /* Data access */ ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx); + access_type, mmu_idx, 0); if (ret == TLBRET_MATCH) { tlb_set_page(cs, address & TARGET_PAGE_MASK,
Signed-off-by: Miao Hao <haomiao23s@ict.ac.cn> --- target/loongarch/cpu_helper.c | 104 ++++++++++++++++++++++++++++-- target/loongarch/internals.h | 4 +- target/loongarch/tcg/tlb_helper.c | 4 +- 3 files changed, 104 insertions(+), 8 deletions(-)