This is a cosmetic change which formats timestamp information
retrieved by cbmem.py.
Instead of printing timestamps in a single line, print them one per
line and add time (in us) elapsed since the previous timestamp.
     time base 4149594, total entries 18
     1:56,928
     2:58,851  (1,923)
     3:175,230  (116,378)
     4:175,340  (109)
     8:177,199  (1,859)
     9:214,368  (37,168)
     10:214,450  (81)
     30:214,462  (11)
     40:215,205  (743)
     50:217,180  (1,974)
     60:217,312  (132)
     70:436,984  (219,671)
     75:436,993  (8)
     80:441,424  (4,431)
     90:442,487  (1,062)
     99:553,777  (111,289)
     1000:556,513  (2,736)
     1100:824,621  (268,107)
Change-Id: I0d25cafe766c10377017697e6b206276e1a92992
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: http://review.coreboot.org/1716
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
		
	
		
			
				
	
	
		
			260 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/python
 | 
						|
#
 | 
						|
# cbmem.py - Linux space CBMEM contents parser
 | 
						|
#
 | 
						|
# Copyright (C) 2011 The ChromiumOS Authors.  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 as published by
 | 
						|
# the Free Software Foundation; version 2 of the License
 | 
						|
#
 | 
						|
# 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.
 | 
						|
#
 | 
						|
# You should have received a copy of the GNU General Public License
 | 
						|
# along with this program; if not, write to the Free Software
 | 
						|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 | 
						|
#
 | 
						|
'''
 | 
						|
Parse and display CBMEM contents.
 | 
						|
 | 
						|
This module is meant to run on systems with coreboot based firmware.
 | 
						|
 | 
						|
When started, it determines the amount of DRAM installed on the system, and
 | 
						|
then scans the top area of DRAM (right above the available memory size)
 | 
						|
looking for the CBMEM base signature at locations aligned at 0x20000
 | 
						|
boundaries.
 | 
						|
 | 
						|
Once it finds the CBMEM signature, the utility parses the contents, reporting
 | 
						|
the section IDs/sizes and also reporting the contents of the tiemstamp and
 | 
						|
console sections.
 | 
						|
'''
 | 
						|
 | 
						|
import mmap
 | 
						|
import struct
 | 
						|
import sys
 | 
						|
 | 
						|
def get_phys_mem(addr, size):
 | 
						|
    '''Read size bytes from address addr by mmaping /dev/mem'''
 | 
						|
 | 
						|
    mf = open("/dev/mem")
 | 
						|
    delta = addr % 4096
 | 
						|
    mm = mmap.mmap(mf.fileno(), size + delta,
 | 
						|
                   mmap.MAP_PRIVATE, offset=(addr - delta))
 | 
						|
    buf = mm.read(size + delta)
 | 
						|
    mf.close()
 | 
						|
    return buf[delta:]
 | 
						|
 | 
						|
# This class and metaclass make it easier to define and access structures
 | 
						|
# which live in physical memory. To use them, inherit from CStruct and define
 | 
						|
# a class member called struct_members which is a tuple of pairs. The first
 | 
						|
# item in the pair is the type format specifier that should be used with
 | 
						|
# struct.unpack to read that member from memory. The second item is the name
 | 
						|
# that member should have in the resulting object.
 | 
						|
 | 
						|
class MetaCStruct(type):
 | 
						|
    def __init__(cls, name, bases, dct):
 | 
						|
        struct_members = dct["struct_members"]
 | 
						|
        cls.struct_fmt = "<"
 | 
						|
        for char, name in struct_members:
 | 
						|
            cls.struct_fmt += char
 | 
						|
        cls.struct_len = struct.calcsize(cls.struct_fmt)
 | 
						|
        super(MetaCStruct, cls).__init__(name, bases, dct)
 | 
						|
 | 
						|
class CStruct(object):
 | 
						|
    __metaclass__ = MetaCStruct
 | 
						|
    struct_members = ()
 | 
						|
 | 
						|
    def __init__(self, addr):
 | 
						|
        self.raw_memory = get_phys_mem(addr, self.struct_len)
 | 
						|
        values = struct.unpack(self.struct_fmt, self.raw_memory)
 | 
						|
        names = (name for char, name in self.struct_members)
 | 
						|
        for name, value in zip(names, values):
 | 
						|
            setattr(self, name, value)
 | 
						|
 | 
						|
def normalize_timer(value, freq):
 | 
						|
    '''Convert timer reading into microseconds.
 | 
						|
 | 
						|
    Get the free running clock counter value, divide it by the clock frequency
 | 
						|
    and multiply by 1 million to get reading in microseconds.
 | 
						|
 | 
						|
    Then convert the value into an ASCII string with groups of three digits
 | 
						|
    separated by commas.
 | 
						|
 | 
						|
    Inputs:
 | 
						|
      value: int, the clock reading
 | 
						|
      freq: float, the clock frequency
 | 
						|
 | 
						|
    Returns:
 | 
						|
      A string presenting 'value' in microseconds.
 | 
						|
    '''
 | 
						|
 | 
						|
    result = []
 | 
						|
    value = int(value * 1000000.0 / freq)
 | 
						|
    svalue = '%d' % value
 | 
						|
    vlength = len(svalue)
 | 
						|
    remainder = vlength % 3
 | 
						|
    if remainder:
 | 
						|
        result.append(svalue[0:remainder])
 | 
						|
    while remainder < vlength:
 | 
						|
        result.append(svalue[remainder:remainder+3])
 | 
						|
        remainder = remainder + 3
 | 
						|
    return ','.join(result)
 | 
						|
 | 
						|
def get_cpu_freq():
 | 
						|
    '''Retrieve CPU frequency from sysfs.
 | 
						|
 | 
						|
    Use /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq as the source.
 | 
						|
    '''
 | 
						|
    freq_str = open('/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq'
 | 
						|
                    ).read()
 | 
						|
    # Convert reading into Hertz
 | 
						|
    return float(freq_str) * 1000.0
 | 
						|
 | 
						|
def process_timers(base):
 | 
						|
    '''Scan the array of timestamps found in CBMEM at address base.
 | 
						|
 | 
						|
    For each timestamp print the timer ID and the value in microseconds.
 | 
						|
    '''
 | 
						|
 | 
						|
    class TimestampHeader(CStruct):
 | 
						|
        struct_members = (
 | 
						|
            ("Q", "base_time"),
 | 
						|
            ("L", "max_entr"),
 | 
						|
            ("L", "entr")
 | 
						|
        )
 | 
						|
 | 
						|
    class TimestampEntry(CStruct):
 | 
						|
        struct_members = (
 | 
						|
            ("L", "timer_id"),
 | 
						|
            ("Q", "timer_value")
 | 
						|
        )
 | 
						|
 | 
						|
    header = TimestampHeader(base)
 | 
						|
    print('\ntime base %d, total entries %d' % (header.base_time, header.entr))
 | 
						|
    clock_freq = get_cpu_freq()
 | 
						|
    base = base + header.struct_len
 | 
						|
    prev_time = 0
 | 
						|
    for i in range(header.entr):
 | 
						|
        timestamp = TimestampEntry(base)
 | 
						|
        print '%d:%s ' % (timestamp.timer_id,
 | 
						|
            normalize_timer(timestamp.timer_value, clock_freq)),
 | 
						|
        if prev_time:
 | 
						|
            print '(%s)' % normalize_timer(
 | 
						|
                timestamp.timer_value - prev_time, clock_freq),
 | 
						|
        prev_time = timestamp.timer_value
 | 
						|
        print
 | 
						|
        base = base + timestamp.struct_len
 | 
						|
    print
 | 
						|
 | 
						|
def process_console(base):
 | 
						|
    '''Dump the console log buffer contents found at address base.'''
 | 
						|
 | 
						|
    class ConsoleHeader(CStruct):
 | 
						|
        struct_members = (
 | 
						|
            ("L", "size"),
 | 
						|
            ("L", "cursor")
 | 
						|
        )
 | 
						|
 | 
						|
    header = ConsoleHeader(base)
 | 
						|
    print 'cursor at %d\n' % header.cursor
 | 
						|
 | 
						|
    cons_addr = base + header.struct_len
 | 
						|
    cons_length = min(header.cursor, header.size)
 | 
						|
    cons_text = get_phys_mem(cons_addr, cons_length)
 | 
						|
    print cons_text
 | 
						|
    print '\n'
 | 
						|
 | 
						|
def ipchksum(buf):
 | 
						|
    '''Checksumming function used on the coreboot tables. The buffer being
 | 
						|
    checksummed is summed up as if it was an array of 16 bit unsigned
 | 
						|
    integers. If there are an odd number of bytes, the last element is zero
 | 
						|
    extended.'''
 | 
						|
 | 
						|
    size = len(buf)
 | 
						|
    odd = size % 2
 | 
						|
    fmt = "<%dH" % ((size - odd) / 2)
 | 
						|
    if odd:
 | 
						|
        fmt += "B"
 | 
						|
    shorts = struct.unpack(fmt, buf)
 | 
						|
    checksum = sum(shorts)
 | 
						|
    checksum = (checksum >> 16) + (checksum & 0xffff)
 | 
						|
    checksum += (checksum >> 16)
 | 
						|
    checksum = ~checksum & 0xffff
 | 
						|
    return checksum
 | 
						|
 | 
						|
def parse_tables(base, length):
 | 
						|
    '''Find the coreboot tables in memory and process whatever we can.'''
 | 
						|
 | 
						|
    class CBTableHeader(CStruct):
 | 
						|
        struct_members = (
 | 
						|
            ("4s", "signature"),
 | 
						|
            ("I", "header_bytes"),
 | 
						|
            ("I", "header_checksum"),
 | 
						|
            ("I", "table_bytes"),
 | 
						|
            ("I", "table_checksum"),
 | 
						|
            ("I", "table_entries")
 | 
						|
        )
 | 
						|
 | 
						|
    class CBTableEntry(CStruct):
 | 
						|
        struct_members = (
 | 
						|
            ("I", "tag"),
 | 
						|
            ("I", "size")
 | 
						|
        )
 | 
						|
 | 
						|
    class CBTableForward(CBTableEntry):
 | 
						|
        struct_members = CBTableEntry.struct_members + (
 | 
						|
            ("Q", "forward"),
 | 
						|
        )
 | 
						|
 | 
						|
    class CBMemTab(CBTableEntry):
 | 
						|
        struct_members = CBTableEntry.struct_members + (
 | 
						|
            ("L", "cbmem_tab"),
 | 
						|
        )
 | 
						|
 | 
						|
    for addr in range(base, base + length, 16):
 | 
						|
        header = CBTableHeader(addr)
 | 
						|
        if header.signature == "LBIO":
 | 
						|
            break
 | 
						|
    else:
 | 
						|
        return -1
 | 
						|
 | 
						|
    if header.header_bytes == 0:
 | 
						|
        return -1
 | 
						|
 | 
						|
    if ipchksum(header.raw_memory) != 0:
 | 
						|
        print "Bad header checksum"
 | 
						|
        return -1
 | 
						|
 | 
						|
    addr += header.header_bytes
 | 
						|
    table = get_phys_mem(addr, header.table_bytes)
 | 
						|
    if ipchksum(table) != header.table_checksum:
 | 
						|
        print "Bad table checksum"
 | 
						|
        return -1
 | 
						|
 | 
						|
    for i in range(header.table_entries):
 | 
						|
        entry = CBTableEntry(addr)
 | 
						|
        if entry.tag == 0x11: # Forwarding entry
 | 
						|
            return parse_tables(CBTableForward(addr).forward, length)
 | 
						|
        elif entry.tag == 0x16: # Timestamps
 | 
						|
            process_timers(CBMemTab(addr).cbmem_tab)
 | 
						|
        elif entry.tag == 0x17: # CBMEM console
 | 
						|
            process_console(CBMemTab(addr).cbmem_tab)
 | 
						|
 | 
						|
        addr += entry.size
 | 
						|
 | 
						|
    return 0
 | 
						|
 | 
						|
def main():
 | 
						|
    for base, length in (0x00000000, 0x1000), (0x000f0000, 0x1000):
 | 
						|
        if parse_tables(base, length):
 | 
						|
            break
 | 
						|
    else:
 | 
						|
        print "Didn't find the coreboot tables"
 | 
						|
        return 0
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    sys.exit(main())
 |