import collections import copy import logging from ScEpTIC import tools from ScEpTIC.emulator.memory.memory_access_trace import MemoryAccessTrace from ScEpTIC.exceptions import RuntimeException class VirtualMemoryCell: """ Class that represents a single memory cell. Cells do not have a fixed dimension, but each cell refers to a single element (e.g. variable) """ _vmstate = None def __init__(self, address, address_prefix, dimension, content=None, garbage=False, alignment=None): self.address = address self.address_prefix = address_prefix self.dimension = dimension self.garbage = garbage self.metadata = '' self._init_lookup() self.other_memory_trace_addresses = [] self.alignment = alignment # Init memory cell to zero if no value provided if content is None: self.content = 0 else: self.content = content logging.debug('created {}'.format(repr(self))) def get_bit_size(self): return self.dimension * 8 def get_content(self): return self.content def set_content(self, content): self.content = content def _init_lookup(self): """ Inits/resets the lookup table of the memory cell. """ self.lookup = {'old_content': None, 'write_pc': None, 'write_global_clock': -1, 'memory_mapped': -1, 'memory_mapped_pc': None, 'input': tools.build_input_lookup_data(None, None)} def remap(self, dimension): """ Remaps the dimension of the cell and invalidates the cell content. """ logging.debug('Remapping cell at address {} with dimension {} bytes.'.format(hex(self.address), dimension)) self.dimension = dimension self._init_lookup() self.content = None def free(self): """ Frees the cell like it is done with C function "free" """ logging.debug('Memory cell at address {} freed.'.format(hex(self.address))) self._init_lookup() self.content = None self.garbage = True def set_lookup(self, old_content, current_pc, current_clock, memory_mapped=False): """ Sets lookup informations for memory anomaly checks. """ if not self._vmstate.do_data_anomaly_check: return self.lookup['old_content'] = old_content self.lookup['write_pc'] = copy.deepcopy(current_pc) self.lookup['write_global_clock'] = current_clock if memory_mapped: self.set_memory_mapped(current_pc, current_clock, self.address, self.dimension) def set_input_lookup(self, input_lookup_data): """ Sets the input lookup information of the cell. """ self.lookup['input'] = input_lookup_data def get_input_lookup(self): """ Returns the input lookup information of the cell. """ return self.lookup['input'] def set_memory_mapped(self, current_pc, current_clock, old_memory_address, old_dimension): """ Sets lookup informations for memory anomaly checks. """ self.lookup['memory_mapped_pc'] = copy.deepcopy(current_pc) self.lookup['memory_mapped'] = current_clock self.lookup['old_memory_address'] = old_memory_address self.lookup['old_dimension'] = old_dimension @property def absolute_address(self): """ Returns the absolute address of the cell """ return "{}{}".format(self.address_prefix, hex(self.address)) def __str__(self): return '[{}{} - {}{} ({} bytes)] {}{}'.format(self.address_prefix, hex(self.address), self.address_prefix, hex(self.address + self.dimension - 1), self.dimension, self.content, ' {GARBAGE}' if self.garbage else '') def __repr__(self): return 'VirtualMemoryCell({}{}, {}, {}, {}, {})'.format(self.address_prefix, hex(self.address), self.dimension, self.content, self.garbage, self.alignment) def __eq__(self, other): if not isinstance(other, VirtualMemoryCell): return False return self.address == other.address and self.address_prefix == other.address_prefix and self.dimension == other.dimension and self.content == other.content and self.garbage == other.garbage and self.lookup == other.lookup def collect_memory_trace(self, op_type): # If ScEpTIC not set to collect memory traces, skip if not self._vmstate.collect_memory_trace: return # If memory cell not in memory_trace_prefixes (i.e. not in NVM) if self.address_prefix not in self._vmstate.memory_trace_prefixes: return if op_type not in ['read', 'write', 'allocation', 'deallocation']: raise RuntimeException('Wrong operation type for memory trace!') # Create memory trace addr = self.absolute_address clock = self._vmstate.global_clock # Adjust for deallocation dimension = self.dimension if op_type != 'deallocation' else None traced_address = addr if op_type != 'deallocation' else None if traced_address is None: self.metadata = 'Memory address {}'.format(addr) memory_trace = MemoryAccessTrace(clock, self._vmstate.register_file.pc, traced_address, self.content, dimension, op_type, self.metadata) addresses = [addr] # on write propagate also write operations that exceeds original cell boundaries if op_type == 'write': addresses = addresses + self.other_memory_trace_addresses for addr in addresses: # Create data structure, if needed if addr not in self._vmstate.memory_trace: self._vmstate.memory_trace[addr] = {'read': collections.OrderedDict(), 'write': collections.OrderedDict(), 'allocation': collections.OrderedDict(), 'deallocation': collections.OrderedDict()} # Add memory trace to collected memory traces self._vmstate.memory_trace[addr][op_type][clock] = memory_trace