def get_interrupts(self, tree: FdtParser, node: WrappedNode) -> List[KernelInterrupt]: ''' Returns a list of KernelInterrupts that this rule says are used by the kernel for this device. ''' ret = [] interrupts = node.get_interrupts(tree) for name, rule in self.interrupts.items(): irq_desc = '{} generated from {}'.format(name, node.path) if type(rule) == dict: en_macro = rule.get('enable_macro', None) if rule['index'] >= len(interrupts): # XXX: skip this rule silently. continue defaultIrq = interrupts[rule['index']] sel_macro = rule.get('sel_macro', None) falseIrq = interrupts[rule['undef_index']] if 'undef_index' in rule else -1 prio = rule.get('priority', 0) irq = KernelInterrupt(name, defaultIrq, prio, sel_macro, falseIrq, en_macro, desc=irq_desc) elif type(rule) == int: if rule >= len(interrupts): # XXX: skip this rule silently. continue irq = KernelInterrupt(name, interrupts[rule], desc=irq_desc) else: # rule == 'boot-cpu' affinities = node.get_interrupt_affinities() boot_cpu = tree.get_boot_cpu() idx = affinities.index(boot_cpu) irq = KernelInterrupt(name, interrupts[idx]) ret.append(irq) return ret
def get_matched_compatible(self, device: WrappedNode) -> str: ''' Returns the best matching compatible string for this device ''' if not device.has_prop('compatible'): raise ValueError( 'Not sure what to do with node {} with no compatible!'.format(device.path)) for compat in device.get_prop('compatible').strings: if compat in self.rules: return compat return None
def _walk(self): ''' walk the underlying fdt, constructing wrapped nodes as we go ''' root = self.fdt.get_rootnode() self.wrapped_root = WrappedNode(root, None, '/') # for easier parent lookups self.by_path = {'/': self.wrapped_root} for (name, node) in root.walk(): if not isinstance(node, pyfdt.pyfdt.FdtNode): continue parent_path = name.rsplit('/', 1)[0] if parent_path == '': parent_path = '/' parent = self.by_path[parent_path] wrapped = WrappedNode(node, parent, name) self.by_path[name] = wrapped if not wrapped.get_phandle(): continue self.by_phandle[wrapped.get_phandle()] = wrapped # We only care about interrupt controllers with phandles - # if an interrupt controller has no phandle, it isn't used anywhere # and so we can safely ignore it. if wrapped.has_prop('interrupt-controller') or wrapped.has_prop( 'interrupt-map'): self.irq_controllers[ wrapped.get_phandle()] = create_irq_controller( wrapped, self)
def create_irq_controller(node: WrappedNode, tree: 'FdtParser'): if node.has_prop('interrupt-map'): # interrupt nexus return InterruptNexus(node, tree) elif node.has_prop('compatible'): # try and find a matching class that will know how to parse it for compat in node.get_prop('compatible').strings: if compat in CONTROLLERS: return CONTROLLERS[compat](node, tree) # otherwise, just return a dummy irq controller return IrqController(node, tree)
def get_rule(self, device: WrappedNode) -> DeviceRule: ''' Returns the matching DeviceRule for this device. ''' if not device.has_prop('compatible'): raise ValueError( 'Not sure what to do with node {} with no compatible!'.format(device.path)) for compat in device.get_prop('compatible').strings: if compat in self.rules: return self.rules[compat] raise ValueError('Failed to match compatibles "{}" for node {}!'.format( ', '.join(device.get_prop('compatible').strings), device.path))
def get_regions(self, node: WrappedNode) -> List[KernelRegionGroup]: ''' Returns a list of KernelRegionGroups that this rule specifies should be mapped into the kernel for this device. ''' ret = [] regions = node.get_regions() for (i, rule) in self.regions.items(): if i >= len(regions): # XXX: skip this rule silently continue reg = regions[i] kernel_name = rule['kernel'] user = rule.get('user', False) macro = rule.get('macro', None) max_size = 1 << self.config.get_page_bits() if 'kernel_size' in rule: max_size = rule['kernel_size'] elif max_size < reg.size: logging.warning( "Only mapping {}/{} bytes from node {}, region {}. Set kernel_size in YAML to silence." .format(max_size, reg.size, node.path, i)) ret.append( KernelRegionGroup(reg, kernel_name, self.config.get_page_bits(), max_size, macro, user)) return ret
def visitor(node: WrappedNode): if node.has_prop('device_type') and node.get_prop( 'device_type').strings[0] == 'memory': regions.update(node.get_regions())
class FdtParser: ''' Parse a DTB into a python representation ''' def __init__(self, dtb_file: IO[bytes]): self.fdt = pyfdt.pyfdt.FdtBlobParse(dtb_file).to_fdt() self.wrapped_root: WrappedNode self.by_phandle: Dict[int, WrappedNode] = {} self.by_path: Dict[str, WrappedNode] = {} self.irq_controllers: Dict[int, IrqController] = {} # initialise the "nice" representation of the tree self._walk() def _walk(self): ''' walk the underlying fdt, constructing wrapped nodes as we go ''' root = self.fdt.get_rootnode() self.wrapped_root = WrappedNode(root, None, '/') # for easier parent lookups self.by_path = {'/': self.wrapped_root} for (name, node) in root.walk(): if not isinstance(node, pyfdt.pyfdt.FdtNode): continue parent_path = name.rsplit('/', 1)[0] if parent_path == '': parent_path = '/' parent = self.by_path[parent_path] wrapped = WrappedNode(node, parent, name) self.by_path[name] = wrapped if not wrapped.get_phandle(): continue self.by_phandle[wrapped.get_phandle()] = wrapped # We only care about interrupt controllers with phandles - # if an interrupt controller has no phandle, it isn't used anywhere # and so we can safely ignore it. if wrapped.has_prop('interrupt-controller') or wrapped.has_prop( 'interrupt-map'): self.irq_controllers[ wrapped.get_phandle()] = create_irq_controller( wrapped, self) def get_phandle(self, phandle: int) -> WrappedNode: ''' Look up a node by phandle ''' return self.by_phandle[phandle] def get_path(self, path: str) -> WrappedNode: ''' Look up a node by path ''' return self.by_path.get(path, None) def get_irq_controller(self, phandle: int) -> IrqController: ''' Get an IRQ controller by phandle ''' return self.irq_controllers[phandle] def lookup_alias(self, alias: str) -> str: ''' Look up a node by its alias ''' alias = alias.split(':')[0] aliases = self.get_path('/aliases') if aliases is None or not aliases.has_prop(alias): raise KeyError('Unmatched alias {}'.format(alias)) prop = aliases.get_prop(alias) return prop.strings[0] def get_kernel_devices(self) -> List[WrappedNode]: ''' Returns a list of devices that are used by the kernel ''' chosen = self.get_path('/chosen') if not chosen.has_prop('seL4,kernel-devices'): return [] ret = [] paths = chosen.get_prop('seL4,kernel-devices').strings for path in paths: if path[0] != '/': path = self.lookup_alias(path) ret.append(self.get_path(path)) return ret def get_boot_cpu(self) -> int: ''' Returns phandle of the cpu node specified by the seL4,boot-cpu property ''' chosen = self.get_path('/chosen') if not chosen.has_prop('seL4,boot-cpu'): raise KeyError( 'must specify seL4,boot-cpu in /chosen to get boot cpu') prop = chosen.get_prop('seL4,boot-cpu') return prop.words[0] def visit(self, visitor: Any): ''' Walk the tree, calling the given visitor function on each node ''' self.wrapped_root.visit(visitor)