├── README.md ├── SV39.scm ├── SV48.scm ├── img ├── paging.png └── v2p.png ├── paging_debug.py ├── paging_debug.scm └── utils.scm /README.md: -------------------------------------------------------------------------------- 1 | # RISCV-GDB-Paging 2 | **SV39** Paging Debug tool for GDB using python and guile 3 | 4 | Reference: [riscv-privileged-v1.10.pdf](https://riscv.org/wp-content/uploads/2017/05/riscv-privileged-v1.10.pdf) 5 | 6 | ## Usage: 7 | Place `paging_debug.py` or all the `.scm` files inside your project root. 8 | 9 | Inside gdb console: 10 | 11 | ```(gdb) so paging_debug.py``` 12 | 13 | or 14 | 15 | ```(gdb) so paging_debug.scm``` 16 | 17 | Then you can type `help paging` or `help v2p` to show the help message. 18 | 19 | If you have any question, please search for `GDB Extension`. 20 | 21 | > Note: I have no SV48 codes and emulator, so `SV48.scm` is not tested. 22 | 23 | ### Paging Table Inspector 24 | ``` 25 | > (gdb) help paging 26 | 27 | RISC-V SV39 MMU Paging Debugging tool. 28 | Usage: 29 | paging : The shortcut of `paging satp` 30 | paging satp : Show page table from satp register. 31 | paging addr : Show page table at addr. 32 | Example: 33 | (gdb) paging 34 | (gdb) paging satp 35 | (gdb) paging 0x12340000 36 | ``` 37 | 38 | ### Virtual Address To Physical Address 39 | ``` 40 | (gdb) help v2p 41 | 42 | RISC-V SV39 MMU Paging Debugging tool. 43 | Usage: 44 | v2p add : Get Physical address of a virtual address from pagetable at satp. 45 | v2p pg_addr addr : Get Physical address of a virtual address from pagetable at pg_addr. 46 | Example: 47 | (gdb) v2p 0x12345678 48 | (gdb) v2p 0x81230000 0x12345678 49 | 50 | ``` 51 | 52 | ## Example 53 | 54 | paging: 55 | 56 | ![paging](img/paging.png) 57 | 58 | v2p: 59 | 60 | ![v2p](img/v2p.png) 61 | 62 | 63 | ## Todo 64 | 65 | After finished oscomp (or a debug routine is hard for me), I may implement features list below: 66 | 67 | 1. Processes debug for custom os (flexible via user defines debug symbol, like FreeRTOS GDB). 68 | 2. Ecall tracing 69 | 3. named bitfield for register 70 | -------------------------------------------------------------------------------- /SV39.scm: -------------------------------------------------------------------------------- 1 | ; SV39 implementation 2 | (define-module (SV39) 3 | #:export ( 4 | pte-at 5 | v2p 6 | print-type 7 | pte-max-level 8 | pte-level-size 9 | pte-per-page 10 | pte-type 11 | is-pte-valid? 12 | is-pte-a-pde? 13 | )) 14 | 15 | (load "utils.scm") 16 | 17 | (use-modules ((gdb) #:prefix gdb:)) 18 | (use-modules (utils)) 19 | 20 | (define page-size 4096) ; in bytes 21 | (define pte-size 8) ; in bytes 22 | (define pte-per-page (/ page-size pte-size)) 23 | (define pte-max-level 3) 24 | (define pte-level-size 25 | (letrec ( 26 | (l (letrec 27 | ((size (lambda (lvl pre) 28 | (let ((cur (* pre pte-per-page))) 29 | (if (>= lvl pte-max-level) 30 | '() 31 | (cons cur (size (+ lvl 1) cur)) 32 | ))))) 33 | (size 0 (/ page-size pte-per-page)) 34 | )) 35 | (reverse-list (lambda (l) 36 | (if (null? l) '() (append (reverse-list (cdr l)) (list (car l))))))) 37 | (reverse-list l))) 38 | 39 | ; PTE gdb type 40 | (define pte-type (gdb:lookup-type "unsigned long")) 41 | ; PTE destruct 42 | (define (pte-destruct pte) 43 | (cons 44 | ; Physical address 45 | (ash (ash pte -10) 12) 46 | ; Type bit-vector 47 | (bit-vector pte 0 9) 48 | )) 49 | ; PTE Check 50 | (define (is-pte-valid? pte) (cadr pte)) 51 | (define (is-pte-a-pde? pte) 52 | (let ((type (cdr pte))) (not (or (list-ref type 1) (list-ref type 2) (list-ref type 3))))) 53 | ; Get PTE via ID and pgdir 54 | (define (pte-at pgdir id) 55 | (pte-destruct (gdb:value->integer (gdb:value-subscript pgdir id)))) 56 | 57 | ; SV39 PTE Type bits names 58 | (define type-names (list "V" "R" "W" "X" "U" "G" "A" "D" "RS1" "RS2")) 59 | 60 | (define (print-type type sep) 61 | (for-loop 0 9 62 | (lambda (i) 63 | (if (list-ref type i) 64 | (begin 65 | (display (list-ref type-names i)) 66 | (display sep)))))) 67 | 68 | ; VA destruct 69 | (define (va-destruct va) 70 | (let 71 | ((vpn-mask #b111111111) 72 | (offset-mask #b111111111111) 73 | (va (string->number (make-guile-style-hex va)))) 74 | (list 75 | ; VPN 2 76 | (logand (ash va -30) vpn-mask) 77 | ; VPN 1 78 | (logand (ash va -21) vpn-mask) 79 | ; VPN 0 80 | (logand (ash va -12) vpn-mask) 81 | ; offset 82 | (logand va offset-mask) 83 | ))) 84 | 85 | ; V2P 86 | (define (v2p pgdir va) 87 | (let ( 88 | (va (va-destruct va))) 89 | (letrec 90 | ((walk (lambda (pgdir level) 91 | (let* ( 92 | (pgdir (gdb:value-cast (gdb:make-value pgdir) (gdb:type-pointer pte-type))) 93 | (pte (pte-at pgdir (list-ref va level)))) 94 | (if (is-pte-valid? pte) 95 | (if (is-pte-a-pde? pte) 96 | (walk (car pte) (+ level 1)) ; Dir 97 | (cons 98 | (+ (car pte) (list-ref va pte-max-level)) 99 | (cdr pte)) ; Leaf 100 | ) ; Valid 101 | (gdb:throw-user-error "Virtual address not mapped or map not valid.") ; Not-valid 102 | ) 103 | ) 104 | ))) 105 | (walk pgdir 0) 106 | )) 107 | ) -------------------------------------------------------------------------------- /SV48.scm: -------------------------------------------------------------------------------- 1 | ; SV48 implementation 2 | (define-module (SV48) 3 | #:export ( 4 | pte-at 5 | v2p 6 | print-type 7 | pte-max-level 8 | pte-level-size 9 | pte-per-page 10 | pte-type 11 | is-pte-valid? 12 | is-pte-a-pde? 13 | )) 14 | 15 | (load "utils.scm") 16 | 17 | (use-modules ((gdb) #:prefix gdb:)) 18 | (use-modules (utils)) 19 | 20 | (define page-size 4096) ; in bytes 21 | (define pte-size 8) ; in bytes 22 | (define pte-per-page (/ page-size pte-size)) 23 | (define pte-max-level 4) 24 | (define pte-level-size 25 | (letrec ( 26 | (l (letrec 27 | ((size (lambda (lvl pre) 28 | (let ((cur (* pre pte-per-page))) 29 | (if (>= lvl pte-max-level) 30 | '() 31 | (cons cur (size (+ lvl 1) cur)) 32 | ))))) 33 | (size 0 (/ page-size pte-per-page)) 34 | )) 35 | (reverse-list (lambda (l) 36 | (if (null? l) '() (append (reverse-list (cdr l)) (list (car l))))))) 37 | (reverse-list l))) 38 | 39 | ; PTE gdb type 40 | (define pte-type (gdb:lookup-type "unsigned long")) 41 | ; PTE destruct 42 | (define (pte-destruct pte) 43 | (cons 44 | ; Physical address 45 | (ash (ash pte -10) 12) 46 | ; Type bit-vector 47 | (bit-vector pte 0 9) 48 | )) 49 | ; PTE Check 50 | (define (is-pte-valid? pte) (cadr pte)) 51 | (define (is-pte-a-pde? pte) 52 | (let ((type (cdr pte))) (not (or (list-ref type 1) (list-ref type 2) (list-ref type 3))))) 53 | ; Get PTE via ID and pgdir 54 | (define (pte-at pgdir id) 55 | (pte-destruct (gdb:value->integer (gdb:value-subscript pgdir id)))) 56 | 57 | ; SV39 PTE Type bits names 58 | (define type-names (list "V" "R" "W" "X" "U" "G" "A" "D" "RS1" "RS2")) 59 | 60 | (define (print-type type sep) 61 | (for-loop 0 9 62 | (lambda (i) 63 | (if (list-ref type i) 64 | (begin 65 | (display (list-ref type-names i)) 66 | (display sep)))))) 67 | 68 | ; VA destruct 69 | (define (va-destruct va) 70 | (let 71 | ((vpn-mask #b111111111) 72 | (offset-mask #b111111111111) 73 | (va (string->number (make-guile-style-hex va)))) 74 | (list 75 | ; VPN 3 76 | (logand (ash va -39) vpn-mask) 77 | ; VPN 2 78 | (logand (ash va -30) vpn-mask) 79 | ; VPN 1 80 | (logand (ash va -21) vpn-mask) 81 | ; VPN 0 82 | (logand (ash va -12) vpn-mask) 83 | ; offset 84 | (logand va offset-mask) 85 | ))) 86 | 87 | ; V2P 88 | (define (v2p pgdir va) 89 | (let ( 90 | (va (va-destruct va))) 91 | (letrec 92 | ((walk (lambda (pgdir level) 93 | (let* ( 94 | (pgdir (gdb:value-cast (gdb:make-value pgdir) (gdb:type-pointer pte-type))) 95 | (pte (pte-at pgdir (list-ref va level)))) 96 | (if (is-pte-valid? pte) 97 | (if (is-pte-a-pde? pte) 98 | (walk (car pte) (+ level 1)) ; Dir 99 | (cons 100 | (+ (car pte) (list-ref va pte-max-level)) 101 | (cdr pte)) ; Leaf 102 | ) ; Valid 103 | (gdb:throw-user-error "Virtual address not mapped or map not valid.") ; Not-valid 104 | ) 105 | ) 106 | ))) 107 | (walk pgdir 0) 108 | )) 109 | ) -------------------------------------------------------------------------------- /img/paging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oyami-Srk/RISCV-GDB-Paging/5092ebf39f0238613877af8dac2ce59a995ea4db/img/paging.png -------------------------------------------------------------------------------- /img/v2p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oyami-Srk/RISCV-GDB-Paging/5092ebf39f0238613877af8dac2ce59a995ea4db/img/v2p.png -------------------------------------------------------------------------------- /paging_debug.py: -------------------------------------------------------------------------------- 1 | # Usage: (gdb) so paging_debug.py 2 | # (gdb) help paging 3 | # (gdb) help v2p 4 | 5 | import gdb 6 | 7 | PAGE_SIZE = 4096 8 | PTE_PER_PAGE = 512 9 | LVL3_SIZE = PTE_PER_PAGE * PAGE_SIZE 10 | LVL2_SIZE = PTE_PER_PAGE * LVL3_SIZE 11 | LVL1_SIZE = PTE_PER_PAGE * LVL2_SIZE 12 | 13 | level_total_size = [ 14 | LVL1_SIZE, 15 | LVL2_SIZE, 16 | LVL3_SIZE, 17 | PAGE_SIZE 18 | ] 19 | 20 | show_invalid = False 21 | use_utf8 = True 22 | 23 | if not use_utf8: 24 | normal_line = '| ' 25 | branch_line = '|- ' 26 | type_separator = ' | ' 27 | else: 28 | normal_line = '│ ' 29 | branch_line = '├─ ' 30 | type_separator = ' │ ' 31 | 32 | u64 = gdb.lookup_type('unsigned long long') 33 | u64p = u64.pointer() 34 | 35 | def print_type(type, end=' '): 36 | if type & 0b1: 37 | print('V', end=end) 38 | if type & 0b10: 39 | print('R', end=end) 40 | if type & 0b100: 41 | print('W', end=end) 42 | if type & 0b1000: 43 | print('X', end=end) 44 | if type & 0b10000: 45 | print('U', end=end) 46 | if type & 0b100000: 47 | print('G', end=end) 48 | if type & 0b1000000: 49 | print('A', end=end) 50 | if type & 0b10000000: 51 | print('D', end=end) 52 | if type & 0b100000000: 53 | print('R1', end=end) 54 | if type & 0b1000000000: 55 | print('R2', end=end) 56 | 57 | 58 | def parse_page_table(pgdir: gdb.Value, level: int, offset: int): 59 | if level == 0: 60 | print(f"Page Table @ 0x{format(int(pgdir), 'X')}") 61 | last_pa = 0 62 | last_start = 0 63 | last_end = 0 64 | last_type = 0 65 | last_continue = False 66 | 67 | for id in range(0, PTE_PER_PAGE): 68 | pte = int(pgdir[id]) 69 | if pte != 0: 70 | pte = format(pte, '064b')[::-1] 71 | pte_ppn = int(pte[10:54][::-1], 2) 72 | V = int(pte[0]) 73 | R = int(pte[1]) 74 | W = int(pte[2]) 75 | X = int(pte[3]) 76 | type = int(pte[0:10][::-1], 2) 77 | if V or show_invalid: 78 | entry_size = level_total_size[level + 1] 79 | start = offset + id * entry_size 80 | pa = gdb.Value( 81 | pte_ppn * PAGE_SIZE).cast(u64p) 82 | if R == W == X == 0: 83 | # Dir Entry 84 | print(normal_line * (level) + branch_line, end='') 85 | print(f"Directory @ 0x{format(int(pa), 'X')}") 86 | parse_page_table(pa, level + 1, start) 87 | last_start = last_end = last_type = last_pa = 0 88 | last_continue = False 89 | else: 90 | # Leaf node 91 | pa = int(pa) 92 | end = start + entry_size 93 | if last_end == start and last_type == type and last_pa + (last_end - last_start) == pa: 94 | # Contiune blocks 95 | last_end = end 96 | last_continue = True 97 | else: 98 | # Not continue 99 | if last_continue: 100 | print(normal_line * (level) + branch_line, end='') 101 | print( 102 | f"0x{format(last_start, 'X')} ~ 0x{format(last_end, 'X')} => ", end='') 103 | print( 104 | f"0x{format(last_pa, 'X')} ~ 0x{format(last_pa + (last_end - last_start), 'X')}", end='') 105 | print(type_separator, end='') 106 | print_type(last_type) 107 | print() 108 | last_start = start 109 | last_end = end 110 | last_pa = pa 111 | last_type = type 112 | last_continue = True 113 | 114 | if last_continue: 115 | print(normal_line * (level) + branch_line, end='') 116 | print( 117 | f"0x{format(last_start, 'X')} ~ 0x{format(last_end, 'X')} => ", end='') 118 | print( 119 | f"0x{format(last_pa, 'X')} ~ 0x{format(last_pa + (last_end - last_start), 'X')}", end='') 120 | print(type_separator, end='') 121 | print_type(last_type) 122 | print() 123 | 124 | 125 | class Paging(gdb.Command): 126 | 127 | """RISC-V SV39 MMU Paging Debugging tool. 128 | Usage: 129 | paging : The shortcut of `paging satp` 130 | paging satp : Show page table from satp register. 131 | paging addr : Show page table at addr. 132 | Example: 133 | (gdb) paging 134 | (gdb) paging satp 135 | (gdb) paging 0x12340000""" 136 | 137 | def __init__(self): 138 | super(self.__class__, self).__init__("paging", gdb.COMMAND_USER) 139 | 140 | def invoke(self, args, from_tty): 141 | root_pgdir = None 142 | args = gdb.string_to_argv(args) 143 | if len(args) == 0 or args[0] == "satp": 144 | satp = gdb.selected_frame().read_register('satp') 145 | satp = int(satp.cast(u64)) 146 | satp = format(satp, '064b')[::-1] 147 | satp_ppn = int(satp[0:44][::-1], 2) 148 | satp_asid = int(satp[44:60][::-1], 2) 149 | satp_mode = int(satp[60:64][::-1], 2) 150 | root_pgdir = satp_ppn * PAGE_SIZE 151 | print(f"MMU Mode: {satp_mode}, ASID: {satp_asid}.") 152 | print( 153 | f"Page table root address: {'0x{:016X}'.format(root_pgdir)}") 154 | if satp_mode != 8: 155 | print("Only support SV39 paging mode with mode 8.") 156 | if satp_mode != 0: 157 | return 158 | else: 159 | print("Perhaps it's a SBI simulation MMU, continue...") 160 | root_pgdir = gdb.Value( 161 | int(root_pgdir)).cast(u64p) 162 | else: 163 | if 'x' in args[0] or 'X' in args[0]: 164 | root_pgdir = gdb.Value( 165 | int(args[0], 16)).cast(u64p) 166 | else: 167 | root_pgdir = gdb.Value( 168 | int(args[0], 10)).cast(u64p) 169 | parse_page_table(root_pgdir, 0, 0) 170 | 171 | 172 | class V2P(gdb.Command): 173 | 174 | """RISC-V SV39 MMU Paging Debugging tool. 175 | Usage: 176 | v2p add : Get Physical address of a virtual address from pagetable at satp. 177 | v2p pg_addr addr : Get Physical address of a virtual address from pagetable at pg_addr. 178 | Example: 179 | (gdb) v2p 0x12345678 180 | (gdb) v2p 0x81230000 0x12345678""" 181 | 182 | def __init__(self): 183 | super(self.__class__, self).__init__("v2p", gdb.COMMAND_USER) 184 | 185 | def invoke(self, args, from_tty): 186 | pgdir = None 187 | va = None 188 | args = gdb.string_to_argv(args) 189 | if len(args) == 1: 190 | satp = gdb.selected_frame().read_register('satp') 191 | satp = int(satp.cast(gdb.lookup_type("unsigned long long"))) 192 | satp = format(satp, '064b')[::-1] 193 | satp_ppn = int(satp[0:44][::-1], 2) 194 | satp_mode = int(satp[60:64][::-1], 2) 195 | root_pgdir = satp_ppn * PAGE_SIZE 196 | if satp_mode != 8: 197 | print("Only support SV39 paging mode with mode 8.") 198 | return 199 | pgdir = gdb.Value( 200 | int(root_pgdir)).cast(gdb.lookup_type("unsigned long long *")) 201 | va = args[0] 202 | else: 203 | if 'x' in args[0] or 'X' in args[0]: 204 | pgdir = gdb.Value( 205 | int(args[0], 16)).cast(gdb.lookup_type("unsigned long long *")) 206 | else: 207 | pgdir = gdb.Value( 208 | int(args[0], 10)).cast(gdb.lookup_type("unsigned long long *")) 209 | va = args[1] 210 | if 'x' in va or 'X' in va: 211 | va = int(va, 16) 212 | else: 213 | va = int(va, 10) 214 | va_b = format(va, '064b')[::-1] 215 | va_offset = int(va_b[0:12][::-1], 2) 216 | va_vpn = [] 217 | va_vpn.append(int(va_b[30:39][::-1], 2)) 218 | va_vpn.append(int(va_b[21:30][::-1], 2)) 219 | va_vpn.append(int(va_b[12:21][::-1], 2)) 220 | for i in range(0, 3): 221 | pte = int(pgdir[va_vpn[i]]) 222 | if pte & 0b1 == 0: 223 | print("Page not valid.") 224 | return 225 | if pte & 0b1110 != 0: 226 | pte = format(pte, '064b')[::-1] 227 | pte_ppn = int(pte[10:54][::-1], 2) 228 | type = int(pte[0:10][::-1], 2) 229 | print( 230 | f"0x{format(va, 'X')} -> 0x{format(pte_ppn * PAGE_SIZE + va_offset, 'X')}", end='') 231 | print(" | ", end='') 232 | print_type(type) 233 | print() 234 | return 235 | else: 236 | pte = format(pte, '064b')[::-1] 237 | pte_ppn = int(pte[10:54][::-1], 2) 238 | pgdir = gdb.Value( 239 | pte_ppn * PAGE_SIZE).cast(gdb.lookup_type("unsigned long long *")) 240 | 241 | 242 | Paging() 243 | V2P() 244 | -------------------------------------------------------------------------------- /paging_debug.scm: -------------------------------------------------------------------------------- 1 | ; Paging Debugging tool for RISCV, Guile Version 2 | 3 | (use-modules ((gdb) #:prefix gdb:)) 4 | 5 | (load "SV39.scm") 6 | (load "SV48.scm") 7 | (load "utils.scm") 8 | 9 | (use-modules (utils)) 10 | 11 | (define is-riscv64? (string=? (gdb:target-config) "riscv64-elf")) 12 | (define is-riscv32? (string=? (gdb:target-config) "riscv32-elf")) 13 | (if is-riscv64? 14 | (display "Target is riscv64\n") 15 | (if is-riscv32? 16 | (display "Traget is riscv32, which is not supported yet.\n") 17 | (display "Unknown target, target must be riscv32/64\n"))) 18 | 19 | 20 | (define use-utf8 #t) 21 | (define output-lines (if use-utf8 (list "│ " "├─ " " │ ") (list "| " "|-" " |"))) 22 | (define type-separator " ") 23 | 24 | ; PDE walk 25 | ; on-dent parameters: dir-address level. 26 | ; on-leaf parameters: va-start va-end pa-start pa-end type level 27 | (define (pde-walk pgdir level offset on-dent on-leaf) 28 | (if (< level mmu:pte-max-level) 29 | (let ((pgdir (gdb:value-cast (gdb:make-value pgdir) (gdb:type-pointer mmu:pte-type)))) 30 | (letrec ( 31 | (walk (lambda (id last_va_start last_va_end last_pa_start last_type last_continue) 32 | (let ((try-last 33 | (lambda () 34 | (if last_continue 35 | (on-leaf last_va_start last_va_end 36 | last_pa_start 37 | (+ last_pa_start 38 | (- last_va_end last_va_start)) 39 | last_type level) 40 | )))) 41 | (if (= id mmu:pte-per-page) 42 | (try-last) 43 | (let* ( 44 | (pte (mmu:pte-at pgdir id)) 45 | (pa (car pte)) 46 | (type (cdr pte)) 47 | (start (+ offset (* id (list-ref mmu:pte-level-size level)))) 48 | (end (+ start (list-ref mmu:pte-level-size level))) 49 | ) 50 | (if (mmu:is-pte-valid? pte) 51 | (if (mmu:is-pte-a-pde? pte) 52 | (begin 53 | (on-dent pa level) 54 | (pde-walk pa (+ level 1) start on-dent on-leaf) 55 | (walk (+ id 1) 0 0 0 0 #f)); Dir 56 | (if (and 57 | (= last_va_end start) 58 | (equal? last_type type) 59 | (= (+ last_pa_start (- last_va_end last_va_start))) 60 | ) 61 | (walk (+ id 1) last_va_start end last_pa_start last_type last_continue) 62 | (begin 63 | (try-last) 64 | (walk (+ id 1) start end pa type #t) 65 | )) ; Leaf 66 | ) ; Valid 67 | (begin 68 | (try-last) 69 | (walk (+ id 1) 0 0 0 0 #f)) ; Non-valid 70 | ))))))) 71 | (walk 0 0 0 0 0 #f))))) 72 | 73 | ; Command Paging inspector 74 | (gdb:register-command! ( 75 | gdb:make-command "paging" 76 | #: command-class gdb:COMMAND_USER 77 | #: doc "RISC-V MMU Paging Debugging tool. 78 | Usage: 79 | v2p add : Get Physical address of a virtual address from pagetable at satp. 80 | v2p pg_addr addr : Get Physical address of a virtual address from pagetable at pg_addr. 81 | Example: 82 | (gdb) v2p 0x12345678 83 | (gdb) v2p 0x81230000 0x12345678" 84 | #: invoke (lambda (self args from-tty) (mmu-debug-command "paging" args)) 85 | )) 86 | 87 | ; Command Paging inspector 88 | (gdb:register-command! ( 89 | gdb:make-command "v2p" 90 | #: command-class gdb:COMMAND_USER 91 | #: doc "RISC-V MMU Paging Debugging tool. 92 | Usage: 93 | paging : The shortcut of `paging satp` 94 | paging satp : Show page table from satp register. 95 | paging addr : Show page table at addr. 96 | Example: 97 | (gdb) paging 98 | (gdb) paging satp 99 | (gdb) paging 0x12340000" 100 | #: invoke (lambda (self args from-tty) (mmu-debug-command "v2p" args)) 101 | )) 102 | 103 | 104 | (define (extract-root-pgdir-from-satp satp) 105 | (if is-riscv64? 106 | (ash (logand satp #xFFFFFFFFFFF) 12) 107 | (ash (logand satp #x3FFFFF) 12)) 108 | ) 109 | 110 | (define (extract-root-pgdir-from-satp? subcommand args) 111 | (if (string=? subcommand "paging") (or (= (string-length (car args)) 0) (string=? (car args) "satp")) 112 | (if (string=? subcommand "v2p") (null? (cdr args)))) 113 | ) 114 | 115 | (define (extract-root-pgdir-from-args subcommand args) 116 | (string->number (make-guile-style-hex (car args))) 117 | ; (if (string=? subcommand "paging") (string->number (car args)) 118 | ; (if (string=? subcommand "v2p") (string->number (car args)))) 119 | ) 120 | 121 | (define (mmu-debug-command subcommand args) 122 | (let* ( 123 | (args (string-split args #\ )) 124 | (satp (gdb:value->integer (gdb:frame-read-register (gdb:selected-frame) "satp"))) 125 | (mode (if is-riscv64? 126 | (logand (ash satp -60) #xF ) ;RV64 127 | (logand (ash satp -31) 1))) ;RV32 128 | (mode-str (case mode ((0) "Bare") ((1) "SV32") ((8) "SV39") (else "Unknown"))) 129 | (root-pgdir (if (extract-root-pgdir-from-satp? subcommand args) 130 | (extract-root-pgdir-from-satp satp) 131 | (extract-root-pgdir-from-args subcommand args))) 132 | ) 133 | 134 | (case mode 135 | ((8) (use-modules ((SV39) #:prefix mmu:))) 136 | (else (gdb:throw-user-error (format #f "MMU Mode (~a) unsupported." mode-str))) 137 | ) 138 | (display "MMU Mode: ") 139 | (display mode-str) 140 | (newline) 141 | (format #t "Page Table @ 0x~:@(~x~)" root-pgdir) 142 | (newline) 143 | 144 | (if (string=? subcommand "paging") (do-paging root-pgdir) 145 | (if (string=? subcommand "v2p") (do-v2p root-pgdir (if (extract-root-pgdir-from-satp? "v2p" args) (car args) (cadr args))))) 146 | )) 147 | 148 | 149 | (define (do-paging root-pgdir) 150 | (let ( 151 | ; on-dent parameter: dir-address. 152 | (on-dent (lambda (dir-addr level) 153 | (for-loop 0 (- level 1) (lambda (i) (display (list-ref output-lines 0)))) 154 | (display (list-ref output-lines 1)) 155 | (format #t "Directory @ 0x~:@(~x~)\n" dir-addr))) 156 | ; on-leaf parameter: va-start va-end pa-start pa-end type level 157 | (on-leaf (lambda (va-start va-end pa-start pa-end type level) 158 | (for-loop 0 (- level 1) (lambda (i) (display (list-ref output-lines 0)))) 159 | (display (list-ref output-lines 1)) 160 | (format #t "0x~:@(~x~) ~~ 0x~:@(~x~) => 0x~:@(~x~) ~~ 0x~:@(~x~)" 161 | va-start va-end pa-start pa-end) 162 | (display (list-ref output-lines 2)) 163 | (mmu:print-type type type-separator) 164 | (newline) 165 | ))) 166 | (pde-walk root-pgdir 0 0 on-dent on-leaf))) 167 | 168 | (define (do-v2p root-pgdir va) 169 | (let* ((v2p-info (mmu:v2p root-pgdir va)) (pa (car v2p-info)) (type (cdr v2p-info))) 170 | (format #t "0x~:@(~x~) => 0x~:@(~x~)" (string->number (make-guile-style-hex va)) pa) 171 | (display (list-ref output-lines 2)) 172 | (print-type type type-separator) 173 | (newline) 174 | )) 175 | -------------------------------------------------------------------------------- /utils.scm: -------------------------------------------------------------------------------- 1 | ; Misc utils 2 | (define-module (utils) 3 | #:export ( 4 | bit-vector 5 | for-loop 6 | make-guile-style-hex 7 | )) 8 | 9 | ; for-loop 10 | (define* (for-loop start end func #:optional (step 1)) 11 | (letrec 12 | ((loop 13 | (lambda (i) 14 | (if (> i end) 15 | '() 16 | (cons (func i) (loop (+ i step))) 17 | ) 18 | ) 19 | )) 20 | (loop start) 21 | ) 22 | ) 23 | 24 | ; from start to end of num 25 | (define (bit-vector num start end) 26 | (for-loop start end (lambda (i) (logbit? i num))) 27 | ) 28 | 29 | ; Convert 0x123 to #x123 as guile style 30 | (define (make-guile-style-hex str) 31 | (if (string-prefix-ci? "0x" str) 32 | (string-append "#" (substring str 1)) 33 | 'str 34 | )) --------------------------------------------------------------------------------