blob: d52d51c0a03fcbeffdeeb954af9437dcc3221aa5 [file] [log] [blame] [edit]
# SPDX-License-Identifier: GPL-2.0
#
# Maple tree helpers
#
# Copyright (c) 2025 Broadcom
#
# Authors:
# Florian Fainelli <[email protected]>
import gdb
from linux import utils
from linux import constants
from linux import xarray
maple_tree_root_type = utils.CachedType("struct maple_tree")
maple_node_type = utils.CachedType("struct maple_node")
maple_enode_type = utils.CachedType("void")
maple_dense = 0
maple_leaf_64 = 1
maple_range_64 = 2
maple_arange_64 = 3
class Mas(object):
ma_active = 0
ma_start = 1
ma_root = 2
ma_none = 3
ma_pause = 4
ma_overflow = 5
ma_underflow = 6
ma_error = 7
def __init__(self, mt, first, end):
if mt.type == maple_tree_root_type.get_type().pointer():
self.tree = mt.dereference()
elif mt.type != maple_tree_root_type.get_type():
raise gdb.GdbError("must be {} not {}"
.format(maple_tree_root_type.get_type().pointer(), mt.type))
self.tree = mt
self.index = first
self.last = end
self.node = None
self.status = self.ma_start
self.min = 0
self.max = -1
def is_start(self):
# mas_is_start()
return self.status == self.ma_start
def is_ptr(self):
# mas_is_ptr()
return self.status == self.ma_root
def is_none(self):
# mas_is_none()
return self.status == self.ma_none
def root(self):
# mas_root()
return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer())
def start(self):
# mas_start()
if self.is_start() is False:
return None
self.min = 0
self.max = ~0
while True:
self.depth = 0
root = self.root()
if xarray.xa_is_node(root):
self.depth = 0
self.status = self.ma_active
self.node = mte_safe_root(root)
self.offset = 0
if mte_dead_node(self.node) is True:
continue
return None
self.node = None
# Empty tree
if root is None:
self.status = self.ma_none
self.offset = constants.LX_MAPLE_NODE_SLOTS
return None
# Single entry tree
self.status = self.ma_root
self.offset = constants.LX_MAPLE_NODE_SLOTS
if self.index != 0:
return None
return root
return None
def reset(self):
# mas_reset()
self.status = self.ma_start
self.node = None
def mte_safe_root(node):
if node.type != maple_enode_type.get_type().pointer():
raise gdb.GdbError("{} must be {} not {}"
.format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type))
ulong_type = utils.get_ulong_type()
indirect_ptr = node.cast(ulong_type) & ~0x2
val = indirect_ptr.cast(maple_enode_type.get_type().pointer())
return val
def mte_node_type(entry):
ulong_type = utils.get_ulong_type()
val = None
if entry.type == maple_enode_type.get_type().pointer():
val = entry.cast(ulong_type)
elif entry.type == ulong_type:
val = entry
else:
raise gdb.GdbError("{} must be {} not {}"
.format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type))
return (val >> 0x3) & 0xf
def ma_dead_node(node):
if node.type != maple_node_type.get_type().pointer():
raise gdb.GdbError("{} must be {} not {}"
.format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type))
ulong_type = utils.get_ulong_type()
parent = node['parent']
indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK
return indirect_ptr == node
def mte_to_node(enode):
ulong_type = utils.get_ulong_type()
if enode.type == maple_enode_type.get_type().pointer():
indirect_ptr = enode.cast(ulong_type)
elif enode.type == ulong_type:
indirect_ptr = enode
else:
raise gdb.GdbError("{} must be {} not {}"
.format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK
return indirect_ptr.cast(maple_node_type.get_type().pointer())
def mte_dead_node(enode):
if enode.type != maple_enode_type.get_type().pointer():
raise gdb.GdbError("{} must be {} not {}"
.format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
node = mte_to_node(enode)
return ma_dead_node(node)
def ma_is_leaf(tp):
result = tp < maple_range_64
return tp < maple_range_64
def mt_pivots(t):
if t == maple_dense:
return 0
elif t == maple_leaf_64 or t == maple_range_64:
return constants.LX_MAPLE_RANGE64_SLOTS - 1
elif t == maple_arange_64:
return constants.LX_MAPLE_ARANGE64_SLOTS - 1
def ma_pivots(node, t):
if node.type != maple_node_type.get_type().pointer():
raise gdb.GdbError("{}: must be {} not {}"
.format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type))
if t == maple_arange_64:
return node['ma64']['pivot']
elif t == maple_leaf_64 or t == maple_range_64:
return node['mr64']['pivot']
else:
return None
def ma_slots(node, tp):
if node.type != maple_node_type.get_type().pointer():
raise gdb.GdbError("{}: must be {} not {}"
.format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type))
if tp == maple_arange_64:
return node['ma64']['slot']
elif tp == maple_range_64 or tp == maple_leaf_64:
return node['mr64']['slot']
elif tp == maple_dense:
return node['slot']
else:
return None
def mt_slot(mt, slots, offset):
ulong_type = utils.get_ulong_type()
return slots[offset].cast(ulong_type)
def mtree_lookup_walk(mas):
ulong_type = utils.get_ulong_type()
n = mas.node
while True:
node = mte_to_node(n)
tp = mte_node_type(n)
pivots = ma_pivots(node, tp)
end = mt_pivots(tp)
offset = 0
while True:
if pivots[offset] >= mas.index:
break
if offset >= end:
break
offset += 1
slots = ma_slots(node, tp)
n = mt_slot(mas.tree, slots, offset)
if ma_dead_node(node) is True:
mas.reset()
return None
break
if ma_is_leaf(tp) is True:
break
return n
def mtree_load(mt, index):
ulong_type = utils.get_ulong_type()
# MT_STATE(...)
mas = Mas(mt, index, index)
entry = None
while True:
entry = mas.start()
if mas.is_none():
return None
if mas.is_ptr():
if index != 0:
entry = None
return entry
entry = mtree_lookup_walk(mas)
if entry is None and mas.is_start():
continue
else:
break
if xarray.xa_is_zero(entry):
return None
return entry