3 # cbmem.py - Linux space CBMEM contents parser
5 # Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; version 2 of the License
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 Parse and display CBMEM contents.
23 This module is meant to run on systems with coreboot based firmware.
25 When started, it determines the amount of DRAM installed on the system, and
26 then scans the top area of DRAM (right above the available memory size)
27 looking for the CBMEM base signature at locations aligned at 0x20000
30 Once it finds the CBMEM signature, the utility parses the contents, reporting
31 the section IDs/sizes and also reporting the contents of the tiemstamp and
41 # These definitions follow src/include/cbmem.h
42 CBMEM_MAGIC = 0x434f5245
43 CBMEM_MAX_ENTRIES = 16
45 CBMEM_ENTRY_FORMAT = '@LLQQ'
46 CONSOLE_HEADER_FORMAT = '@LL'
47 TIMESTAMP_HEADER_FORMAT = '@QLL'
48 TIMESTAMP_ENTRY_FORMAT = '@LQ'
50 mf_fileno = 0 # File number of the file providing access to memory.
52 def align_up(base, alignment):
53 '''Increment to the alignment boundary.
55 Return the next integer larger than 'base' and divisible by 'alignment'.
58 return base + alignment - base % alignment
60 def normalize_timer(value, freq):
61 '''Convert timer reading into microseconds.
63 Get the free running clock counter value, divide it by the clock frequency
64 and multiply by 1 million to get reading in microseconds.
66 Then convert the value into an ASCII string with groups of three digits
70 value: int, the clock reading
71 freq: float, the clock frequency
74 A string presenting 'value' in microseconds.
78 value = int(value * 1000000.0 / freq)
81 remainder = vlength % 3
83 result.append(svalue[0:remainder])
84 while remainder < vlength:
85 result.append(svalue[remainder:remainder+3])
86 remainder = remainder + 3
87 return ','.join(result)
90 '''Retrieve CPU frequency from sysfs.
92 Use /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq as the source.
94 freq_str = open('/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq'
96 # Convert reading into Hertz
97 return float(freq_str) * 1000.0
100 '''Retrieve amount of memory available to the CPU from /proc/meminfo.'''
104 meminfo = open('/proc/meminfo').read()
105 m = re.search('MemTotal:.*\n', meminfo)
106 mem_string = re.search('MemTotal:.*\n', meminfo).group(0)
107 (_, size, mult_name) = mem_string.split()
108 return int(size) * mult[mult_name]
110 def parse_mem_at(addr, format):
111 '''Read and parse a memory location.
113 This function reads memory at the passed in address, parses it according
114 to the passed in format specification and returns a list of values.
116 The first value in the list is the size of data matching the format
117 expression, and the rest of the elements of the list are the actual values
118 retrieved using the format.
121 size = struct.calcsize(format)
122 delta = addr % 4096 # mmap requires the offset to be page size aligned.
123 mm = mmap.mmap(mf_fileno, size + delta,
124 mmap.MAP_PRIVATE, offset=(addr - delta))
125 buf = mm.read(size + delta)
127 rv = [size,] + list(struct.unpack(format, buf[delta:size + delta + 1]))
131 '''Debug print function.
133 Edit it to get the debug output.
139 def process_timers(base):
140 '''Scan the array of timestamps found in CBMEM at address base.
142 For each timestamp print the timer ID and the value in microseconds.
145 (step, base_time, max_entr, entr) = parse_mem_at(
146 base, TIMESTAMP_HEADER_FORMAT)
148 print('\ntime base %d, total entries %d' % (base_time, entr))
149 clock_freq = get_cpu_freq()
151 for i in range(entr):
152 (step, timer_id, timer_value) = parse_mem_at(
153 base, TIMESTAMP_ENTRY_FORMAT)
154 print '%d:%s ' % (timer_id, normalize_timer(timer_value, clock_freq)),
158 def process_console(base):
159 '''Dump the console log buffer contents found at address base.'''
161 (step, size, cursor) = parse_mem_at(base, CONSOLE_HEADER_FORMAT)
162 print 'cursor at %d\n' % cursor
164 cons_string_format = '%ds' % min(cursor, size)
165 (_, cons_text) = parse_mem_at(base + step, cons_string_format)
169 mem_alignment = 1024 * 1024 * 1024 # 1 GBytes
170 table_alignment = 128 * 1024
172 mem_size = get_mem_size()
174 # start at memory address aligned at 128K.
175 offset = align_up(mem_size, table_alignment)
177 dprint('mem_size %x offset %x' %(mem_size, offset))
178 mf = open("/dev/mem")
179 mf_fileno = mf.fileno()
181 while offset % mem_alignment: # do not cross the 1G boundary while searching
182 (step, magic, mid, base, size) = parse_mem_at(offset, CBMEM_ENTRY_FORMAT)
183 if magic == CBMEM_MAGIC:
184 offset = offset + step
186 offset += table_alignment
188 print 'Did not find the CBMEM'
191 for i in (range(1, CBMEM_MAX_ENTRIES)):
192 (_, magic, mid, base, size) = parse_mem_at(offset, CBMEM_ENTRY_FORMAT)
196 print '%x, %x, %x' % (mid, base, size)
197 if mid == 0x54494d45:
199 if mid == 0x434f4e53:
200 process_console(base)
202 offset = offset + step