| 113 | class VirtMapping: va_addr:int; size:int; paddrs:list[tuple[int, int]]; aspace:AddrSpace; uncached:bool=False; snooped:bool=False # noqa: E702 |
| 114 | |
| 115 | class PageTableTraverseContext: |
| 116 | def __init__(self, dev, pt, vaddr, create_pts=False, free_pts=False, inspect=False, boot=False): |
| 117 | self.dev, self.vaddr, self.create_pts, self.free_pts, self.inspect, self.boot = dev, vaddr - dev.mm.va_base, create_pts, free_pts, inspect, boot |
| 118 | self.pt_stack:list[tuple[Any, int, int]] = [(pt, self._pt_pte_idx(pt, self.vaddr), self._pt_pte_size(pt))] |
| 119 | |
| 120 | def _pt_pte_cnt(self, lv): return self.dev.mm.pte_cnt[lv] |
| 121 | def _pt_pte_size(self, pt): return self.dev.mm.pte_covers[pt.lv] |
| 122 | def _pt_pte_idx(self, pt, va): return (va // self._pt_pte_size(pt)) % self._pt_pte_cnt(pt.lv) |
| 123 | |
| 124 | def level_down(self): |
| 125 | pt, pte_idx, _ = self.pt_stack[-1] |
| 126 | |
| 127 | if not pt.valid(pte_idx): |
| 128 | assert self.create_pts, "Not allowed to create new page table" |
| 129 | pt.set_entry(pte_idx, self.dev.mm.palloc(0x1000, zero=True, boot=self.boot, ptable=True), table=True, valid=True) |
| 130 | |
| 131 | assert not pt.is_page(pte_idx), f"Must be table pt={pt.paddr:#x}, {pt.lv=} {pte_idx=} {pt.entry(pte_idx)=:#x}" |
| 132 | child_page_table = self.dev.mm.pt_t(self.dev, pt.address(pte_idx), lv=pt.lv+1) |
| 133 | |
| 134 | self.pt_stack.append((child_page_table, self._pt_pte_idx(child_page_table, self.vaddr), self._pt_pte_size(child_page_table))) |
| 135 | return self.pt_stack[-1] |
| 136 | |
| 137 | def _try_free_pt(self) -> bool: |
| 138 | pt, _, _ = self.pt_stack[-1] |
| 139 | if self.free_pts and pt != self.dev.mm.root_page_table and all(not pt.valid(i) for i in range(self._pt_pte_cnt(self.pt_stack[-1][0].lv))): |
| 140 | self.dev.mm.pfree(pt.paddr, ptable=True) |
| 141 | parent_pt, parent_pte_idx, _ = self.pt_stack[-2] |
| 142 | parent_pt.set_entry(parent_pte_idx, 0x0, valid=False) |
| 143 | return True |
| 144 | return False |
| 145 | |
| 146 | def level_up(self): |
| 147 | while self._try_free_pt() or self.pt_stack[-1][1] == self._pt_pte_cnt(self.pt_stack[-1][0].lv): |
| 148 | pt, pt_cnt, _ = self.pt_stack.pop() |
| 149 | if pt_cnt == self._pt_pte_cnt(pt.lv): self.pt_stack[-1] = (self.pt_stack[-1][0], self.pt_stack[-1][1] + 1, self.pt_stack[-1][2]) |
| 150 | |
| 151 | def next(self, size:int, paddr:int|None=None, off:int=0): |
| 152 | while size > 0: |
| 153 | pt, pte_idx, pte_covers = self.pt_stack[-1] |
| 154 | |
| 155 | # create_pts goes down until the page covers the request. |
| 156 | # free_pts goes down to the table, it assumses all entries are valid on the range (and validates that) |
| 157 | # inspect just visits any valid ranges and yields them. |
| 158 | if self.create_pts: |
| 159 | assert paddr is not None, "paddr must be provided when allocating new page tables" |
| 160 | while pte_covers > size or not pt.supports_huge_page(paddr+off) or self.vaddr&(pte_covers-1) != 0: pt, pte_idx, pte_covers = self.level_down() |
| 161 | else: |
| 162 | while not pt.is_page(pte_idx) and (self.free_pts or pt.valid(pte_idx)): pt, pte_idx, pte_covers = self.level_down() |
| 163 | |
| 164 | entries = max(min(size // pte_covers, self._pt_pte_cnt(pt.lv) - pte_idx), 1 if self.inspect else 0) |
| 165 | assert entries > 0, f"Invalid entries {size=:#x}, {pte_covers=:#x}" |
| 166 | yield off, pt, pte_idx, entries, pte_covers |
| 167 | |
| 168 | size, off, self.vaddr = size - entries * pte_covers, off + entries * pte_covers, self.vaddr + entries * pte_covers |
| 169 | self.pt_stack[-1] = (pt, pte_idx + entries, pte_covers) |
| 170 | self.level_up() |
| 171 | |
| 172 | class MemoryManager: |
no outgoing calls
searching dependent graphs…