# Copyright 2011-2021 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""":mod:`microprobe.target.env` package
"""
# Futures
from __future__ import absolute_import, annotations
# Built-in modules
import abc
from typing import TYPE_CHECKING, List
# Third party modules
# Own modules
from microprobe import MICROPROBE_RC
from microprobe.code.address import Address
from microprobe.code.context import Context
from microprobe.code.var import VariableArray
from microprobe.exceptions import MicroprobeImportDefinitionError, \
MicroprobeValueError
from microprobe.property import Property, PropertyHolder
from microprobe.utils.imp import find_subclasses
from microprobe.utils.logger import get_logger
from microprobe.utils.misc import findfiles
from microprobe.utils.typeguard_decorator import typeguard_testsuite
# Local modules
# Type hinting
if TYPE_CHECKING:
from microprobe.target.isa import ISA
from microprobe.code.ins import Instruction
from microprobe.target import Target
from microprobe.target import Definition
from microprobe.target.isa.register import Register
# Constants
LOG = get_logger(__name__)
_INIT = True
_ENV_DEFINITIONS = None
__all__ = [
"import_env_definition", "find_env_definitions", "Environment",
"GenericEnvironment"
]
# Functions
[docs]
@typeguard_testsuite
def import_env_definition(module,
isa: ISA,
definition_name: str | None = None):
"""
:param module:
:param isa:
:param definition_name: (Default value = None)
"""
LOG.info("Start Environment import")
envcls = list(
find_subclasses(module,
GenericEnvironment,
extra_import_name=definition_name))
LOG.debug("Definition name: %s", definition_name)
LOG.debug("Classes: %s", envcls)
if definition_name is not None:
envcls = [cls for cls in envcls if cls.__name__ == definition_name]
if len(envcls) > 1 and definition_name is None:
LOG.warning("Multiple environment definitions found and a specific"
" name not provided. Taking the first one.")
elif len(envcls) < 1 and definition_name is None:
raise MicroprobeImportDefinitionError(
"No environment definitions found in '%s'" % module)
elif len(envcls) < 1:
raise MicroprobeImportDefinitionError(
"No environment definitions found in '%s' with name"
" '%s'" % (module, definition_name))
environment = envcls[0](isa)
LOG.info("Environment '%s' imported", environment)
return environment
[docs]
@typeguard_testsuite
def find_env_definitions(paths: List[str] | None = None):
LOG.debug("Start find environment definitions")
global _INIT # pylint: disable=global-statement
global _ENV_DEFINITIONS # pylint: disable=global-statement
if not _INIT:
return _ENV_DEFINITIONS
_INIT = False
if paths is None:
paths = []
paths = paths + MICROPROBE_RC["environment_paths"] \
+ MICROPROBE_RC["default_paths"]
results: List["Definition"] = []
files = findfiles(paths, "env/.*.py$", full=True)
if len(files) > 0:
from microprobe.target import Definition
LOG.debug("Files found")
for modfile in files:
LOG.debug("Processing file: '%s'", modfile)
try:
envclses = list(find_subclasses(modfile, GenericEnvironment))
except (MicroprobeValueError, TypeError) as exc:
continue
LOG.debug("Classes find: '%s'", envclses)
for envcls in envclses:
LOG.debug("Trying class: '%s'", envcls)
try:
env = envcls(None)
definition = Definition(modfile, env.name, env.description)
if definition not in results:
results.append(definition)
except TypeError as exc:
# Skip not complete environments
LOG.debug("Skipping class '%s'...", envcls)
LOG.debug(exc)
continue
LOG.debug("End find environment definitions")
_ENV_DEFINITIONS = results
return results
# Classes
[docs]
@typeguard_testsuite
class Environment(PropertyHolder, metaclass=abc.ABCMeta):
""" """
[docs]
@abc.abstractmethod
def __init__(self):
pass
@property
@abc.abstractmethod
def name(self):
raise NotImplementedError
@property
@abc.abstractmethod
def description(self):
raise NotImplementedError
@property
@abc.abstractmethod
def isa(self):
raise NotImplementedError
@property
@abc.abstractmethod
def environment_reserved_registers(self):
raise NotImplementedError
@property
@abc.abstractmethod
def threads(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def set_threads(self, num_threads):
"""
:param num_threads:
"""
raise NotImplementedError
@abc.abstractmethod
def __str__(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def register_name(self, register):
"""
:param register:
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def full_report(self):
raise NotImplementedError
@property
@abc.abstractmethod
def default_wrapper(self):
raise NotImplementedError
@property
@abc.abstractmethod
def stack_pointer(self):
raise NotImplementedError
@property
@abc.abstractmethod
def stack_direction(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def elf_abi(self, stack_size, start_symbol):
raise NotImplementedError
[docs]
@abc.abstractmethod
def function_call(self, target, return_address_reg=None, long_jump=False):
raise NotImplementedError
[docs]
@abc.abstractmethod
def function_return(self, return_address_reg=None):
raise NotImplementedError
@property
@abc.abstractmethod
def volatile_registers(self):
raise NotImplementedError
@property
@abc.abstractmethod
def little_endian(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def set_target(self, target: Target):
"""
:param target:
"""
raise NotImplementedError
@property
@abc.abstractmethod
def target(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def hook_before_test_instructions(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def hook_after_test_instructions(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def hook_test_init_instructions(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def hook_after_reset_instructions(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def hook_test_end_instructions(self):
raise NotImplementedError
[docs]
@typeguard_testsuite
class GenericEnvironment(Environment):
""" """
_cmp_attributes = ["name", "descr"]
[docs]
def __init__(self,
name: str,
descr: str,
isa: ISA,
little_endian: bool = False):
"""
:param name:
:param descr:
:param isa:
"""
super(GenericEnvironment, self).__init__()
self._name = name
self._description = descr
self._isa = isa
self._reserved_registers: List[Register] = []
self._threads = 1
self._target: Target | None = None
self._default_wrapper = None
self._little_endian = little_endian
self.register_property(
Property(
"problem_state", "Boolean indicating if the program"
" is executed in the problem state"
" (not privilege level)", True))
@property
def name(self):
return self._name
@property
def description(self):
return self._description
@property
def isa(self):
return self._isa
@property
def environment_reserved_registers(self):
return self._reserved_registers
@property
def threads(self):
return self._threads
@property
def target(self):
return self._target
[docs]
def set_target(self, target: Target):
"""
:param target:
"""
self._target = target
[docs]
def set_threads(self, num_threads: int):
"""
:param num_threads:
"""
self._threads = num_threads
def __str__(self):
return "Environment '%s': %s" % (self.name, self.description)
[docs]
def register_name(self, register: str):
"""
:param register:
"""
raise NotImplementedError(
"Register name translation requested but not implemented. Check"
" if you are targeting the appropriate environment (%s)" %
register)
[docs]
def full_report(self):
return str(self)
@property
def default_wrapper(self):
return self._default_wrapper
[docs]
def elf_abi(self,
stack_size: int,
start_symbol,
stack_name: str = "microprobe stack",
stack_alignment: int = 16,
stack_address: Address | None = None,
**kwargs):
stack = VariableArray(stack_name,
"uint8_t",
stack_size,
align=stack_alignment,
address=stack_address)
instructions: List[Instruction] = []
instructions += self.target.set_register_to_address(
self.stack_pointer, Address(base_address=stack_name), Context())
if self.stack_direction == "decrease":
instructions += self.target.add_to_register(
self.stack_pointer, stack_size)
if start_symbol is not None:
instructions += self.target.function_call(start_symbol)
instructions += self.target.function_call("ELF_ABI_EXIT")
instructions[0].set_label("ELF_ABI_START")
return [stack], instructions
[docs]
def function_call(self,
target: Target,
return_address_reg: Register | None = None,
long_jump: bool = False):
raise NotImplementedError
[docs]
def function_return(self, return_address_reg: Register | None = None):
raise NotImplementedError
@property
def volatile_registers(self):
raise NotImplementedError
@property
def stack_pointer(self):
raise NotImplementedError
@property
def stack_direction(self):
raise NotImplementedError
@property
def little_endian(self):
return self._little_endian
def _check_cmp(self, other):
if not isinstance(other, self.__class__):
raise NotImplementedError("%s != %s" %
(other.__class__, self.__class__))
def __eq__(self, other):
"""x.__eq__(y) <==> x==y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if not getattr(self, attr) == getattr(other, attr):
return False
return True
def __ne__(self, other):
"""x.__ne__(y) <==> x!=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if not getattr(self, attr) == getattr(other, attr):
return True
return False
def __lt__(self, other):
"""x.__lt__(y) <==> x<y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) < getattr(other, attr):
return True
elif getattr(self, attr) > getattr(other, attr):
return False
return False
def __gt__(self, other):
"""x.__gt__(y) <==> x>y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) > getattr(other, attr):
return True
elif getattr(self, attr) < getattr(other, attr):
return False
return False
def __le__(self, other):
"""x.__le__(y) <==> x<=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) <= getattr(other, attr):
continue
else:
return False
return True
def __ge__(self, other):
"""x.__ge__(y) <==> x>=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) >= getattr(other, attr):
continue
else:
return False
return True
[docs]
def hook_before_test_instructions(self):
return []
[docs]
def hook_after_test_instructions(self):
return [self._target.nop()]
[docs]
def hook_test_init_instructions(self):
return []
[docs]
def hook_after_reset_instructions(self):
return []
[docs]
def hook_test_end_instructions(self):
return []