From 29d3f2edc8f8235a0a77273431371ad6ce05ec50 Mon Sep 17 00:00:00 2001 From: Mitchel Humpherys <mitchelh@codeaurora.org> Date: Fri, 4 Apr 2014 17:33:08 -0700 Subject: [PATCH] lrdp-v2: refactor Iommu domain parsing code The Iommu domain parsing code is common across different Iommu page table types (normal vs LPAE). Pull this code out so that it can be easily re-used later when LPAE page table support is added. Change-Id: Ifc39c12428a9c090f8197d997ddd293e0ae1f0b1 --- linux-ramdump-parser-v2/iommulib.py | 107 +++++++++++++++++++ linux-ramdump-parser-v2/linux_list.py | 32 ++---- linux-ramdump-parser-v2/parsers/iommu.py | 124 +---------------------- linux-ramdump-parser-v2/rb_tree.py | 16 +-- 4 files changed, 128 insertions(+), 151 deletions(-) create mode 100644 linux-ramdump-parser-v2/iommulib.py diff --git a/linux-ramdump-parser-v2/iommulib.py b/linux-ramdump-parser-v2/iommulib.py new file mode 100644 index 0000000..fc941e2 --- /dev/null +++ b/linux-ramdump-parser-v2/iommulib.py @@ -0,0 +1,107 @@ +# Copyright (c) 2014, The Linux Foundation. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 and +# only version 2 as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import rb_tree +import linux_list as llist + +class Domain(object): + def __init__(self, domain_num, pg_table, redirect, ctx_list, client_name): + self.domain_num = domain_num + self.pg_table = pg_table + self.redirect = redirect + self.ctx_list = ctx_list + self.client_name = client_name + +class IommuLib(object): + def __init__(self, ramdump): + self.ramdump = ramdump + self.domain_list = [] + + root = self.ramdump.read_word('domain_root') + if root is None: + return + rb_walker = rb_tree.RbTreeWalker(self.ramdump) + rb_walker.walk(root, self._iommu_domain_func, self.domain_list) + + def _iommu_list_func(self, node, ctx_list): + ctx_drvdata_name_ptr = self.ramdump.read_word( + node + self.ramdump.field_offset('struct msm_iommu_ctx_drvdata', 'name')) + ctxdrvdata_num_offset = self.ramdump.field_offset( + 'struct msm_iommu_ctx_drvdata', 'num') + num = self.ramdump.read_word(node + ctxdrvdata_num_offset) + if ctx_drvdata_name_ptr != 0: + name = self.ramdump.read_cstring(ctx_drvdata_name_ptr, 100) + ctx_list.append((num, name)) + + def _iommu_domain_func(self, node, domain_list): + domain_num = self.ramdump.read_word(self.ramdump.sibling_field_addr( + node, 'struct msm_iova_data', 'node', 'domain_num')) + domain = self.ramdump.read_word(self.ramdump.sibling_field_addr( + node, 'struct msm_iova_data', 'node', 'domain')) + priv_ptr = self.ramdump.read_word( + domain + self.ramdump.field_offset('struct iommu_domain', 'priv')) + + client_name_offset = self.ramdump.field_offset( + 'struct msm_iommu_priv', 'client_name') + + if client_name_offset is not None: + client_name_ptr = self.ramdump.read_word( + priv_ptr + self.ramdump.field_offset( + 'struct msm_iommu_priv', 'client_name')) + if client_name_ptr != 0: + client_name = self.ramdump.read_cstring(client_name_ptr, 100) + else: + client_name = '(null)' + else: + client_name = 'unknown' + + list_attached_offset = self.ramdump.field_offset( + 'struct msm_iommu_priv', 'list_attached') + + if list_attached_offset is not None: + list_attached = self.ramdump.read_word(priv_ptr + list_attached_offset) + else: + list_attached = None + + priv_pt_offset = self.ramdump.field_offset('struct msm_iommu_priv', 'pt') + pgtable_offset = self.ramdump.field_offset('struct msm_iommu_pt', 'fl_table') + redirect_offset = self.ramdump.field_offset('struct msm_iommu_pt', 'redirect') + + if priv_pt_offset is not None: + pg_table = self.ramdump.read_word( + priv_ptr + priv_pt_offset + pgtable_offset) + redirect = self.ramdump.read_word( + priv_ptr + priv_pt_offset + redirect_offset) + else: + # On some builds we are unable to look up the offsets so hardcode + # the offsets. + pg_table = self.ramdump.read_word(priv_ptr + 0) + redirect = self.ramdump.read_word(priv_ptr + self.ramdump.sizeof('void *')) + + # Note: On some code bases we don't have this pg_table and redirect in the priv structure (see msm_iommu_sec.c). It only + # contains list_attached. If this is the case we can detect that by checking whether + # pg_table == redirect (prev == next pointers of the attached + # list). + if pg_table == redirect: + # This is a secure domain. We don't have access to the page + # tables. + pg_table = 0 + redirect = None + + ctx_list = [] + if list_attached is not None and list_attached != 0: + list_walker = llist.ListWalker( + self.ramdump, list_attached, + self.ramdump.field_offset('struct msm_iommu_ctx_drvdata', 'attached_elm')) + list_walker.walk(list_attached, self._iommu_list_func, extra=ctx_list) + + domain_list.append( + Domain(domain_num, pg_table, redirect, ctx_list, client_name)) diff --git a/linux-ramdump-parser-v2/linux_list.py b/linux-ramdump-parser-v2/linux_list.py index cb526a3..3a3295c 100644 --- a/linux-ramdump-parser-v2/linux_list.py +++ b/linux-ramdump-parser-v2/linux_list.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013, The Linux Foundation. All rights reserved. +# Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 and @@ -17,50 +17,32 @@ struct list_head { }; ''' - -def get_list_offsets(ram_dump): - next_offset = ram_dump.field_offset('struct list_head', 'next') - prev_offset = ram_dump.field_offset('struct list_head', 'prev') - return next_offset, prev_offset - - class ListWalker(object): ''' ram_dump: Reference to the ram dump node_addr: The address of the first element of the list list_elem_offset: The offset of the list_head in the structure that this list is container for. - next_offset: The offset for the next pointer in the list - prev_offset: The offset for the prev pointer in the list ''' - def __init__(self, ram_dump, node_addr, list_elem_offset, next_offset, prev_offset): - self.LIST_OFFSETS = [ - ('((struct list_head *)0x0)', 'next', 0, 0), - ('((struct list_head *)0x0)', 'prev', 0, 0), - ] - self.LIST_NEXT_IDX = 0 - self.LIST_PREV_IDX = 1 + def __init__(self, ram_dump, node_addr, list_elem_offset): self.ram_dump = ram_dump - self.next_offset = next_offset - self.prev_offset = prev_offset self.list_elem_offset = list_elem_offset - self.last_node = node_addr self.seen_nodes = [] - def walk(self, node_addr, func): + def walk(self, node_addr, func, extra=None): if node_addr != 0: - func(node_addr - self.list_elem_offset) + func(node_addr - self.list_elem_offset, extra) - next_node_addr = node_addr + self.next_offset + next_node_addr = node_addr + self.ram_dump.field_offset('struct list_head', 'next') next_node = self.ram_dump.read_word(next_node_addr) if next_node != self.last_node: if next_node in self.seen_nodes: print_out_str( - '[!] WARNING: Cycle found in attach list for IOMMU domain. List is corrupted!') + '[!] WARNING: Cycle found in list. List is corrupted!') else: self.seen_nodes.append(node_addr) - self.walk(next_node, func) + self.walk(next_node, func, extra) diff --git a/linux-ramdump-parser-v2/parsers/iommu.py b/linux-ramdump-parser-v2/parsers/iommu.py index bb3e265..0216ee9 100644 --- a/linux-ramdump-parser-v2/parsers/iommu.py +++ b/linux-ramdump-parser-v2/parsers/iommu.py @@ -11,27 +11,14 @@ import math -import rb_tree -import linux_list as llist from print_out import print_out_str from parser_util import register_parser, RamParser from sizes import SZ_4K, SZ_64K, SZ_1M, SZ_16M, get_order, order_size_strings - -IOMMU_DOMAIN_VAR = 'domain_root' - +from iommulib import IommuLib @register_parser('--print-iommu-pg-tables', 'Print IOMMU page tables') class IOMMU(RamParser): - class Domain(object): - - def __init__(self): - self.domain_num = -1 - self.pg_table = 0 - self.redirect = 0 - self.ctx_name = '' - self.client_name = '' - class FlatMapping(object): def __init__(self, virt, phys=-1, type='[]', size=SZ_4K, mapped=False): @@ -61,7 +48,6 @@ class IOMMU(RamParser): def __init__(self, *args): super(IOMMU, self).__init__(*args) self.out_file = None - self.domain_list = [] self.NUM_FL_PTE = 4096 self.NUM_SL_PTE = 256 @@ -90,37 +76,9 @@ class IOMMU(RamParser): self.SL_CACHEABLE = (1 << 3) self.SL_TEX0 = (1 << 6) self.SL_NG = (1 << 11) - self.ctxdrvdata_name_offset = 0 - self.ctxdrvdata_num_offset = 0 - self.ctx_list = [] self.node_offset = self.ramdump.field_offset( 'struct msm_iova_data', 'node') - self.domain_num_offset = self.ramdump.field_offset( - 'struct msm_iova_data', 'domain_num') - self.domain_offset = self.ramdump.field_offset( - 'struct msm_iova_data', 'domain') - self.priv_offset = self.ramdump.field_offset( - 'struct iommu_domain', 'priv') - self.ctxdrvdata_attached_offset = self.ramdump.field_offset( - 'struct msm_iommu_ctx_drvdata', 'attached_elm') - self.ctxdrvdata_name_offset = self.ramdump.field_offset( - 'struct msm_iommu_ctx_drvdata', 'name') - self.ctxdrvdata_num_offset = self.ramdump.field_offset( - 'struct msm_iommu_ctx_drvdata', 'num') - self.priv_pt_offset = self.ramdump.field_offset( - 'struct msm_iommu_priv', 'pt') - self.list_attached_offset = self.ramdump.field_offset( - 'struct msm_iommu_priv', 'list_attached') - self.client_name_offset = self.ramdump.field_offset( - 'struct msm_iommu_priv', 'client_name') - self.pgtable_offset = self.ramdump.field_offset( - 'struct msm_iommu_pt', 'fl_table') - self.redirect_offset = self.ramdump.field_offset( - 'struct msm_iommu_pt', 'redirect') - - self.list_next_offset, self.list_prev_offset = llist.get_list_offsets( - self.ramdump) def fl_offset(va): return (((va) & 0xFFF00000) >> 20) @@ -128,76 +86,6 @@ class IOMMU(RamParser): def sl_offset(va): return (((va) & 0xFF000) >> 12) - def list_func(self, node): - ctx_drvdata_name_ptr = self.ramdump.read_word( - node + self.ctxdrvdata_name_offset) - num = self.ramdump.read_word(node + self.ctxdrvdata_num_offset) - - if ctx_drvdata_name_ptr != 0: - name = self.ramdump.read_cstring(ctx_drvdata_name_ptr, 100) - self.ctx_list.append((num, name)) - - def iommu_domain_func(self, node): - - domain_num_addr = (node - self.node_offset) + self.domain_num_offset - domain_num = self.ramdump.read_word(domain_num_addr) - - domain_addr = (node - self.node_offset) + self.domain_offset - domain = self.ramdump.read_word(domain_addr) - - priv_ptr = self.ramdump.read_word(domain + self.priv_offset) - - if self.client_name_offset is not None: - client_name_ptr = self.ramdump.read_word( - priv_ptr + self.client_name_offset) - if client_name_ptr != 0: - client_name = self.ramdump.read_cstring(client_name_ptr, 100) - else: - client_name = '(null)' - else: - client_name = 'unknown' - - if self.list_attached_offset is not None: - list_attached = self.ramdump.read_word( - priv_ptr + self.list_attached_offset) - else: - list_attached = None - - if self.priv_pt_offset is not None: - pg_table = self.ramdump.read_word( - priv_ptr + self.priv_pt_offset + self.pgtable_offset) - redirect = self.ramdump.read_word( - priv_ptr + self.priv_pt_offset + self.redirect_offset) - else: - # On some builds we are unable to look up the offsets so hardcode - # the offsets. - pg_table = self.ramdump.read_word(priv_ptr + 0) - redirect = self.ramdump.read_word(priv_ptr + 4) - - # Note: On some code bases we don't have this pg_table and redirect in the priv structure (see msm_iommu_sec.c). It only - # contains list_attached. If this is the case we can detect that by checking whether - # pg_table == redirect (prev == next pointers of the attached - # list). - if pg_table == redirect: - # This is a secure domain. We don't have access to the page - # tables. - pg_table = 0 - redirect = None - - if list_attached is not None and list_attached != 0: - list_walker = llist.ListWalker( - self.ramdump, list_attached, self.ctxdrvdata_attached_offset, self.list_next_offset, self.list_prev_offset) - list_walker.walk(list_attached, self.list_func) - - dom = self.Domain() - dom.domain_num = domain_num - dom.pg_table = pg_table - dom.redirect = redirect - dom.ctx_list = self.ctx_list - dom.client_name = client_name - self.ctx_list = [] - self.domain_list.append(dom) - def print_sl_page_table(self, pg_table): sl_pte = pg_table for i in range(0, self.NUM_SL_PTE): @@ -415,17 +303,13 @@ class IOMMU(RamParser): (mapping.virt_start, mapping.virt_end, mapping.virt_size())) def parse(self): - iommu_domains_rb_root = self.ramdump.addr_lookup(IOMMU_DOMAIN_VAR) - if iommu_domains_rb_root is None: + ilib = IommuLib(self.ramdump) + self.domain_list = ilib.domain_list + if self.domain_list is None: print_out_str( '[!] WARNING: IOMMU domains was not found in this build. No IOMMU page tables will be generated') return - iommu_domains_rb_root_addr = self.ramdump.read_word( - iommu_domains_rb_root) - rb_walker = rb_tree.RbTreeWalker(self.ramdump) - rb_walker.walk(iommu_domains_rb_root_addr, self.iommu_domain_func) - for d in self.domain_list: self.out_file = self.ramdump.open_file( 'msm_iommu_domain_%02d.txt' % (d.domain_num)) diff --git a/linux-ramdump-parser-v2/rb_tree.py b/linux-ramdump-parser-v2/rb_tree.py index b7a5d33..0eacfd0 100644 --- a/linux-ramdump-parser-v2/rb_tree.py +++ b/linux-ramdump-parser-v2/rb_tree.py @@ -30,21 +30,25 @@ class RbTreeWalker(object): self.left_offset = self.ram_dump.field_offset( 'struct rb_node', 'rb_left') - def _walk(self, node, func, seen): + def _walk(self, node, func, seen, extra): if node != 0: left_node_addr = node + self.left_offset left_node = self.ram_dump.read_word(left_node_addr) if left_node not in seen: seen.append(left_node) - self._walk(left_node, func, seen) + self._walk(left_node, func, seen, extra) - func(node) + func(node, extra) right_node_addr = node + self.right_offset right_node = self.ram_dump.read_word(right_node_addr) if right_node not in seen: seen.append(right_node) - self._walk(right_node, func, seen) + self._walk(right_node, func, seen, extra) - def walk(self, node, func): - self._walk(node, func, []) + def walk(self, node, func, extra=None): + """Walks the RbTree, calling `func' on each iteration. `func' receives + two arguments: the current `struct rb_node', and `extra'. + + """ + self._walk(node, func, [], extra) -- GitLab