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
39 def get_phys_mem(addr, size):
40 '''Read size bytes from address addr by mmaping /dev/mem'''
44 mm = mmap.mmap(mf.fileno(), size + delta,
45 mmap.MAP_PRIVATE, offset=(addr - delta))
46 buf = mm.read(size + delta)
50 # This class and metaclass make it easier to define and access structures
51 # which live in physical memory. To use them, inherit from CStruct and define
52 # a class member called struct_members which is a tuple of pairs. The first
53 # item in the pair is the type format specifier that should be used with
54 # struct.unpack to read that member from memory. The second item is the name
55 # that member should have in the resulting object.
57 class MetaCStruct(type):
58 def __init__(cls, name, bases, dct):
59 struct_members = dct["struct_members"]
61 for char, name in struct_members:
62 cls.struct_fmt += char
63 cls.struct_len = struct.calcsize(cls.struct_fmt)
64 super(MetaCStruct, cls).__init__(name, bases, dct)
66 class CStruct(object):
67 __metaclass__ = MetaCStruct
70 def __init__(self, addr):
71 self.raw_memory = get_phys_mem(addr, self.struct_len)
72 values = struct.unpack(self.struct_fmt, self.raw_memory)
73 names = (name for char, name in self.struct_members)
74 for name, value in zip(names, values):
75 setattr(self, name, value)
77 def normalize_timer(value, freq):
78 '''Convert timer reading into microseconds.
80 Get the free running clock counter value, divide it by the clock frequency
81 and multiply by 1 million to get reading in microseconds.
83 Then convert the value into an ASCII string with groups of three digits
87 value: int, the clock reading
88 freq: float, the clock frequency
91 A string presenting 'value' in microseconds.
95 value = int(value * 1000000.0 / freq)
98 remainder = vlength % 3
100 result.append(svalue[0:remainder])
101 while remainder < vlength:
102 result.append(svalue[remainder:remainder+3])
103 remainder = remainder + 3
104 return ','.join(result)
107 '''Retrieve CPU frequency from sysfs.
109 Use /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq as the source.
111 freq_str = open('/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq'
113 # Convert reading into Hertz
114 return float(freq_str) * 1000.0
116 def process_timers(base):
117 '''Scan the array of timestamps found in CBMEM at address base.
119 For each timestamp print the timer ID and the value in microseconds.
122 class TimestampHeader(CStruct):
129 class TimestampEntry(CStruct):
135 header = TimestampHeader(base)
136 print('\ntime base %d, total entries %d' % (header.base_time, header.entr))
137 clock_freq = get_cpu_freq()
138 base = base + header.struct_len
139 for i in range(header.entr):
140 timestamp = TimestampEntry(base)
141 print '%d:%s ' % (timestamp.timer_id,
142 normalize_timer(timestamp.timer_value, clock_freq)),
143 base = base + timestamp.struct_len
146 def process_console(base):
147 '''Dump the console log buffer contents found at address base.'''
149 class ConsoleHeader(CStruct):
155 header = ConsoleHeader(base)
156 print 'cursor at %d\n' % header.cursor
158 cons_addr = base + header.struct_len
159 cons_length = min(header.cursor, header.size)
160 cons_text = get_phys_mem(cons_addr, cons_length)
165 '''Checksumming function used on the coreboot tables. The buffer being
166 checksummed is summed up as if it was an array of 16 bit unsigned
167 integers. If there are an odd number of bytes, the last element is zero
172 fmt = "<%dH" % ((size - odd) / 2)
175 shorts = struct.unpack(fmt, buf)
176 checksum = sum(shorts)
177 checksum = (checksum >> 16) + (checksum & 0xffff)
178 checksum += (checksum >> 16)
179 checksum = ~checksum & 0xffff
182 def parse_tables(base, length):
183 '''Find the coreboot tables in memory and process whatever we can.'''
185 class CBTableHeader(CStruct):
188 ("I", "header_bytes"),
189 ("I", "header_checksum"),
190 ("I", "table_bytes"),
191 ("I", "table_checksum"),
192 ("I", "table_entries")
195 class CBTableEntry(CStruct):
201 class CBTableForward(CBTableEntry):
202 struct_members = CBTableEntry.struct_members + (
206 class CBMemTab(CBTableEntry):
207 struct_members = CBTableEntry.struct_members + (
211 for addr in range(base, base + length, 16):
212 header = CBTableHeader(addr)
213 if header.signature == "LBIO":
218 if header.header_bytes == 0:
221 if ipchksum(header.raw_memory) != 0:
222 print "Bad header checksum"
225 addr += header.header_bytes
226 table = get_phys_mem(addr, header.table_bytes)
227 if ipchksum(table) != header.table_checksum:
228 print "Bad table checksum"
231 for i in range(header.table_entries):
232 entry = CBTableEntry(addr)
233 if entry.tag == 0x11: # Forwarding entry
234 return parse_tables(CBTableForward(addr).forward, length)
235 elif entry.tag == 0x16: # Timestamps
236 process_timers(CBMemTab(addr).cbmem_tab)
237 elif entry.tag == 0x17: # CBMEM console
238 process_console(CBMemTab(addr).cbmem_tab)
245 for base, length in (0x00000000, 0x1000), (0x000f0000, 0x1000):
246 if parse_tables(base, length):
249 print "Didn't find the coreboot tables"
252 if __name__ == "__main__":