Stefan Reinauer (stefan.reinauer@coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/723
-gerrit
commit 930ecd22286a3a8f708e562362cef1e3a81f0200 Author: Vadim Bendebury vbendeb@chromium.org Date: Fri Sep 30 14:21:03 2011 -0700
Introduce utility for parsing CBMEM contents.
This is a python script which is supposed to run on a target which is controlled by coreboot. The script examines top of memory looking for the CBMEM signature at addresses aligned at 128K boundary. Once the script finds the CBMEM, it iterates through the CBMEM table of contents and parses two entries: the timestamps and the console log.
This submission is just a template to build upon to create a utility for displaying CBMEM information while running Linux on the target.
BUG=chrome-os-partner:4200 TEST=manual
See test description of d81e6b8c8d41f2d6 for test procedure.
Change-Id: Id863a8598eaadc2d20d728f9186843e65cbe6f37 Signed-off-by: Vadim Bendebury vbendeb@chromium.org Reviewed-on: https://gerrit-int.chromium.org/5942 Tested-by: Vadim Bendebury vbendeb@google.com Reviewed-by: Stefan Reinauer reinauer@google.com --- util/cbmem/cbmem.py | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 204 insertions(+), 0 deletions(-)
diff --git a/util/cbmem/cbmem.py b/util/cbmem/cbmem.py new file mode 100755 index 0000000..3e8476d --- /dev/null +++ b/util/cbmem/cbmem.py @@ -0,0 +1,204 @@ +#!/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()