205 lines
6.1 KiB
Python
Executable File
205 lines
6.1 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 re
|
|
import struct
|
|
import sys
|
|
import time
|
|
|
|
# These definitions follow src/include/cbmem.h
|
|
CBMEM_MAGIC = 0x434f5245
|
|
CBMEM_MAX_ENTRIES = 16
|
|
|
|
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
|
|
|
|
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 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))
|
|
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
|
|
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
|
|
|
|
cons_string_format = '%ds' % min(cursor, size)
|
|
(_, cons_text) = parse_mem_at(base + step, cons_string_format)
|
|
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()
|