|
@@ -0,0 +1,120 @@
|
|
|
+'''
|
|
|
+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 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)
|