-
Notifications
You must be signed in to change notification settings - Fork 164
/
mmio.py
134 lines (108 loc) · 4.37 KB
/
mmio.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
'''
Stripped down version from https://github.com/vsergeev/python-periphery/blob/master/periphery/mmio.py
'''
import mmap
import os
import struct
import sys
# Alias long to int on Python 3
if sys.version_info[0] >= 3:
long = int
class MMIOError(IOError):
"""Base class for MMIO errors."""
pass
class MMIO(object):
def __init__(self, physaddr, size):
"""Instantiate an MMIO object and map the region of physical memory
specified by the address base `physaddr` and size `size` in bytes.
Args:
physaddr (int, long): base physical address of memory region.
size (int, long): size of memory region.
Returns:
MMIO: MMIO object.
Raises:
MMIOError: if an I/O or OS error occurs.
TypeError: if `physaddr` or `size` types are invalid.
"""
self.mapping = None
self._open(physaddr, size)
def __del__(self):
self.close()
def __enter__(self):
pass
def __exit__(self, t, value, traceback):
self.close()
def _open(self, physaddr, size):
if not isinstance(physaddr, (int, long)):
raise TypeError("Invalid physaddr type, should be integer.")
if not isinstance(size, (int, long)):
raise TypeError("Invalid size type, should be integer.")
pagesize = os.sysconf(os.sysconf_names['SC_PAGESIZE'])
self._physaddr = physaddr
self._size = size
self._aligned_physaddr = physaddr - (physaddr % pagesize)
self._aligned_size = size + (physaddr - self._aligned_physaddr)
try:
fd = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
except OSError as e:
raise MMIOError(e.errno, "Opening /dev/mem: " + e.strerror)
try:
self.mapping = mmap.mmap(
fd, self._aligned_size, flags=mmap.MAP_SHARED, prot=mmap.PROT_WRITE, offset=self._aligned_physaddr)
except OSError as e:
raise MMIOError(e.errno, "Mapping /dev/mem: " + e.strerror)
try:
os.close(fd)
except OSError as e:
raise MMIOError(e.errno, "Closing /dev/mem: " + e.strerror)
# Methods
def _adjust_offset(self, offset):
return offset + (self._physaddr - self._aligned_physaddr)
def _validate_offset(self, offset, length):
if (offset + length) > self._aligned_size:
raise ValueError("Offset out of bounds.")
def read32(self, offset):
"""Read 32-bits from the specified `offset` in bytes, relative to the
base physical address of the MMIO region.
Args:
offset (int, long): offset from base physical address, in bytes.
Returns:
int: 32-bit value read.
Raises:
TypeError: if `offset` type is invalid.
ValueError: if `offset` is out of bounds.
"""
if not isinstance(offset, (int, long)):
raise TypeError("Invalid offset type, should be integer.")
offset = self._adjust_offset(offset)
self._validate_offset(offset, 4)
return struct.unpack("=L", self.mapping[offset:offset + 4])[0]
def write32(self, offset, value):
"""Write 32-bits to the specified `offset` in bytes, relative to the
base physical address of the MMIO region.
Args:
offset (int, long): offset from base physical address, in bytes.
value (int, long): 32-bit value to write.
Raises:
TypeError: if `offset` or `value` type are invalid.
ValueError: if `offset` or `value` are out of bounds.
"""
if not isinstance(offset, (int, long)):
raise TypeError("Invalid offset type, should be integer.")
if not isinstance(value, (int, long)):
raise TypeError("Invalid value type, should be integer.")
if value < 0 or value > 0xffffffff:
raise ValueError("Value out of bounds.")
offset = self._adjust_offset(offset)
self._validate_offset(offset, 4)
self.mapping[offset:offset + 4] = struct.pack("=L", value)
def close(self):
"""Unmap the MMIO object's mapped physical memory."""
if self.mapping is None:
return
self.mapping.close()
self.mapping = None
self._fd = None
# String representation
def __str__(self):
return "MMIO 0x%08x (size=%d)" % (self.base, self.size)