| @@ -0,0 +1,252 @@ |
| | 1 | # SPDX-License-Identifier: GPL-2.0 |
| | 2 | # |
| | 3 | # Maple tree helpers |
| | 4 | # |
| | 5 | # Copyright (c) 2025 Broadcom |
| | 6 | # |
| | 7 | # Authors: |
| | 8 | # Florian Fainelli <florian.fainelli@broadcom.com> |
| | 9 | |
| | 10 | import gdb |
| | 11 | |
| | 12 | from linux import utils |
| | 13 | from linux import constants |
| | 14 | from linux import xarray |
| | 15 | |
| | 16 | maple_tree_root_type = utils.CachedType("struct maple_tree") |
| | 17 | maple_node_type = utils.CachedType("struct maple_node") |
| | 18 | maple_enode_type = utils.CachedType("void") |
| | 19 | |
| | 20 | maple_dense = 0 |
| | 21 | maple_leaf_64 = 1 |
| | 22 | maple_range_64 = 2 |
| | 23 | maple_arange_64 = 3 |
| | 24 | |
| | 25 | class Mas(object): |
| | 26 | ma_active = 0 |
| | 27 | ma_start = 1 |
| | 28 | ma_root = 2 |
| | 29 | ma_none = 3 |
| | 30 | ma_pause = 4 |
| | 31 | ma_overflow = 5 |
| | 32 | ma_underflow = 6 |
| | 33 | ma_error = 7 |
| | 34 | |
| | 35 | def __init__(self, mt, first, end): |
| | 36 | if mt.type == maple_tree_root_type.get_type().pointer(): |
| | 37 | self.tree = mt.dereference() |
| | 38 | elif mt.type != maple_tree_root_type.get_type(): |
| | 39 | raise gdb.GdbError("must be {} not {}" |
| | 40 | .format(maple_tree_root_type.get_type().pointer(), mt.type)) |
| | 41 | self.tree = mt |
| | 42 | self.index = first |
| | 43 | self.last = end |
| | 44 | self.node = None |
| | 45 | self.status = self.ma_start |
| | 46 | self.min = 0 |
| | 47 | self.max = -1 |
| | 48 | |
| | 49 | def is_start(self): |
| | 50 | # mas_is_start() |
| | 51 | return self.status == self.ma_start |
| | 52 | |
| | 53 | def is_ptr(self): |
| | 54 | # mas_is_ptr() |
| | 55 | return self.status == self.ma_root |
| | 56 | |
| | 57 | def is_none(self): |
| | 58 | # mas_is_none() |
| | 59 | return self.status == self.ma_none |
| | 60 | |
| | 61 | def root(self): |
| | 62 | # mas_root() |
| | 63 | return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer()) |
| | 64 | |
| | 65 | def start(self): |
| | 66 | # mas_start() |
| | 67 | if self.is_start() is False: |
| | 68 | return None |
| | 69 | |
| | 70 | self.min = 0 |
| | 71 | self.max = ~0 |
| | 72 | |
| | 73 | while True: |
| | 74 | self.depth = 0 |
| | 75 | root = self.root() |
| | 76 | if xarray.xa_is_node(root): |
| | 77 | self.depth = 0 |
| | 78 | self.status = self.ma_active |
| | 79 | self.node = mte_safe_root(root) |
| | 80 | self.offset = 0 |
| | 81 | if mte_dead_node(self.node) is True: |
| | 82 | continue |
| | 83 | |
| | 84 | return None |
| | 85 | |
| | 86 | self.node = None |
| | 87 | # Empty tree |
| | 88 | if root is None: |
| | 89 | self.status = self.ma_none |
| | 90 | self.offset = constants.LX_MAPLE_NODE_SLOTS |
| | 91 | return None |
| | 92 | |
| | 93 | # Single entry tree |
| | 94 | self.status = self.ma_root |
| | 95 | self.offset = constants.LX_MAPLE_NODE_SLOTS |
| | 96 | |
| | 97 | if self.index != 0: |
| | 98 | return None |
| | 99 | |
| | 100 | return root |
| | 101 | |
| | 102 | return None |
| | 103 | |
| | 104 | def reset(self): |
| | 105 | # mas_reset() |
| | 106 | self.status = self.ma_start |
| | 107 | self.node = None |
| | 108 | |
| | 109 | def mte_safe_root(node): |
| | 110 | if node.type != maple_enode_type.get_type().pointer(): |
| | 111 | raise gdb.GdbError("{} must be {} not {}" |
| | 112 | .format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type)) |
| | 113 | ulong_type = utils.get_ulong_type() |
| | 114 | indirect_ptr = node.cast(ulong_type) & ~0x2 |
| | 115 | val = indirect_ptr.cast(maple_enode_type.get_type().pointer()) |
| | 116 | return val |
| | 117 | |
| | 118 | def mte_node_type(entry): |
| | 119 | ulong_type = utils.get_ulong_type() |
| | 120 | val = None |
| | 121 | if entry.type == maple_enode_type.get_type().pointer(): |
| | 122 | val = entry.cast(ulong_type) |
| | 123 | elif entry.type == ulong_type: |
| | 124 | val = entry |
| | 125 | else: |
| | 126 | raise gdb.GdbError("{} must be {} not {}" |
| | 127 | .format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type)) |
| | 128 | return (val >> 0x3) & 0xf |
| | 129 | |
| | 130 | def ma_dead_node(node): |
| | 131 | if node.type != maple_node_type.get_type().pointer(): |
| | 132 | raise gdb.GdbError("{} must be {} not {}" |
| | 133 | .format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type)) |
| | 134 | ulong_type = utils.get_ulong_type() |
| | 135 | parent = node['parent'] |
| | 136 | indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK |
| | 137 | return indirect_ptr == node |
| | 138 | |
| | 139 | def mte_to_node(enode): |
| | 140 | ulong_type = utils.get_ulong_type() |
| | 141 | if enode.type == maple_enode_type.get_type().pointer(): |
| | 142 | indirect_ptr = enode.cast(ulong_type) |
| | 143 | elif enode.type == ulong_type: |
| | 144 | indirect_ptr = enode |
| | 145 | else: |
| | 146 | raise gdb.GdbError("{} must be {} not {}" |
| | 147 | .format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) |
| | 148 | indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK |
| | 149 | return indirect_ptr.cast(maple_node_type.get_type().pointer()) |
| | 150 | |
| | 151 | def mte_dead_node(enode): |
| | 152 | if enode.type != maple_enode_type.get_type().pointer(): |
| | 153 | raise gdb.GdbError("{} must be {} not {}" |
| | 154 | .format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) |
| | 155 | node = mte_to_node(enode) |
| | 156 | return ma_dead_node(node) |
| | 157 | |
| | 158 | def ma_is_leaf(tp): |
| | 159 | result = tp < maple_range_64 |
| | 160 | return tp < maple_range_64 |
| | 161 | |
| | 162 | def mt_pivots(t): |
| | 163 | if t == maple_dense: |
| | 164 | return 0 |
| | 165 | elif t == maple_leaf_64 or t == maple_range_64: |
| | 166 | return constants.LX_MAPLE_RANGE64_SLOTS - 1 |
| | 167 | elif t == maple_arange_64: |
| | 168 | return constants.LX_MAPLE_ARANGE64_SLOTS - 1 |
| | 169 | |
| | 170 | def ma_pivots(node, t): |
| | 171 | if node.type != maple_node_type.get_type().pointer(): |
| | 172 | raise gdb.GdbError("{}: must be {} not {}" |
| | 173 | .format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type)) |
| | 174 | if t == maple_arange_64: |
| | 175 | return node['ma64']['pivot'] |
| | 176 | elif t == maple_leaf_64 or t == maple_range_64: |
| | 177 | return node['mr64']['pivot'] |
| | 178 | else: |
| | 179 | return None |
| | 180 | |
| | 181 | def ma_slots(node, tp): |
| | 182 | if node.type != maple_node_type.get_type().pointer(): |
| | 183 | raise gdb.GdbError("{}: must be {} not {}" |
| | 184 | .format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type)) |
| | 185 | if tp == maple_arange_64: |
| | 186 | return node['ma64']['slot'] |
| | 187 | elif tp == maple_range_64 or tp == maple_leaf_64: |
| | 188 | return node['mr64']['slot'] |
| | 189 | elif tp == maple_dense: |
| | 190 | return node['slot'] |
| | 191 | else: |
| | 192 | return None |
| | 193 | |
| | 194 | def mt_slot(mt, slots, offset): |
| | 195 | ulong_type = utils.get_ulong_type() |
| | 196 | return slots[offset].cast(ulong_type) |
| | 197 | |
| | 198 | def mtree_lookup_walk(mas): |
| | 199 | ulong_type = utils.get_ulong_type() |
| | 200 | n = mas.node |
| | 201 | |
| | 202 | while True: |
| | 203 | node = mte_to_node(n) |
| | 204 | tp = mte_node_type(n) |
| | 205 | pivots = ma_pivots(node, tp) |
| | 206 | end = mt_pivots(tp) |
| | 207 | offset = 0 |
| | 208 | while True: |
| | 209 | if pivots[offset] >= mas.index: |
| | 210 | break |
| | 211 | if offset >= end: |
| | 212 | break |
| | 213 | offset += 1 |
| | 214 | |
| | 215 | slots = ma_slots(node, tp) |
| | 216 | n = mt_slot(mas.tree, slots, offset) |
| | 217 | if ma_dead_node(node) is True: |
| | 218 | mas.reset() |
| | 219 | return None |
| | 220 | break |
| | 221 | |
| | 222 | if ma_is_leaf(tp) is True: |
| | 223 | break |
| | 224 | |
| | 225 | return n |
| | 226 | |
| | 227 | def mtree_load(mt, index): |
| | 228 | ulong_type = utils.get_ulong_type() |
| | 229 | # MT_STATE(...) |
| | 230 | mas = Mas(mt, index, index) |
| | 231 | entry = None |
| | 232 | |
| | 233 | while True: |
| | 234 | entry = mas.start() |
| | 235 | if mas.is_none(): |
| | 236 | return None |
| | 237 | |
| | 238 | if mas.is_ptr(): |
| | 239 | if index != 0: |
| | 240 | entry = None |
| | 241 | return entry |
| | 242 | |
| | 243 | entry = mtree_lookup_walk(mas) |
| | 244 | if entry is None and mas.is_start(): |
| | 245 | continue |
| | 246 | else: |
| | 247 | break |
| | 248 | |
| | 249 | if xarray.xa_is_zero(entry): |
| | 250 | return None |
| | 251 | |
| | 252 | return entry |