diff --git a/linux-ramdump-parser-v2/iommulib.py b/linux-ramdump-parser-v2/iommulib.py new file mode 100644 index 0000000000000000000000000000000000000000..fc941e20e5bd4ab73aa84f19c62a300015b7991a --- /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 cb526a30a5ba0a3752a538a1f6b5df3619048fda..3a3295c24a36111896f20e4bef2d1622a8c24800 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 bb3e2657a22d3642e993be0b4ba47fc4251f5ec9..0216ee9a597c86e811da9aa8329a74566932c016 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 b7a5d3387746ce33eaf0682f288ee92608a1be0d..0eacfd08588a1afea2cef13a572fc1352fd5f628 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)