diff mbox series

feat: add loongarch page table walker support for debugger memory access

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

Commit Message

Miao Hao Dec. 19, 2024, 3:24 a.m. UTC
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(-)

Comments

Bibo Mao Dec. 19, 2024, 9:57 a.m. UTC | #1
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 mbox series

Patch

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,