Revamp cbmem.py to use the coreboot tables.
[coreboot.git] / util / cbmem / cbmem.py
index 3e8476dbec36d68e2ab1d32b4d08fb55e71f5629..f4f3e887e7c2c4630ded3dc7aea8c10ebc5238cf 100755 (executable)
@@ -33,29 +33,46 @@ console sections.
 '''
 
 import mmap
-import re
 import struct
 import sys
-import time
 
-# These definitions follow src/include/cbmem.h
-CBMEM_MAGIC = 0x434f5245
-CBMEM_MAX_ENTRIES = 16
+def get_phys_mem(addr, size):
+    '''Read size bytes from address addr by mmaping /dev/mem'''
 
-CBMEM_ENTRY_FORMAT = '@LLQQ'
-CONSOLE_HEADER_FORMAT = '@LL'
-TIMESTAMP_HEADER_FORMAT = '@QLL'
-TIMESTAMP_ENTRY_FORMAT = '@LQ'
-
-mf_fileno = 0  # File number of the file providing access to memory.
-
-def align_up(base, alignment):
-    '''Increment to the alignment boundary.
-
-    Return the next integer larger than 'base' and divisible by 'alignment'.
-    '''
-
-    return base + alignment - base % alignment
+    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.
@@ -96,109 +113,141 @@ def get_cpu_freq():
     # Convert reading into Hertz
     return float(freq_str) * 1000.0
 
-def get_mem_size():
-    '''Retrieve amount of memory available to the CPU from /proc/meminfo.'''
-    mult = {
-        'kB': 1024
-        }
-    meminfo = open('/proc/meminfo').read()
-    m = re.search('MemTotal:.*\n', meminfo)
-    mem_string = re.search('MemTotal:.*\n', meminfo).group(0)
-    (_, size, mult_name) = mem_string.split()
-    return int(size) * mult[mult_name]
-
-def parse_mem_at(addr, format):
-    '''Read and parse a memory location.
-
-    This function reads memory at the passed in address, parses it according
-    to the passed in format specification and returns a list of values.
-
-    The first value in the list is the size of data matching the format
-    expression, and the rest of the elements of the list are the actual values
-    retrieved using the format.
-    '''
-
-    size = struct.calcsize(format)
-    delta = addr % 4096   # mmap requires the offset to be page size aligned.
-    mm = mmap.mmap(mf_fileno, size + delta,
-                   mmap.MAP_PRIVATE, offset=(addr - delta))
-    buf = mm.read(size + delta)
-    mm.close()
-    rv = [size,] + list(struct.unpack(format, buf[delta:size + delta + 1]))
-    return rv
-
-def dprint(text):
-    '''Debug print function.
-
-    Edit it to get the debug output.
-    '''
-
-    if False:
-        print text
-
 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.
     '''
 
-    (step, base_time, max_entr, entr) = parse_mem_at(
-        base, TIMESTAMP_HEADER_FORMAT)
-
-    print('\ntime base %d, total entries %d' % (base_time, entr))
+    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 + step
-    for i in range(entr):
-        (step, timer_id, timer_value) = parse_mem_at(
-            base, TIMESTAMP_ENTRY_FORMAT)
-        print '%d:%s ' % (timer_id, normalize_timer(timer_value, clock_freq)),
-        base = base + step
+    base = base + header.struct_len
+    for i in range(header.entr):
+        timestamp = TimestampEntry(base)
+        print '%d:%s ' % (timestamp.timer_id,
+            normalize_timer(timestamp.timer_value, clock_freq)),
+        base = base + timestamp.struct_len
     print
 
 def process_console(base):
     '''Dump the console log buffer contents found at address base.'''
 
-    (step, size, cursor) = parse_mem_at(base, CONSOLE_HEADER_FORMAT)
-    print 'cursor at %d\n' % cursor
+    class ConsoleHeader(CStruct):
+        struct_members = (
+            ("L", "size"),
+            ("L", "cursor")
+        )
 
-    cons_string_format = '%ds' % min(cursor, size)
-    (_, cons_text) = parse_mem_at(base + step, cons_string_format)
+    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'
 
-mem_alignment = 1024 * 1024 * 1024 # 1 GBytes
-table_alignment = 128 * 1024
-
-mem_size = get_mem_size()
-
-# start at memory address aligned at 128K.
-offset = align_up(mem_size, table_alignment)
-
-dprint('mem_size %x offset %x' %(mem_size, offset))
-mf = open("/dev/mem")
-mf_fileno = mf.fileno()
-
-while offset % mem_alignment: # do not cross the 1G boundary while searching
-    (step, magic, mid, base, size) = parse_mem_at(offset, CBMEM_ENTRY_FORMAT)
-    if magic == CBMEM_MAGIC:
-        offset = offset + step
-        break
-    offset += table_alignment
-else:
-    print 'Did not find the CBMEM'
-    sys.exit(0)
-
-for i in (range(1, CBMEM_MAX_ENTRIES)):
-    (_, magic, mid, base, size) = parse_mem_at(offset, CBMEM_ENTRY_FORMAT)
-    if mid == 0:
-        break
-
-    print '%x, %x, %x' % (mid, base, size)
-    if mid == 0x54494d45:
-        process_timers(base)
-    if mid == 0x434f4e53:
-        process_console(base)
-
-    offset = offset + step
-
-mf.close()
+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())