Files
seL4/tools/hardware/device.py
Ahmed Charles c92b3b3163 Add __repr__ for WrappedNode
Signed-off-by: Ahmed Charles <acharles@outlook.com>
2022-07-20 16:37:33 +10:00

233 lines
8.5 KiB
Python

#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: GPL-2.0-only
#
from collections import OrderedDict
from typing import Any, Dict, Generator, List, Tuple, cast
import logging
import pyfdt.pyfdt
from hardware.memory import Region
class WrappedNode:
''' A wrapper around an underlying pyfdt node '''
# TODO: Python 3.7 with 'from __future__ import annotations' will remove the need
# to put 'WrappedNode' in a string.
def __init__(self, node: pyfdt.pyfdt.FdtNode, parent: 'WrappedNode', path: str):
self.node = node
self.parent = parent
self.depth = 0
self.path = path
self.children: Dict[str, 'WrappedNode'] = OrderedDict()
self.props: Dict[str, pyfdt.pyfdt.FdtProperty] = {}
for prop in node:
if not isinstance(prop, pyfdt.pyfdt.FdtProperty):
continue
self.props[prop.get_name()] = prop
if parent is not None:
parent.add_child(self)
self.depth = parent.depth + 1
self.is_cpu_addressable: bool = parent.is_cpu_addressable and \
'ranges' in self.props
else:
self.is_cpu_addressable = True # root node is always cpu addressable
def add_child(self, child: 'WrappedNode'):
''' Add a child to this node '''
self.children[child.node.get_name()] = child
def get_prop(self, name: str) -> pyfdt.pyfdt.FdtProperty:
''' Returns prop with given name, or throws KeyError if not found '''
return self.props[name]
def has_prop(self, name: str) -> bool:
''' Returns True if prop with given name exists, otherwise False. '''
return name in self.props
def get_phandle(self) -> int:
''' Return this node's phandle '''
if 'phandle' not in self.props:
return False
return self.props['phandle'].words[0]
def get_interrupt_parent(self) -> int:
''' Return this node's interrupt parent's phandle '''
if 'interrupt-parent' not in self.props:
return self.parent.get_interrupt_parent()
return self.props['interrupt-parent'].words[0]
def is_mmio_device(self) -> bool:
''' Returns True if this node is an MMIO device '''
return self.is_cpu_addressable
def recursive_get_addr_cells(self) -> int:
''' This returns the #address-cells of this node, or otherwise
the parent. Note that this contravenes the spec in that
the default is meant to be 2 if unspecified, rather than the parent's value.
This is used by the IRQ parsing code to match Linux's behaviour. '''
if '#address-cells' in self.props:
return self.props['#address-cells'].words[0]
if self.parent is None:
return 2
return self.parent.recursive_get_addr_cells()
def get_addr_cells(self) -> int:
''' Return the number of 32-bit cells that children of this node
use to specify addresses '''
if '#address-cells' in self.props:
return self.props['#address-cells'].words[0]
# see devicetree spec v0.2, 2.3.5 "#address-cells and #size-cells"
return 2
def get_size_cells(self) -> int:
''' Return the number of 32-bit cells that children of this node
use to specify sizes. '''
if '#size-cells' in self.props:
return self.props['#size-cells'].words[0]
# see devicetree spec v0.2, 2.3.5 "#address-cells and #size-cells"
return 1
def get_regions(self) -> List[Region]:
if 'reg' not in self.props:
return []
reg = []
prop = list(self.props['reg'].words)
sizes = (self.parent.get_addr_cells(), self.parent.get_size_cells())
for r in Utils.intarray_iter(prop, sizes):
reg.append(Region(self.parent._translate_child_address(r[0]), r[1], self))
return reg
def parse_address(self, array) -> int:
''' parse a single address from the array. will pop values from the array '''
size = self.parent.get_addr_cells()
return Utils.make_number(size, array)
def get_interrupts(self, tree: 'FdtParser') -> List[int]:
irqs = []
if 'interrupts-extended' in self.props:
data = list(self.props['interrupts-extended'].words)
while len(data) > 0:
phandle = data.pop(0)
interrupt_parent = tree.get_irq_controller(phandle)
irqs.append(interrupt_parent.parse_irq(self, data))
elif 'interrupts' in self.props:
data = list(self.props['interrupts'].words)
interrupt_parent = tree.get_irq_controller(self.get_interrupt_parent())
while len(data) > 0:
irqs.append(interrupt_parent.parse_irq(self, data))
return irqs
def get_interrupt_affinities(self) -> List[int]:
if not self.has_prop('interrupt-affinity'):
return []
return list(self.get_prop('interrupt-affinity').words)
def visit(self, visitor: Any):
''' Visit this node and all its children '''
ret = [visitor(self)]
if ret[0] is None:
ret = []
for child in self.children.values():
ret += child.visit(visitor)
return ret
def __iter__(self) -> Generator['WrappedNode', None, None]:
''' Iterate over all immediate children of this node '''
for child in self.children.values():
yield child
def _translate_child_address(self, addr: int) -> int:
''' translate an address in this node's address space
to the parent's address space. '''
# see devicetree spec v0.2, 2.3.8 "ranges"
if self.parent is None:
# the root node does not need to do any translation of addresses
return addr
if 'ranges' not in self.props:
logging.warning('cannot translate address through node ' + self.path)
return -1
if not isinstance(self.props['ranges'], pyfdt.pyfdt.FdtPropertyWords):
return self.parent._translate_child_address(addr)
addr = Utils.translate_address(self, addr)
return self.parent._translate_child_address(addr)
def __hash__(self):
return hash(self.path)
def __repr__(self):
return 'WrappedNode(path={})'.format(self.path)
class Utils:
@staticmethod
def translate_address(node: WrappedNode, addr: int) -> int:
ranges_prop = node.props['ranges']
data = list(ranges_prop.words)
device_type = cast(pyfdt.pyfdt.FdtPropertyStrings, node.get_prop(
'device_type')).strings[0] if node.has_prop('device_type') else None
is_pci = device_type in ('pci', 'pciex')
# ranges is a list of tuples with the following format:
# <child-addr> <parent-addr> <length>
# child-addr is self.get_addr_cells() long
# parent-addr is self.parent.get_addr_cells() long
# length is self.get_size_cells() long
# the address 'child-addr' is at 'parent-addr' in the parent node.
child_addr_cells = node.get_addr_cells()
parent_addr_cells = node.parent.get_addr_cells()
size_cells = node.get_size_cells()
if is_pci:
# PCI needs to be handled specially, see
# https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf
child_addr_cells -= 1
# the top 32 bits for PCI devices contains configuration flags.
# we ignore it when doing this check.
addr &= (1 << (4 * child_addr_cells)) - 1
while len(data) > 0:
child_addr = Utils.make_number(child_addr_cells, data)
parent_addr = Utils.make_number(parent_addr_cells, data)
length = Utils.make_number(size_cells, data)
if child_addr <= addr < (child_addr + length):
return addr - child_addr + parent_addr
logging.warning("Could not translate 0x{:x} at {}, not translating".format(addr, node.path))
return addr
@staticmethod
def make_number(cells: int, array: List[int]) -> int:
ret = 0
for i in range(cells):
ret = (ret << 32)
ret |= array.pop(0)
return ret
@staticmethod
def intarray_iter(array: List[int], sizes: Tuple[int, ...]) -> Generator[List[int], None, None]:
i = 0
res = []
while len(array) > 0:
res.append(Utils.make_number(sizes[i], array))
i += 1
if i >= len(sizes):
yield res
i = 0
res = []