Revamp cbmem.py to use the coreboot tables.
This change makes significant changes to cbmem.py to make it use the coreboot tables to find the memory console and timestamp areas instead of looking for the in memory table TOC structure. That appears to be more robust and gets cbmem.py working again after some unrelated changes that affected memory layout. It also introduces some small infrastructure to make accessing C style structures in physical memory easier and more transparent. Change-Id: I51833055a50c2d76423520ba6e059bf8fc50adea Signed-off-by: Gabe Black <gabeblack@google.com> Reviewed-on: http://review.coreboot.org/762 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
parent
67aa3d6b87
commit
5d3438de41
|
@ -33,29 +33,46 @@ console sections.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import mmap
|
import mmap
|
||||||
import re
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
|
|
||||||
# These definitions follow src/include/cbmem.h
|
def get_phys_mem(addr, size):
|
||||||
CBMEM_MAGIC = 0x434f5245
|
'''Read size bytes from address addr by mmaping /dev/mem'''
|
||||||
CBMEM_MAX_ENTRIES = 16
|
|
||||||
|
|
||||||
CBMEM_ENTRY_FORMAT = '@LLQQ'
|
mf = open("/dev/mem")
|
||||||
CONSOLE_HEADER_FORMAT = '@LL'
|
delta = addr % 4096
|
||||||
TIMESTAMP_HEADER_FORMAT = '@QLL'
|
mm = mmap.mmap(mf.fileno(), size + delta,
|
||||||
TIMESTAMP_ENTRY_FORMAT = '@LQ'
|
mmap.MAP_PRIVATE, offset=(addr - delta))
|
||||||
|
buf = mm.read(size + delta)
|
||||||
|
mf.close()
|
||||||
|
return buf[delta:]
|
||||||
|
|
||||||
mf_fileno = 0 # File number of the file providing access to memory.
|
# 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.
|
||||||
|
|
||||||
def align_up(base, alignment):
|
class MetaCStruct(type):
|
||||||
'''Increment to the alignment boundary.
|
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)
|
||||||
|
|
||||||
Return the next integer larger than 'base' and divisible by 'alignment'.
|
class CStruct(object):
|
||||||
'''
|
__metaclass__ = MetaCStruct
|
||||||
|
struct_members = ()
|
||||||
|
|
||||||
return base + alignment - base % alignment
|
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):
|
def normalize_timer(value, freq):
|
||||||
'''Convert timer reading into microseconds.
|
'''Convert timer reading into microseconds.
|
||||||
|
@ -96,109 +113,141 @@ def get_cpu_freq():
|
||||||
# Convert reading into Hertz
|
# Convert reading into Hertz
|
||||||
return float(freq_str) * 1000.0
|
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):
|
def process_timers(base):
|
||||||
'''Scan the array of timestamps found in CBMEM at address base.
|
'''Scan the array of timestamps found in CBMEM at address base.
|
||||||
|
|
||||||
For each timestamp print the timer ID and the value in microseconds.
|
For each timestamp print the timer ID and the value in microseconds.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
(step, base_time, max_entr, entr) = parse_mem_at(
|
class TimestampHeader(CStruct):
|
||||||
base, TIMESTAMP_HEADER_FORMAT)
|
struct_members = (
|
||||||
|
("Q", "base_time"),
|
||||||
|
("L", "max_entr"),
|
||||||
|
("L", "entr")
|
||||||
|
)
|
||||||
|
|
||||||
print('\ntime base %d, total entries %d' % (base_time, 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()
|
clock_freq = get_cpu_freq()
|
||||||
base = base + step
|
base = base + header.struct_len
|
||||||
for i in range(entr):
|
for i in range(header.entr):
|
||||||
(step, timer_id, timer_value) = parse_mem_at(
|
timestamp = TimestampEntry(base)
|
||||||
base, TIMESTAMP_ENTRY_FORMAT)
|
print '%d:%s ' % (timestamp.timer_id,
|
||||||
print '%d:%s ' % (timer_id, normalize_timer(timer_value, clock_freq)),
|
normalize_timer(timestamp.timer_value, clock_freq)),
|
||||||
base = base + step
|
base = base + timestamp.struct_len
|
||||||
print
|
print
|
||||||
|
|
||||||
def process_console(base):
|
def process_console(base):
|
||||||
'''Dump the console log buffer contents found at address base.'''
|
'''Dump the console log buffer contents found at address base.'''
|
||||||
|
|
||||||
(step, size, cursor) = parse_mem_at(base, CONSOLE_HEADER_FORMAT)
|
class ConsoleHeader(CStruct):
|
||||||
print 'cursor at %d\n' % cursor
|
struct_members = (
|
||||||
|
("L", "size"),
|
||||||
|
("L", "cursor")
|
||||||
|
)
|
||||||
|
|
||||||
cons_string_format = '%ds' % min(cursor, size)
|
header = ConsoleHeader(base)
|
||||||
(_, cons_text) = parse_mem_at(base + step, cons_string_format)
|
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 cons_text
|
||||||
print '\n'
|
print '\n'
|
||||||
|
|
||||||
mem_alignment = 1024 * 1024 * 1024 # 1 GBytes
|
def ipchksum(buf):
|
||||||
table_alignment = 128 * 1024
|
'''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.'''
|
||||||
|
|
||||||
mem_size = get_mem_size()
|
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
|
||||||
|
|
||||||
# start at memory address aligned at 128K.
|
def parse_tables(base, length):
|
||||||
offset = align_up(mem_size, table_alignment)
|
'''Find the coreboot tables in memory and process whatever we can.'''
|
||||||
|
|
||||||
dprint('mem_size %x offset %x' %(mem_size, offset))
|
class CBTableHeader(CStruct):
|
||||||
mf = open("/dev/mem")
|
struct_members = (
|
||||||
mf_fileno = mf.fileno()
|
("4s", "signature"),
|
||||||
|
("I", "header_bytes"),
|
||||||
|
("I", "header_checksum"),
|
||||||
|
("I", "table_bytes"),
|
||||||
|
("I", "table_checksum"),
|
||||||
|
("I", "table_entries")
|
||||||
|
)
|
||||||
|
|
||||||
while offset % mem_alignment: # do not cross the 1G boundary while searching
|
class CBTableEntry(CStruct):
|
||||||
(step, magic, mid, base, size) = parse_mem_at(offset, CBMEM_ENTRY_FORMAT)
|
struct_members = (
|
||||||
if magic == CBMEM_MAGIC:
|
("I", "tag"),
|
||||||
offset = offset + step
|
("I", "size")
|
||||||
break
|
)
|
||||||
offset += table_alignment
|
|
||||||
else:
|
|
||||||
print 'Did not find the CBMEM'
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
for i in (range(1, CBMEM_MAX_ENTRIES)):
|
class CBTableForward(CBTableEntry):
|
||||||
(_, magic, mid, base, size) = parse_mem_at(offset, CBMEM_ENTRY_FORMAT)
|
struct_members = CBTableEntry.struct_members + (
|
||||||
if mid == 0:
|
("Q", "forward"),
|
||||||
break
|
)
|
||||||
|
|
||||||
print '%x, %x, %x' % (mid, base, size)
|
class CBMemTab(CBTableEntry):
|
||||||
if mid == 0x54494d45:
|
struct_members = CBTableEntry.struct_members + (
|
||||||
process_timers(base)
|
("L", "cbmem_tab"),
|
||||||
if mid == 0x434f4e53:
|
)
|
||||||
process_console(base)
|
|
||||||
|
|
||||||
offset = offset + step
|
for addr in range(base, base + length, 16):
|
||||||
|
header = CBTableHeader(addr)
|
||||||
|
if header.signature == "LBIO":
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
mf.close()
|
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())
|
||||||
|
|
Loading…
Reference in New Issue