add vm probing for x86_64
[lunaix-os.git] / lunaix-os / scripts / gdb / lunadbg / structs / pagetable.py
index 1ffc06211cc0381738461eaf8337d449a111bc7d..c58eedd916586b2c9303e3a1e687a9dcd7769d59 100644 (file)
@@ -3,22 +3,33 @@ from . import KernelStruct
 from ..arch import PageTableHelper as TLB
 
 class PageTableEntry(KernelStruct):
-    def __init__(self, gdb_inferior: Value, level, va) -> None:
+    def __init__(self, ptep, level, pte=None) -> None:
         self.level = level
-        self.pg_mask = self.get_page_mask()
-        self.va = va & ~self.pg_mask
         self.base_page_order = TLB.translation_shift_bits(-1)
+        self.ptep = ptep
 
-        ptep = gdb_inferior[va // (self.pg_mask + 1)].address
-        super().__init__(ptep, PageTableEntry)
-        
-        try:
-            self.pte = int(self._kstruct.dereference())
-        except:
-            self.pte = 0
+        super().__init__(Value(ptep), PageTableEntry)
+
+        if pte:
+            self.pte = pte
+            self.va  = None
+        else:
+            self.va = PageTable.va_at(ptep, level)
+            try:
+                self.pte = int(self._kstruct['val'])
+            except:
+                self.pte = 0
 
         self.pa = TLB.physical_pfn(self.pte) << self.base_page_order
 
+        self.page_order = TLB.translation_shift_bits(self.level)
+        self.page_size = 1 << self.page_order
+        self.page_order -= self.base_page_order
+
+    @staticmethod
+    def from_pteval(pte_val, level):
+        return PageTableEntry(0, level, pte=pte_val)
+
     def print_abstract(self, pp, *args):
         self.print_detailed(pp, *args)
 
@@ -30,24 +41,29 @@ class PageTableEntry(KernelStruct):
             pp.print("<Mapping not exists>")
             return
         
-        page_order = TLB.translation_shift_bits(self.level)
-        page_order -= self.base_page_order
-        
         pp.printf("Level %d Translation", TLB.translation_level(self.level))
 
         pp2 = pp.next_level()
-        pp2.printf("Entry value: 0x%x", self.pte)
-        pp2.printf("Virtual address: 0x%x (ptep=0x%x)", self.va, int(self._kstruct))
-        pp2.printf("Mapped physical: 0x%x (order %d page)", self.pa, page_order)
+        pp2.printf("PTE raw value: 0x%016x", self.pte)
+        
+        if not self.va:
+            pp2.printf("Virtual address: <n/a> (ptep=<n/a>)")
+        else:
+            pp2.printf("Virtual address: 0x%016x (ptep=0x%016x)", self.va, int(self._kstruct))
+
+        pp2.printf("Mapped physical: 0x%016x (order %d page)", self.pa, self.page_order)
         pp2.printf("Page Protection: %s", self.get_page_prot())
         pp2.printf("Present: %s", self.present())
-        pp2.printf("Huge: %s", TLB.huge_page(self.pte))
+        pp2.printf("Huge: %s", TLB.huge_page(self.pte, self.page_order))
         pp2.print("Attributes:")
         pp2.next_level().print(self.get_attributes())
+    
+    def leaf(self):
+        return TLB.huge_page(self.pte, self.page_order) or not self.page_order
 
     @staticmethod
     def get_type() -> Type:
-        return lookup_type("unsigned int").pointer()
+        return lookup_type("pte_t").pointer()
 
     def get_page_mask(self):
         return PageTableEntry.get_level_shift(self.level) - 1
@@ -60,7 +76,8 @@ class PageTableEntry(KernelStruct):
 
     def get_attributes(self):
         attrs = [ self.get_page_prot(),
-                  *TLB.other_attributes(self.level, self.pte) ]
+                  *TLB.other_attributes(self.level, self.pte),
+                  "leaf" if self.leaf() else "root" ]
         return ', '.join(attrs)
     
     def null(self):
@@ -76,22 +93,108 @@ class PageTableEntry(KernelStruct):
     @staticmethod
     def max_page_count():
         return 1 << (TLB.vaddr_width() - TLB.translation_shift_bits(-1))
+    
+    def pfn(self):
+        return (self.ptep & (((1 << TLB.translation_shift_bits(0)) - 1))) // TLB.pte_size()
+    
+    def vfn(self):
+        return (self.ptep & (((1 << TLB.translation_shift_bits(-1)) - 1))) // TLB.pte_size()
 
 class PageTable():
     def __init__(self) -> None:
-        self.levels = [
-            Value(0xFFFFF000).cast(PageTableEntry.get_type()),
-            Value(0xFFC00000).cast(PageTableEntry.get_type())
-        ]
+        pass
 
-    def get_pte(self, va, level=-1) -> PageTableEntry:
-        return PageTableEntry(self.levels[level], level, va)
+    @staticmethod
+    def get_pfn(ptep):
+        pfn_mask = ((1 << TLB.translation_shift_bits(0)) - 1)
+        return ((ptep & pfn_mask) // TLB.pte_size())
+    
+    @staticmethod
+    def get_vfn(ptep):
+        vfn_mask = ((1 << TLB.translation_shift_bits(-1)) - 1)
+        return ((ptep & vfn_mask) // TLB.pte_size())
+
+    @staticmethod
+    def mkptep_for(mnt, va):
+        mnt_mask = ~((1 << TLB.translation_shift_bits(0)) - 1)
+        offset   = (TLB.physical_pfn(va) * TLB.pte_size()) & ~mnt_mask
+
+        return (mnt & mnt_mask) | offset
+
+    @staticmethod
+    def ptep_infer_level(ptep):
+        l = 0
+        pfn = PageTable.get_pfn(ptep)
+        pfn = pfn << TLB.translation_shift_bits(-1)
+        vfn = (TLB.pgtable_len() - 1)
+        msk = vfn << TLB.translation_shift_bits(l)
+        max_l = TLB.translation_level()
+
+        mnt = ptep & msk
+
+        while (pfn & msk) == msk:
+            l+=1
+            msk = vfn << TLB.translation_shift_bits(l)
+            if l == max_l:
+                break
+
+        return (max_l - l, mnt)
+
+    @staticmethod
+    def va_at(ptep, level):
+        vms_mask = ((1 << TLB.vaddr_width()) - 1)
+
+        ptep = PageTable.get_pfn(ptep) << TLB.translation_shift_bits(level)
+        return ptep & vms_mask
+    
+    @staticmethod
+    def get_l0tep(ptep):
+        return PageTable.get_lntep(ptep, 0)
+    
+    @staticmethod
+    def get_lntep(ptep, level):
+        lnmask = (1 << TLB.translation_shift_bits(level)) - 1
+        size = (1 << TLB.translation_shift_bits(-1))
+        vpfn = (lnmask * size) & lnmask
+        offset = ((ptep // TLB.pte_size()) * size // (lnmask + 1)) & (size - 1)
+
+        return (ptep & ~lnmask) | vpfn | (offset * TLB.pte_size())
+    
+    @staticmethod
+    def mkptep_at(mnt, va, level):
+        lfmask = (1 << TLB.translation_shift_bits(-1)) - 1
+        lsize = (1 << TLB.translation_shift_bits(level))
+        offset = (va // lsize) * TLB.pte_size()
+
+        return mnt | ((lsize - 1) & ~lfmask) | offset
+    
+    @staticmethod
+    def shift_ptep_nextlevel(ptep):
+        mnt_mask = ~((1 << TLB.translation_shift_bits(0)) - 1)
+        size     = (1 << TLB.translation_shift_bits(-1))
+        mnt      = ptep & mnt_mask
+        vpfn     = ((ptep // TLB.pte_size()) * size) & ~mnt_mask
+
+        return mnt | vpfn
+
+    @staticmethod
+    def shift_ptep_prevlevel(ptep):
+        mnt_mask = ~((1 << TLB.translation_shift_bits(0)) - 1)
+        self_mnt = (TLB.pgtable_len() - 1) * (~mnt_mask + 1)
+        unshifted = PageTable.get_pfn(ptep) << TLB.translation_shift_bits(-1)
+        unshifted = PageTable.mkptep_for(self_mnt, unshifted)
+        return PageTable.mkptep_for(ptep & mnt_mask, unshifted)
     
     def __print_pte_ranged(self, pp, pte_head, pte_tail):
         start_va = pte_head.va
-        end_va = pte_tail.va
+
+        if pte_head == pte_tail:
+            end_va = pte_head.va + pte_head.page_size
+        else:
+            end_va = pte_tail.va
+
         sz = end_va - start_va
-        if not (pte_head.null() and pte_tail.null()):
+        if not pte_head.null():
             pp.printf("0x%016x...0x%016x, 0x%016x [0x%08x] %s", 
                     start_va, end_va - 1, pte_head.pa, sz,
                     pte_head.get_attributes())
@@ -99,25 +202,65 @@ class PageTable():
             pp.printfa("0x{:016x}...0x{:016x}, {:^18s} [0x{:08x}] <no mapping>", 
                     start_va, end_va - 1, "n/a", sz)
     
-    def print_ptes_between(self, pp, va, va_end, level=-1):
-        shift = PageTableEntry.get_level_shift(level)
-        n = (va_end - va) // shift
-        self.print_ptes(pp, va, n, level)
-
-    def print_ptes(self, pp, va, pte_num, level=-1):
-        head_pte = PageTableEntry(self.levels[level], level, va)
-        curr_pte = head_pte
-        va = head_pte.va
+    def __scan_pagetable(self, pp, start_ptep, end_ptep, max_level = -1):
+        ptep  = PageTable.get_l0tep(start_ptep)
+        level = 0
+        max_level = TLB.translation_level(max_level)
+        va_end = PageTable.va_at(end_ptep, -1) + 1
 
+        head_pte = None
+        prev_pte = None
         pp.printfa("{:^18s}   {:^18s}  {:^18s}  {:^10s}  {:^20s}", 
                    "va-start", "va-end", "physical", "size", "attributes")
-        for i in range(1, pte_num):
-            va_ = va + i * PageTableEntry.get_level_shift(level)
-            curr_pte = PageTableEntry(self.levels[level], level, va_)
+        while PageTable.va_at(ptep, level) <= va_end:
+            pte = PageTableEntry(ptep, level)
+            if head_pte == None:
+                head_pte = pte
+                prev_pte = pte
+
+            if pte.null():
+                if not head_pte.null():
+                    self.__print_pte_ranged(pp, head_pte, prev_pte)
+                    head_pte = pte
+            elif not pte.leaf() and level < max_level:
+                ptep = PageTable.shift_ptep_nextlevel(ptep)
+                level+=1
+                continue
+            else:
+                if head_pte.null():
+                    self.__print_pte_ranged(pp, head_pte, prev_pte)
+                    head_pte = pte
+                else:
+                    n = pte.pfn() - head_pte.pfn()
+                    pa = head_pte.pa + (n << pte.base_page_order)
+                    if pa != pte.pa or not pte.same_kind_to(head_pte):
+                        self.__print_pte_ranged(pp, head_pte, prev_pte)
+                        head_pte = pte
+
+            prev_pte = pte
+            if pte.vfn() == TLB.pgtable_len() - 1:
+                if level != 0:
+                    ptep = PageTable.shift_ptep_prevlevel(ptep + TLB.pte_size())
+                    level-=1
+                    continue
+                break
             
-            if not curr_pte.same_kind_to(head_pte):                
-                self.__print_pte_ranged(pp, head_pte, curr_pte)
-                head_pte = curr_pte
+            ptep += TLB.pte_size()
+
         
-        if curr_pte != head_pte:
-            self.__print_pte_ranged(pp, head_pte, curr_pte)
\ No newline at end of file
+        if head_pte:
+            self.__print_pte_ranged(pp, head_pte, prev_pte)
+                    
+    def print_ptes_between(self, pp, va, va_end, level=-1, mnt=0xFFC00000):
+        ptep_start = PageTable.mkptep_for(mnt, va)
+        ptep_end = PageTable.mkptep_for(mnt, va_end)
+        self.__scan_pagetable(pp, ptep_start, ptep_end, level)
+
+    def get_pte(self, va, level=-1, mnt=0xFFC00000) -> PageTableEntry:
+        ptep = PageTable.mkptep_at(mnt, va, level)
+        return PageTableEntry(ptep, level)
+    
+    def print_ptes(self, pp, va, pte_num, level=-1, mnt=0xFFC00000):
+        ptep_start = PageTable.mkptep_for(mnt, va)
+        ptep_end = ptep_start + pte_num * TLB.pte_size()
+        self.__scan_pagetable(pp, ptep_start, ptep_end, level)
\ No newline at end of file