Skip to content
Snippets Groups Projects
cachedumplib.py 11.8 KiB
Newer Older
# Copyright (c) 2015, 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 struct
from print_out import print_out_str, print_out_exception

"""dictionary mapping from (hw_id, client_id, version) to class CacheDump"""
lookuptable = {}

def lookup_cache_type(hwid, client_id, version):
    """defaults to CacheDump() if no match found"""
    return lookuptable.get((hwid, client_id, version), CacheDump())

def formatwidth(string, limit):
    if len(string) >= limit:
        return string[0:limit]
    formatstr = '{{0:{0}}}'.format(limit)
    return formatstr.format(string)

class TableOutputFormat:
    """ Not sure if using PrettyTable (python lib) is a good idea, since people
    would need to install it"""

    def __init__(self):
        self.titlebar = []
        self.datafmt = []
        self.have_printed_title = False
        self.separator = ' '

    def addColumn(self, string, datafmt='{0}', width=0):
        width = max(len(string), width)
        string = formatwidth(string, width)
        self.titlebar.append(string)
        self.datafmt.append(datafmt)

    def printline(self, array, outfile):
        if (len(array) != len(self.titlebar)):
            raise Exception('BadTableDataSize', array, self.titlebar)

        if (not self.have_printed_title):
            self.have_printed_title = True
            outfile.write(self.separator.join(self.titlebar))
            outfile.write('\n')

        for item, title, fmt in zip(array, self.titlebar, self.datafmt):
            item = fmt.format(item)
            item = formatwidth(item, len(title))
            outfile.write(item)
            outfile.write(self.separator)

        outfile.write('\n')

class CacheDump(object):
    """ Class to describe a method to parse a particular type of cachedump.
    Users should not make instances of this class."""
    def __init__(self):
        """do nothing"""

    def parse(self, start, end, ramdump, outfile):
        """Called from debug_image_v2.py. Overriden by child classes"""
        raise NotImplementedError

struct_CacheDumpType_v1 = [
    ('<I', 'status0'),     #Status Registers
    ('I', 'status1'),
    ('I', 'status2'),
    ('I', 'status3'),
    ('I', 'TagSize'),     #Tag Size in u32 words
    ('I', 'LineSize'),    #Line Size in u32 words
    ('I', 'NumSets'),     #Number of sets
    ('I', 'NumWays'),     #Number of ways
    ('Q', 'next'),        #unused
    ('I', '__reserved0'),
    ('I', '__reserved1'),
    ('I', '__reserved2'),
    ('I', '__reserved3'),
]
CacheDumpFormatStr_v1 = ''.join(zip(*struct_CacheDumpType_v1)[0])
CacheDumpKeys_v1 = zip(*struct_CacheDumpType_v1)[1]

class CacheDumpType_v1(CacheDump):
    """Uses the format struct_CacheDumpType_v1, followed by an array of raw data"""

    def __init__(self):
        super(CacheDumpType_v1, self).__init__()
        self.tableformat = TableOutputFormat()
        self.tableformat.addColumn('Way', '{0:01x}')
        self.tableformat.addColumn('Set', '{0:03x}')
        self.ramdump = None
        self.linefmt = None
        self.HACK_HEADER_OFFSET = -1

        for key in CacheDumpKeys_v1:
            setattr(self, key, None)

    def add_table_data_columns(self):
        for i in range(0, self.LineSize):
            str ="DATA{0}".format(i)
            self.tableformat.addColumn(str, '{0:08x}', 8)

    def read_line(self, start):
        if self.linefmt is None:
            self.linefmt = '<'
            self.linefmt += 'I'*(self.TagSize + self.LineSize)
        return self.ramdump.read_string(start, self.linefmt, virtual=False)

    def parse_tag_fn(output, data, nset, nway):
        """append data elements to output. Overriden by child classes"""
        raise NotImplementedError

    def parse_header(self, start, end):
        """add the information from the header to this object. Returns
        number of bytes read"""

        if self.HACK_HEADER_OFFSET >= 0:
            return self.HACK_HEADER_OFFSET

        items = self.ramdump.read_string(start, CacheDumpFormatStr_v1, virtual=False)
        if items is None:
            raise Exception('Unable to read header information')

        for i in range(len(items)):
            setattr(self, CacheDumpKeys_v1[i], items[i])

        struct_size = struct.calcsize(CacheDumpFormatStr_v1)
        size = 0x4 * (self.LineSize + self.TagSize) * self.NumWays * self.NumSets
        size = size + struct_size

        if (size < 0x1000 or size > end - start):
            raise Exception('Unable to read header information')

        return struct_size

    def parse(self, start, end, ramdump, outfile):
        self.ramdump = ramdump

        start = start + self.parse_header(start, end)
        self.add_table_data_columns()

        for nset in range(self.NumSets):
            for nway in range(self.NumWays):
                if start > end:
                    raise Exception('past the end of array')

                output = [nway, nset]
                line = self.read_line(start)
                self.parse_tag_fn(output, line[0:self.TagSize], nset, nway)
                output.extend(line[self.TagSize:])
                self.tableformat.printline(output, outfile)
                start = start + (self.TagSize + self.LineSize) * 0x4

class L1_DCache_A53(CacheDumpType_v1):
    """Refer to ARM documentation:cortex_a53_trm"""
    def __init__(self):
        super(L1_DCache_A53, self).__init__()
        self.tableformat.addColumn('P')
        self.tableformat.addColumn('MOESI')
        self.tableformat.addColumn('RAW_MOESI', '{0:04x}')
        self.tableformat.addColumn('N')
        self.tableformat.addColumn('Addr [39:12]', '{0:016x}', 16)
        self.tableformat.addColumn('P')
        self.tableformat.addColumn('DC', '{0:02b}')
        self.tableformat.addColumn('0A', '{0:02b}')
        self.tableformat.addColumn('0S', '{0:02b}')
        self.HACK_HEADER_OFFSET = 0
        self.TagSize = 2
        self.LineSize = 16
        self.NumSets = 0x80
        self.NumWays = 4

    def MOESI_to_string(self, num):
        if (num & 0xC == 0x0):
            return 'I'
        if ((num & 0x4 == 0x4) and (num & 0x1 == 0x0)):
            return 'S'
        if ((num & 0x4 == 0x4) and (num & 0x1 == 0x1)):
            return 'O'
        if ((num & 0x8 == 0x8) and (num & 0x1 == 0x0)):
            return 'E'
        if ((num & 0x8 == 0x8) and (num & 0x1 == 0x1)):
            return 'M'

    def parse_tag_fn(self, output, data, nset, nway):
        p1 = (data[1] >> 31) & 0x1
        m1 = (data[1] >> 29) & 0x3
        n = (data[1] >> 28) & 0x1
        addr1 = (data[1] >> 0) & 0xfffffff
        addr2 = (data[0] >> 31) & 0x1
        p2 = (data[0] >> 5) & 0x1
        dc = (data[0] >> 4) & 0x1
        oa = (data[0] >> 3) & 0x1
        os = (data[0] >> 2) & 0x1
        m2 = (data[0] >> 0) & 0x3

        moesi = m1 << 2 | m2
        addr = ((addr1 << 1 | addr2) << 11) | (nset << 6)
        output.append(p1)
        output.append(self.MOESI_to_string(moesi))
        output.append(moesi)
        output.append(n)
        output.append(addr)
        output.append(p2)
        output.append(dc)
        output.append(oa)
        output.append(os)

class L1_DCache_A57(CacheDumpType_v1):
    """Refer to ARM documentation:cortex_a57_trm"""
    def __init__(self):
        super(L1_DCache_A57, self).__init__()
        self.tableformat.addColumn('MESI')
        self.tableformat.addColumn('RAW_MESI', '{0:02}')
        self.tableformat.addColumn('N')
        self.tableformat.addColumn('PA [43:14]', '{0:016x}', 16)
        self.HACK_HEADER_OFFSET = 0xC
        self.TagSize = 2
        self.LineSize = 16
        self.NumSets = 0x100
        self.NumWays = 2

    def MESI_to_string(self, num):
        if (num == 0x0):
            return 'I'
        elif (num == 0x1):
            return 'E'
        elif (num == 0x2):
            return 'S'
        elif (num == 0x3):
            return 'M'
        else:
            raise Exception('invalid MOESI value:{:x}'.format(num))

    def parse_tag_fn(self, output, data, nset, nway):
        if self.TagSize != 2:
            raise Exception('cache tag size mismatch')

        mesi = (data[1] >> 0) & 0x3
        n = (data[0] >> 30) & 0x1
        addr = (data[0] >> 0) & 0x3fffffff

        addr = (addr << 14) | (nset << 6)
        output.append(self.MESI_to_string(mesi))
        output.append(mesi)
        output.append(n)
        output.append(addr)

class L1_ICache_A57(CacheDumpType_v1):
    """Refer to ARM documentation:cortex_a57_trm"""
    def __init__(self):
        super(L1_ICache_A57, self).__init__()
        self.tableformat.addColumn('VALID')
        self.tableformat.addColumn('N')
        self.tableformat.addColumn('PA [43:12]', '{0:016x}', 16)
        self.HACK_HEADER_OFFSET = 0
        self.TagSize = 2
        self.LineSize = 16
        self.NumSets = 0x100
        self.NumWays = 2

    def parse_tag_fn(self, output, data, nset, nway):
        if self.TagSize != 2:
            raise Exception('cache tag size mismatch')

        valid = (data[1] >> 1) & 0x1
        n = (data[1] >> 0) & 0x1
        addr = (data[0] >> 0) & 0xffffffff

        addr = (addr << 12) | (nset << 6)
        output.append(valid)
        output.append(n)
        output.append(addr)

class L2_Cache_A57(CacheDumpType_v1):
    """Refer to ARM documentation:cortex_a57_trm"""
    def __init__(self):
        super(L2_Cache_A57, self).__init__()
        self.tableformat.addColumn('MESI')
        self.tableformat.addColumn('Raw MESI', '{0:02}')
        self.tableformat.addColumn('N')
        self.tableformat.addColumn('PA [43:15]', '{0:016x}', 16)
        self.HACK_HEADER_OFFSET = 0
        self.TagSize = 4
        self.LineSize = 16
        self.NumSets = 0x800
        self.NumWays = 0x10

    def MOESI_to_string(self, num):
        if (num == 0x0):
            return 'I'
        elif (num == 0x1):
            return 'E or M'
        elif (num == 0x2):
            raise Exception('invalid MOESI value:{:x}'.format(num))
        elif (num == 0x3):
            return 'S or O'
        else:
            raise Exception('invalid MOESI value:{:x}'.format(num))

    def parse_tag_fn(self, output, data, nset, nway):
        if self.TagSize != 4:
            raise Exception('cache tag size mismatch')

        n = (data[0] >> 31) & 0x1
        addr = (data[0] >> 2) & 0x1fffffff
        moesi = (data[0] >> 0) & 0x3

        addr = (addr << 15) | (nset << 6)
        output.append(self.MOESI_to_string(moesi))
        output.append(moesi)
        output.append(n)
        output.append(addr)

#8994

lookuptable[(8994, 0x80, 0)] = L1_DCache_A53()
lookuptable[(8994, 0x81, 0)] = L1_DCache_A53()
lookuptable[(8994, 0x82, 0)] = L1_DCache_A53()
lookuptable[(8994, 0x83, 0)] = L1_DCache_A53()
lookuptable[(8994, 0x84, 0)] = L1_DCache_A57()
lookuptable[(8994, 0x85, 0)] = L1_DCache_A57()
lookuptable[(8994, 0x86, 0)] = L1_DCache_A57()
lookuptable[(8994, 0x87, 0)] = L1_DCache_A57()

lookuptable[(8994, 0x64, 0)] = L1_ICache_A57()
lookuptable[(8994, 0x65, 0)] = L1_ICache_A57()
lookuptable[(8994, 0x66, 0)] = L1_ICache_A57()
lookuptable[(8994, 0x67, 0)] = L1_ICache_A57()

lookuptable[(8994, 0xC1, 0)] = L2_Cache_A57()

#8992
lookuptable[(8992, 0x80, 0x100)] = L1_DCache_A53()
lookuptable[(8992, 0x81, 0x100)] = L1_DCache_A53()
lookuptable[(8992, 0x82, 0x100)] = L1_DCache_A53()
lookuptable[(8992, 0x83, 0x100)] = L1_DCache_A53()
lookuptable[(8992, 0x84, 0x100)] = L1_DCache_A57()
lookuptable[(8992, 0x85, 0x100)] = L1_DCache_A57()

lookuptable[(8992, 0x64, 0x100)] = L1_ICache_A57()
lookuptable[(8992, 0x65, 0x100)] = L1_ICache_A57()

lookuptable[(8992, 0xC1, 0x100)] = L2_Cache_A57()