forked from kylemanna/pydevmem
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdevmem.py
executable file
·276 lines (208 loc) · 8.8 KB
/
devmem.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#!/usr/bin/env python
"""
This is designed primarily for use with accessing /dev/mem on OMAP platforms.
It should work on other platforms and work to mmap() files rather then just
/dev/mem, but these use cases aren't well tested.
All file accesses are aligned to DevMem.word bytes, which is 4 bytes on ARM
platforms to avoid data abort faults when accessing peripheral registers.
References:
http://wiki.python.org/moin/PythonSpeed/PerformanceTips
http://www.python.org/dev/peps/pep-0008/
"""
import os
import sys
import mmap
import struct
import optparse
""" DevMemBuffer
This class holds data for objects returned from DevMem class
It allows an easy way to print hex data
"""
class DevMemBuffer:
def __init__(self, base_addr, data):
self.data = data
self.base_addr = base_addr
def __len__(self):
return len(self.data)
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, value):
self.data[key] = value
def hexdump(self, word_size = 4, words_per_row = 4):
# Build a list of strings and then join them in the last step.
# This is more efficient then concat'ing immutable strings.
d = self.data
dump = []
word = 0
while (word < len(d)):
dump.append('0x{0:02x}: '.format(self.base_addr
+ word_size * word))
max_col = word + words_per_row
if max_col > len(d): max_col = len(d)
while (word < max_col):
# If the word is 4 bytes, then handle it and continue the
# loop, this should be the normal case
if word_size == 4:
dump.append(" {0:08x} ".format(d[word]))
word += 1
continue
# Otherwise the word_size is not an int, pack it so it can be
# un-packed to the desired word size. This should blindly
# handle endian problems (Verify?)
packed = struct.pack('I',(d[word]))
if word_size == 2:
dh = struct.unpack('HH', packed)
dump.append(" {0:04x}".format(dh[0]))
word += 1
elif word_size == 1:
db = struct.unpack('BBBB', packed)
dump.append(" {0:02x}".format(db[0]))
word += 1
dump.append('\n')
# Chop off the last new line character and join the list of strings
# in to a single string
return ''.join(dump[:-1])
def __str__(self):
return self.hexdump()
""" DevMem
Class to read and write data aligned to word boundaries of /dev/mem
"""
class DevMem:
# Size of a word that will be used for reading/writing
word = 4
mask = ~(word - 1)
def __init__(self, base_addr, length = 1, filename = '/dev/mem',
debug = 0):
if base_addr < 0 or length < 0: raise AssertionError
self._debug = debug
self.base_addr = base_addr & ~(mmap.PAGESIZE - 1)
self.base_addr_offset = base_addr - self.base_addr
stop = base_addr + length * self.word
if (stop % self.mask):
stop = (stop + self.word) & ~(self.word - 1)
self.length = stop - self.base_addr
self.fname = filename
# Check filesize (doesn't work with /dev/mem)
#filesize = os.stat(self.fname).st_size
#if (self.base_addr + self.length) > filesize:
# self.length = filesize - self.base_addr
self.debug('init with base_addr = {0} and length = {1} on {2}'.
format(hex(self.base_addr), hex(self.length), self.fname))
# Open file and mmap
f = os.open(self.fname, os.O_RDWR | os.O_SYNC)
self.mem = mmap.mmap(f, self.length, mmap.MAP_SHARED,
mmap.PROT_READ | mmap.PROT_WRITE,
offset=self.base_addr)
"""
Read length number of words from offset
"""
def read(self, offset, length):
if offset < 0 or length < 0: raise AssertionError
# Make reading easier (and faster... won't resolve dot in loops)
mem = self.mem
self.debug('reading {0} bytes from offset {1}'.
format(length * self.word, hex(offset)))
# Compensate for the base_address not being what the user requested
# and then seek to the aligned offset.
virt_base_addr = self.base_addr_offset & self.mask
mem.seek(virt_base_addr + offset)
# Read length words of size self.word and return it
data = []
for i in range(length):
data.append(struct.unpack('I', mem.read(self.word))[0])
abs_addr = self.base_addr + virt_base_addr
return DevMemBuffer(abs_addr + offset, data)
"""
Write length number of words to offset
"""
def write(self, offset, din):
if offset < 0 or len(din) <= 0: raise AssertionError
self.debug('writing {0} bytes to offset {1}'.
format(len(din), hex(offset)))
# Make reading easier (and faster... won't resolve dot in loops)
mem = self.mem
# Compensate for the base_address not being what the user requested
offset += self.base_addr_offset
# Check that the operation is going write to an aligned location
if (offset & ~self.mask): raise AssertionError
# Seek to the aligned offset
virt_base_addr = self.base_addr_offset & self.mask
mem.seek(virt_base_addr + offset)
# Read until the end of our aligned address
for i in range(0, len(din), self.word):
self.debug('writing at position = {0}: 0x{1:x}'.
format(self.mem.tell(), din[i]))
# Write one word at a time
mem.write(struct.pack('I', din[i]))
def debug_set(self, value):
self._debug = value
def debug(self, debug_str):
if self._debug: print 'DevMem Debug: {0}'.format(debug_str)
""" Main
If this is run as a script (rather then imported as a module) it provides
some basic functionality out of the box
"""
def main():
parser = optparse.OptionParser()
parser.add_option("-r", "--read", dest="read", metavar="ADDR",
type=int, help="read a value")
parser.add_option("-w", "--write", dest="write", help="write a value",
nargs=2, type=int, metavar="ADDR VALUE")
parser.add_option("-n", "--num", dest="num",
help="number of words to read",
type=int, default=1)
parser.add_option("-s", "--word-size", dest="word_size",
help="size of word when displayed",
type=int, default=4)
parser.add_option("-m", "--mmap", dest="mmap",
metavar="FILE",
help="file to open with mmap()",
type=str, default="/dev/mem")
parser.add_option("-v", action="store_true", dest="verbose",
help="provide more information regarding operation")
parser.add_option("-d", action="store_true", dest="debug",
help="provide debugging information")
(options, args) = parser.parse_args()
# Check for sane arguments
if options.write is not None and options.read is not None:
parser.print_help()
print "\nError: Both read and write are specified"
return -1
elif options.write is None and options.read is None:
parser.print_help()
print "\nError: Neither read or write are specified"
return -1
if options.num < 0:
parser.print_help()
print "\nError: Invalid num of words specified"
return -1
if (options.word_size != 1 and options.word_size != 2
and options.word_size != 4):
parser.print_help()
print "\nError: Invalid word size specified"
return -1
# Only support writing one word at a time, force this
if options.write is not None and options.num != 1:
print "Warning: Forcing number of words to 1 for set operation\n"
options.num = 1
# Determine base address to operate on
addr = options.read
if options.write is not None: addr = options.write[0]
# Create the Dev Mem object that does the magic
mem = DevMem(addr, length=options.num, filename=options.mmap,
debug=options.debug)
if options.debug:
mem.debug_set(1)
# Perform the actual read or write
if options.write is not None:
if options.verbose:
print "Value before write:\t{0}".format(
mem.read(0x0, options.num).hexdump(options.word_size))
mem.write(0x0, [options.write[1]])
if options.verbose:
print "Value after write:\t{0}".format(
mem.read(0x0, options.num).hexdump(options.word_size))
else:
print mem.read(0x0, options.num).hexdump(options.word_size)
if __name__ == '__main__':
sys.exit(main())