blob: aac53e2a1068a50673f45845a2b9ea9f58992aa6 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Access memory using /dev/mem.
Can be used as a command-line tool or via 'import mem' in python.
"""
import argparse
import code
import inspect
import mmap
import struct
import sys
_BANNER = """Welcome to mem interactive mode (featuring python!)
Available functions:
"""
class HexInt(int):
"""An int that prints out as hex but is still just a number."""
def __repr__(self):
try:
width = getattr(self, 'width')
except AttributeError:
width = 4
return '%#0*x' % (2 * width + 2, self)
def __str__(self):
return repr(self)
def rm(addr, nbytes):
"""Read memory at the given addr as a string.
Args:
addr: The address to read at.
nbytes: The number of bytes to read.
Returns:
The string that was read.
"""
with open('/dev/mem', 'r+b') as mem:
offset = addr & 0xfff
mem_map = mmap.mmap(mem.fileno(), nbytes + offset, offset=addr & ~0xfff)
return mem_map[offset:offset + nbytes]
def wm(addr, val):
"""Write a string to memory at the given addr.
Args:
addr: The address to read at.
val: A string to write.
"""
with open('/dev/mem', 'r+b') as mem:
nbytes = len(val)
offset = addr & 0xfff
mem_map = mmap.mmap(mem.fileno(), nbytes + offset, offset=addr & ~0xfff)
mem_map[offset:offset + nbytes] = val
def r(addr):
"""Read a single 32-bit word (little endian).
Args:
addr: The address to read at.
Returns:
The value that was read; returns as a HexInt (a subclass of long) so that
it by default prints itself in hex. You can use this like any other int.
"""
return HexInt(struct.unpack('<I', rm(addr, 4))[0])
def w(addr, val):
"""Write a single 32-bit word (little endian).
Args:
addr: The address to write to.
val: The value to write.
"""
wm(addr, struct.pack('<I', val))
def _ParseInt(x):
"""Create an int from a string, autodetecting the base."""
return int(x, 0)
# Commands we export and their argument types.
_COMMANDS = {
'r': [_ParseInt],
'w': [_ParseInt, _ParseInt],
'rm': [_ParseInt, _ParseInt],
'wm': [_ParseInt, str],
}
def _ListCommands():
"""Return a list of commands as a string.
Returns:
A string that can be printed to the user.
"""
commands = []
for cmd_name in sorted(_COMMANDS.keys()):
func = eval(cmd_name)
arg_names = inspect.signature(func).parameters
short_doc = func.__doc__.splitlines()[0]
arg_string = ', '.join(arg_names)
commands.append('- %s(%s) # %s' % (cmd_name, arg_string, short_doc))
return '\n'.join(commands)
def _CreateParser():
"""Create an argparse object we can use.
Returns:
An argparse object that can be used to parse our options.
"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
for cmd_name, arg_types in sorted(_COMMANDS.items()):
func = eval(cmd_name)
arg_names = inspect.signature(func).parameters
short_doc = func.__doc__.splitlines()[0]
# Get descriptions out assuming single-line descs.
arg_descs = {}
for line in func.__doc__.splitlines():
line = line.strip()
arg_name = line.split(':')[0]
if arg_name in arg_names:
arg_descs[arg_name] = line.partition(':')[-1]
subparser = subparsers.add_parser(cmd_name, help=short_doc)
for arg_name, arg_type in zip(arg_names, arg_types):
subparser.add_argument(arg_name, type=arg_type,
help=arg_descs[arg_name])
subparser.set_defaults(func=func)
return parser
def main(args=None):
if args is None:
args = sys.argv[1:]
if not args:
# Try to pretend that they ran python -i to just use us interactively.
return code.interact(banner=_BANNER + _ListCommands(), local=globals())
parser = _CreateParser()
try:
# Parse args and get back something we can use to call the function.
namespace = parser.parse_args(args)
func_kwargs = vars(namespace).copy()
func = func_kwargs.pop('func')
result = func(**func_kwargs)
# Print the result; no trailing return if we're not on a tty.
if result is not None:
print(result, end='\n' if sys.stdout.isatty() else '')
except Exception as e:
# For now really simple error handling...
parser.error(str(e))
return 1
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))