diff --git a/riscemu/priv/ImageLoader.py b/riscemu/priv/ImageLoader.py index b51bb3e..50cc25e 100644 --- a/riscemu/priv/ImageLoader.py +++ b/riscemu/priv/ImageLoader.py @@ -2,7 +2,7 @@ Laods a memory image with debug information into memory """ -from ..MMU import MMU +from .PrivMMU import PrivMMU from ..Config import RunConfig from ..Executable import Executable, LoadedExecutable, LoadedMemorySection, LoadedInstruction, MemoryFlags from .ElfLoader import ElfInstruction, ElfLoadedMemorySection, InstructionAccessFault, InstructionAddressMisalignedTrap @@ -10,6 +10,7 @@ from ..decoder import decode from ..IO.IOModule import IOModule from .privmodes import PrivModes from ..colors import FMT_ERROR, FMT_NONE +import json from functools import lru_cache from typing import Dict, List, Tuple, Optional, TYPE_CHECKING @@ -18,26 +19,37 @@ if TYPE_CHECKING: from .PrivCPU import PrivCPU -class ContinuousMMU(MMU): +class MemoryImageMMU(PrivMMU): io: List[IOModule] data: bytearray io_start: int - debug_info: Dict[str, Dict[str, str]] + debug_info: Dict[str, Dict[str, Dict[str, str]]] + + def __init__(self, file_name: str, io_start: int = 0xFF0000): + super(MemoryImageMMU, self).__init__(conf=RunConfig()) + + with open(file_name, 'rb') as memf: + data = memf.read() + with open(file_name + '.dbg', 'r') as dbgf: + debug_info: Dict = json.load(dbgf) - def __init__(self, data: bytes, debug_info: Dict, cpu, io_start: int = 0xFF0000): - super(ContinuousMMU, self).__init__(conf=RunConfig()) - self.cpu: 'PrivCPU' = cpu self.data = bytearray(data) + # TODO: super wasteful memory allocation happening here if len(data) < io_start: self.data += bytearray(io_start - len(data)) self.debug_info = debug_info self.io_start = io_start self.io = list() - self.kernel_end = 0 - for start, name in debug_info['sections'].items(): - if name.startswith('programs'): - self.kernel_end = int(start) - break + + def get_entrypoint(self): + try: + start = self.debug_info['symbols']['kernel'].get('_start', None) + if start is not None: + return start + return self.debug_info['symbols']['kernel'].get('_ftext') + except KeyError: + print(FMT_ERROR + '[MMU] cannot find kernel entry in debug information! Falling back to 0x100' + FMT_NONE) + return 0x100 @lru_cache(maxsize=2048) def read_ins(self, addr: int) -> ElfInstruction: @@ -66,13 +78,6 @@ class ContinuousMMU(MMU): print(FMT_ERROR + "[MMU] possible null dereference (write {:x}) from (pc={:x},sec={},rel={:x})".format( addr, pc, text_sec.owner + ':' + text_sec.name, pc - text_sec.base ) + FMT_NONE) - if addr < self.kernel_end: - if self.cpu.mode != PrivModes.MACHINE: - pc = self.cpu.pc - text_sec = self.get_sec_containing(pc) - print(FMT_ERROR + "[MMU] kernel access to {:x} from outside kernel mode! (pc={:x},sec={},rel={:x})".format( - addr, pc, text_sec.owner + ':' + text_sec.name, pc - text_sec.base - ) + FMT_NONE) if addr >= self.io_start: return self.io_at(addr).write(addr, data, size) @@ -88,7 +93,7 @@ class ContinuousMMU(MMU): self.io.append(io) def __repr__(self): - return "ImageMMU()" + return "MemoryImageMMU()" @lru_cache(maxsize=32) def get_sec_containing(self, addr: int) -> Optional[LoadedMemorySection]: diff --git a/riscemu/priv/PrivCPU.py b/riscemu/priv/PrivCPU.py index aec1cd6..1b92e1c 100644 --- a/riscemu/priv/PrivCPU.py +++ b/riscemu/priv/PrivCPU.py @@ -8,7 +8,7 @@ import time from riscemu.CPU import * from .CSR import CSR from .ElfLoader import ElfExecutable -from .ImageLoader import ContinuousMMU +from .ImageLoader import MemoryImageMMU from .Exceptions import * from .PrivMMU import PrivMMU from ..IO import TextIO @@ -47,22 +47,19 @@ class PrivCPU(CPU): the equivalent of "1 byte" (this is actually impossible) """ - def __init__(self, conf): + def __init__(self, conf, mmu: PrivMMU): super().__init__(conf, [PrivRV32I, RV32M]) self.mode: PrivModes = PrivModes.MACHINE - with open('mem.img', 'rb') as memf: - data = memf.read() - with open('mem.img.dbg', 'r') as dbgf: - debug_info = json.load(dbgf) + mmu.set_cpu(self) + self.pc = mmu.get_entrypoint() + self.mmu = mmu - self.mmu = ContinuousMMU(data, debug_info, self) - self.pc = 0x100 - self.mmu.add_io(TextIO.TextIO(0xff0000, 64)) - self.syscall_int = None + if hasattr(self.mmu, 'add_io'): + self.mmu.add_io(TextIO.TextIO(0xff0000, 64)) + self.syscall_int = None self.launch_debug = False - self.pending_traps: List[CpuTrap] = list() self._time_start = 0 @@ -87,7 +84,7 @@ class PrivCPU(CPU): self.launch_debug = True self.pc += self.INS_XLEN else: - print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc - 1, ins) + FMT_NONE) + print(FMT_ERROR + "[CPU] exception caught at 0x{:08X}: {}:".format(self.pc - 1, ins) + FMT_NONE) print(ex.message()) if self.conf.debug_on_exception: self.launch_debug = True diff --git a/riscemu/priv/PrivMMU.py b/riscemu/priv/PrivMMU.py index 0e8bb40..39179f9 100644 --- a/riscemu/priv/PrivMMU.py +++ b/riscemu/priv/PrivMMU.py @@ -1,13 +1,29 @@ from ..MMU import * +from abc import abstractmethod import typing from .ElfLoader import ElfExecutable +if typing.TYPE_CHECKING: + from .PrivCPU import PrivCPU + class PrivMMU(MMU): + cpu: 'PrivCPU' + + @abstractmethod + def get_entrypoint(self) -> int: + raise + + def set_cpu(self, cpu: 'PrivCPU'): + self.cpu = cpu + + +class LoadedElfMMU(PrivMMU): def __init__(self, elf: ElfExecutable): - super(PrivMMU, self).__init__(conf=RunConfig()) + super().__init__(conf=RunConfig()) + self.entrypoint = elf.symbols['_start'] self.binaries.append(elf) for sec in elf.sections: @@ -18,3 +34,6 @@ class PrivMMU(MMU): def allocate_section(self, name: str, req_size: int, flag: MemoryFlags): raise NotImplementedError("Not supported!") + + def get_entrypoint(self): + return self.entrypoint diff --git a/riscemu/priv/__main__.py b/riscemu/priv/__main__.py index 47820b7..803487c 100644 --- a/riscemu/priv/__main__.py +++ b/riscemu/priv/__main__.py @@ -1,4 +1,7 @@ from .PrivCPU import PrivCPU, RunConfig +from .ImageLoader import MemoryImageMMU +from .PrivMMU import LoadedElfMMU +from .ElfLoader import ElfExecutable from ..Tokenizer import RiscVInput from ..ExecutableParser import ExecutableParser @@ -8,14 +11,23 @@ import sys if __name__ == '__main__': import argparse - #parser = argparse.ArgumentParser(description='RISC-V privileged architecture emulator', prog='riscemu') + parser = argparse.ArgumentParser(description='RISC-V privileged architecture emulator', prog='riscemu') - #parser.add_argument('--kernel', type=str, help='Kernel elf loaded with user programs', nargs='?') - #parser.add_argument('--image', type=str, help='Memory image containing kernel', nargs='?') + parser.add_argument('--kernel', type=str, help='Kernel elf loaded with user programs', nargs='?') + parser.add_argument('--image', type=str, help='Memory image containing kernel', nargs='?') - #args = parser.parse_args() + args = parser.parse_args() + mmu = None - cpu = PrivCPU(RunConfig()) + if args.kernel is not None: + mmu = LoadedElfMMU(ElfExecutable(args.kernel)) + elif args.image is not None: + mmu = MemoryImageMMU(args.image) + + if mmu is None: + print("You must specify one of --kernel or --image for running in privilege mode!") + + cpu = PrivCPU(RunConfig(), mmu) cpu.run()