A Total Overhaul on the Lunaix's Virtual Memory Model (#26)
[lunaix-os.git] / lunaix-os / scripts / gdb / lunadbg / structs / pagetable.py
1 from gdb import Type, Value, lookup_type
2 from . import KernelStruct
3 from ..arch import PageTableHelper as TLB
4
5 class PageTableEntry(KernelStruct):
6     def __init__(self, ptep, level, pte=None) -> None:
7         self.level = level
8         self.base_page_order = TLB.translation_shift_bits(-1)
9         self.ptep = ptep
10
11         super().__init__(Value(ptep), PageTableEntry)
12
13         if pte:
14             self.pte = pte
15             self.va  = None
16         else:
17             self.va = PageTable.va_at(ptep, level)
18             try:
19                 self.pte = int(self._kstruct['val'])
20             except:
21                 self.pte = 0
22
23         self.pa = TLB.physical_pfn(self.pte) << self.base_page_order
24
25         self.page_order = TLB.translation_shift_bits(self.level)
26         self.page_size = 1 << self.page_order
27         self.page_order -= self.base_page_order
28
29     @staticmethod
30     def from_pteval(pte_val, level):
31         return PageTableEntry(0, level, pte=pte_val)
32
33     def print_abstract(self, pp, *args):
34         self.print_detailed(pp, *args)
35
36     def print_simple(self, pp, *args):
37         self.print_detailed(pp, *args)
38
39     def print_detailed(self, pp, *args):
40         if self.null():
41             pp.print("<Mapping not exists>")
42             return
43         
44         pp.printf("Level %d Translation", TLB.translation_level(self.level))
45
46         pp2 = pp.next_level()
47         pp2.printf("PTE raw value: 0x%016x", self.pte)
48         
49         if not self.va:
50             pp2.printf("Virtual address: <n/a> (ptep=<n/a>)")
51         else:
52             pp2.printf("Virtual address: 0x%016x (ptep=0x%016x)", self.va, int(self._kstruct))
53
54         pp2.printf("Mapped physical: 0x%016x (order %d page)", self.pa, self.page_order)
55         pp2.printf("Page Protection: %s", self.get_page_prot())
56         pp2.printf("Present: %s", self.present())
57         pp2.printf("Huge: %s", TLB.huge_page(self.pte, self.page_order))
58         pp2.print("Attributes:")
59         pp2.next_level().print(self.get_attributes())
60     
61     def leaf(self):
62         return TLB.huge_page(self.pte, self.page_order) or not self.page_order
63
64     @staticmethod
65     def get_type() -> Type:
66         return lookup_type("pte_t").pointer()
67
68     def get_page_mask(self):
69         return PageTableEntry.get_level_shift(self.level) - 1
70     
71     def present(self):
72         return TLB.mapping_present(self.pte)
73     
74     def get_page_prot(self):
75         return ''.join(TLB.protections(self.pte))
76
77     def get_attributes(self):
78         attrs = [ self.get_page_prot(),
79                   *TLB.other_attributes(self.level, self.pte),
80                   "leaf" if self.leaf() else "root" ]
81         return ', '.join(attrs)
82     
83     def null(self):
84         return TLB.null_mapping(self.pte)
85     
86     def same_kind_to(self, pte2):
87         return TLB.same_kind(self.pte, pte2.pte)
88     
89     @staticmethod
90     def get_level_shift(level):
91         return 1 << TLB.translation_shift_bits(level)
92     
93     @staticmethod
94     def max_page_count():
95         return 1 << (TLB.vaddr_width() - TLB.translation_shift_bits(-1))
96     
97     def pfn(self):
98         return (self.ptep & (((1 << TLB.translation_shift_bits(0)) - 1))) // TLB.pte_size()
99     
100     def vfn(self):
101         return (self.ptep & (((1 << TLB.translation_shift_bits(-1)) - 1))) // TLB.pte_size()
102
103 class PageTable():
104     def __init__(self) -> None:
105         pass
106
107     @staticmethod
108     def get_pfn(ptep):
109         pfn_mask = ((1 << TLB.translation_shift_bits(0)) - 1)
110         return ((ptep & pfn_mask) // TLB.pte_size())
111     
112     @staticmethod
113     def get_vfn(ptep):
114         vfn_mask = ((1 << TLB.translation_shift_bits(-1)) - 1)
115         return ((ptep & vfn_mask) // TLB.pte_size())
116
117     @staticmethod
118     def mkptep_for(mnt, va):
119         mnt_mask = ~((1 << TLB.translation_shift_bits(0)) - 1)
120         offset   = (TLB.physical_pfn(va) * TLB.pte_size()) & ~mnt_mask
121
122         return (mnt & mnt_mask) | offset
123
124     @staticmethod
125     def ptep_infer_level(ptep):
126         l = 0
127         pfn = PageTable.get_pfn(ptep)
128         pfn = pfn << TLB.translation_shift_bits(-1)
129         vfn = (TLB.pgtable_len() - 1)
130         msk = vfn << TLB.translation_shift_bits(l)
131         max_l = TLB.translation_level()
132
133         mnt = ptep & msk
134
135         while (pfn & msk) == msk:
136             l+=1
137             msk = vfn << TLB.translation_shift_bits(l)
138             if l == max_l:
139                 break
140
141         return (max_l - l, mnt)
142
143     @staticmethod
144     def va_at(ptep, level):
145         vms_mask = ((1 << TLB.vaddr_width()) - 1)
146
147         ptep = PageTable.get_pfn(ptep) << TLB.translation_shift_bits(level)
148         return ptep & vms_mask
149     
150     @staticmethod
151     def get_l0tep(ptep):
152         return PageTable.get_lntep(ptep, 0)
153     
154     @staticmethod
155     def get_lntep(ptep, level):
156         lnmask = (1 << TLB.translation_shift_bits(level)) - 1
157         size = (1 << TLB.translation_shift_bits(-1))
158         vpfn = (lnmask * size) & lnmask
159         offset = ((ptep // TLB.pte_size()) * size // (lnmask + 1)) & (size - 1)
160
161         return (ptep & ~lnmask) | vpfn | (offset * TLB.pte_size())
162     
163     @staticmethod
164     def mkptep_at(mnt, va, level):
165         lfmask = (1 << TLB.translation_shift_bits(-1)) - 1
166         lsize = (1 << TLB.translation_shift_bits(level))
167         offset = (va // lsize) * TLB.pte_size()
168
169         return mnt | ((lsize - 1) & ~lfmask) | offset
170     
171     @staticmethod
172     def shift_ptep_nextlevel(ptep):
173         mnt_mask = ~((1 << TLB.translation_shift_bits(0)) - 1)
174         size     = (1 << TLB.translation_shift_bits(-1))
175         mnt      = ptep & mnt_mask
176         vpfn     = ((ptep // TLB.pte_size()) * size) & ~mnt_mask
177
178         return mnt | vpfn
179
180     @staticmethod
181     def shift_ptep_prevlevel(ptep):
182         mnt_mask = ~((1 << TLB.translation_shift_bits(0)) - 1)
183         self_mnt = (TLB.pgtable_len() - 1) * (~mnt_mask + 1)
184         unshifted = PageTable.get_pfn(ptep) << TLB.translation_shift_bits(-1)
185         unshifted = PageTable.mkptep_for(self_mnt, unshifted)
186         return PageTable.mkptep_for(ptep & mnt_mask, unshifted)
187     
188     def __print_pte_ranged(self, pp, pte_head, pte_tail):
189         start_va = pte_head.va
190
191         if pte_head == pte_tail:
192             end_va = pte_head.va + pte_head.page_size
193         else:
194             end_va = pte_tail.va
195
196         sz = end_va - start_va
197         if not pte_head.null():
198             pp.printf("0x%016x...0x%016x, 0x%016x [0x%08x] %s", 
199                     start_va, end_va - 1, pte_head.pa, sz,
200                     pte_head.get_attributes())
201         else:
202             pp.printfa("0x{:016x}...0x{:016x}, {:^18s} [0x{:08x}] <no mapping>", 
203                     start_va, end_va - 1, "n/a", sz)
204     
205     def __scan_pagetable(self, pp, start_ptep, end_ptep, max_level = -1):
206         ptep  = PageTable.get_l0tep(start_ptep)
207         level = 0
208         max_level = TLB.translation_level(max_level)
209         va_end = PageTable.va_at(end_ptep, -1) + 1
210
211         head_pte = None
212         prev_pte = None
213         pp.printfa("{:^18s}   {:^18s}  {:^18s}  {:^10s}  {:^20s}", 
214                    "va-start", "va-end", "physical", "size", "attributes")
215         while PageTable.va_at(ptep, level) <= va_end:
216             pte = PageTableEntry(ptep, level)
217             if head_pte == None:
218                 head_pte = pte
219                 prev_pte = pte
220
221             if pte.null():
222                 if not head_pte.null():
223                     self.__print_pte_ranged(pp, head_pte, prev_pte)
224                     head_pte = pte
225             elif not pte.leaf() and level < max_level:
226                 ptep = PageTable.shift_ptep_nextlevel(ptep)
227                 level+=1
228                 continue
229             else:
230                 if head_pte.null():
231                     self.__print_pte_ranged(pp, head_pte, prev_pte)
232                     head_pte = pte
233                 else:
234                     n = pte.pfn() - head_pte.pfn()
235                     pa = head_pte.pa + (n << pte.base_page_order)
236                     if pa != pte.pa or not pte.same_kind_to(head_pte):
237                         self.__print_pte_ranged(pp, head_pte, prev_pte)
238                         head_pte = pte
239
240             prev_pte = pte
241             if pte.vfn() == TLB.pgtable_len() - 1:
242                 if level != 0:
243                     ptep = PageTable.shift_ptep_prevlevel(ptep + TLB.pte_size())
244                     level-=1
245                     continue
246                 break
247             
248             ptep += TLB.pte_size()
249
250         
251         self.__print_pte_ranged(pp, head_pte, prev_pte)
252                     
253     def print_ptes_between(self, pp, va, va_end, level=-1, mnt=0xFFC00000):
254         ptep_start = PageTable.mkptep_for(mnt, va)
255         ptep_end = PageTable.mkptep_for(mnt, va_end)
256         self.__scan_pagetable(pp, ptep_start, ptep_end, level)
257
258     def get_pte(self, va, level=-1, mnt=0xFFC00000) -> PageTableEntry:
259         ptep = PageTable.mkptep_at(mnt, va, level)
260         return PageTableEntry(ptep, level)
261     
262     def print_ptes(self, pp, va, pte_num, level=-1, mnt=0xFFC00000):
263         ptep_start = PageTable.mkptep_for(mnt, va)
264         ptep_end = ptep_start + pte_num * TLB.pte_size()
265         self.__scan_pagetable(pp, ptep_start, ptep_end, level)