| 108 | } |
| 109 | |
| 110 | void walk_maple_node(const PhysicalLayer& phys, const KernelContext& kctx, |
| 111 | const VmaOffsets& voff, u64 mte, u64 expected_depth, |
| 112 | u64 depth, std::unordered_set<u64>& seen, |
| 113 | std::vector<Vma>& out) |
| 114 | { |
| 115 | if (depth > 16) return; // hard recursion guard |
| 116 | if (mte == 0) return; |
| 117 | if (seen.count(mte)) return; |
| 118 | seen.insert(mte); |
| 119 | |
| 120 | u64 node_pa_va = mte & ~kMapleNodePointerMask; // pointer to maple_node (kernel VA, direct-map) |
| 121 | u8 node_type = static_cast<u8>((mte >> kMapleNodeTypeShift) & kMapleNodeTypeMask); |
| 122 | |
| 123 | auto walk_slot_array = [&](u64 slot_off, u64 slot_count, bool recurse) { |
| 124 | for (u64 i = 0; i < slot_count; ++i) { |
| 125 | if (out.size() >= kMaxVmasPerProcess) return; |
| 126 | u64 slot = 0; |
| 127 | if (!read_dm(phys, kctx, node_pa_va + slot_off + i * 8, slot)) continue; |
| 128 | if (slot == 0) continue; |
| 129 | if (recurse) { |
| 130 | walk_maple_node(phys, kctx, voff, slot, expected_depth, depth + 1, seen, out); |
| 131 | } else { |
| 132 | // LEAF: slot is a vm_area_struct pointer (no tag bits). |
| 133 | read_vma(phys, kctx, slot, voff, out); |
| 134 | } |
| 135 | } |
| 136 | }; |
| 137 | |
| 138 | switch (node_type) { |
| 139 | case MAPLE_LEAF_64: walk_slot_array(kMR64_SlotOff, kMR64_SlotCount, false); break; |
| 140 | case MAPLE_RANGE_64: walk_slot_array(kMR64_SlotOff, kMR64_SlotCount, true); break; |
| 141 | case MAPLE_ARANGE_64: walk_slot_array(kMA64_SlotOff, kMA64_SlotCount, true); break; |
| 142 | case MAPLE_DENSE: /* allocator pool, never expected for mm_mt */ break; |
| 143 | default: |
| 144 | log::debug("maple: unknown node type {} at {:#x}", node_type, node_pa_va); |
| 145 | break; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | } // anonymous |
| 150 |
no test coverage detected