diff --git a/riscemu/CPU.py b/riscemu/CPU.py index e2fd594..12fdc91 100644 --- a/riscemu/CPU.py +++ b/riscemu/CPU.py @@ -10,7 +10,7 @@ import sys from typing import Tuple, List, Dict, Callable, Type from .Tokenizer import RiscVTokenizer - +from .Executable import MemoryFlags from .Syscall import SyscallInterface, get_syscall_symbols from .Exceptions import RiscemuBaseException, LaunchDebuggerException from .MMU import MMU @@ -85,6 +85,12 @@ class CPU: Run a loaded executable """ self.pc = le.run_ptr + + if self.conf.stack_size > 0: + stack = self.mmu.allocate_section("stack", self.conf.stack_size, MemoryFlags(False, False)) + self.regs.set('sp', stack.base + stack.size) + print(FMT_CPU + '[CPU] Allocated {} bytes of stack'.format(stack.size) + FMT_NONE) + print(FMT_CPU + '[CPU] Started running from 0x{:08X} ({})'.format(le.run_ptr, le.name) + FMT_NONE) self.__run() diff --git a/riscemu/Config.py b/riscemu/Config.py index 6946de9..b710677 100644 --- a/riscemu/Config.py +++ b/riscemu/Config.py @@ -10,7 +10,7 @@ from typing import Optional @dataclass(frozen=True, init=True) class RunConfig: - preffered_stack_size: Optional[int] = None + stack_size: int = 0 # 8 * 1024 * 1024 * 8 # for 8MB stack include_scall_symbols: bool = True add_accept_imm: bool = False # debugging diff --git a/riscemu/Exceptions.py b/riscemu/Exceptions.py index cf7017d..4bba2c0 100644 --- a/riscemu/Exceptions.py +++ b/riscemu/Exceptions.py @@ -90,12 +90,28 @@ class OutOfMemoryException(RiscemuBaseException): self.action = action def message(self): - return + FMT_MEM + '{}(Ran out of memory during {})'.format( + return FMT_MEM + '{}(Ran out of memory during {})'.format( self.__class__.__name__, self.action ) + FMT_NONE +class InvalidAllocationException(RiscemuBaseException): + def __init__(self, msg, name, size, flags): + self.msg = msg + self.name = name + self.size = size + self.flags = flags + + def message(self): + return FMT_MEM + '{}[{}](name={}, size={}, flags={})'.format( + self.__class__.__name__, + self.msg, + self.name, + self.size, + self.flags + ) + # CPU Exceptions diff --git a/riscemu/MMU.py b/riscemu/MMU.py index a63d3f0..211df7c 100644 --- a/riscemu/MMU.py +++ b/riscemu/MMU.py @@ -7,7 +7,7 @@ SPDX-License-Identifier: BSD-2-Clause from .Config import RunConfig from .Executable import Executable, LoadedExecutable, LoadedMemorySection, LoadedInstruction, MemoryFlags from .helpers import align_addr -from .Exceptions import OutOfMemoryException +from .Exceptions import OutOfMemoryException, InvalidAllocationException from .colors import * from typing import Dict, List, Tuple, Optional @@ -22,6 +22,11 @@ class MMU: The maximum size of the memory in bytes """ + max_alloc_size = 8 * 1024 * 1024 * 64 + """ + No single allocation can be bigger than 64 MB + """ + sections: List[LoadedMemorySection] """ A list of all loaded memory sections @@ -84,6 +89,32 @@ class MMU: return loaded_bin + def allocate_section(self, name: str, req_size: int, flag: MemoryFlags): + """ + Used to allocate a memory region (data only). Use `load_bin` if you want to load a binary, this is used for + stack and maybe malloc in the future. + + :param name: Name of the section to allocate + :param req_size: The requested size + :param flag: The flags protecting this memory section + :return: The LoadedMemorySection + """ + if flag.executable: + raise InvalidAllocationException('cannot allocate executable section', name, req_size, flag) + + if req_size < 0: + raise InvalidAllocationException('Invalid size request', name, req_size, flag) + + if req_size > self.max_alloc_size: + raise InvalidAllocationException('Cannot allocate more than {} bytes at a time'.format(self.max_alloc_size), name, req_size, flag) + + base = align_addr(self.first_free_addr) + size = align_addr(req_size) + sec = LoadedMemorySection(name, base, size, bytearray(size), flag, "") + self.sections.append(sec) + self.first_free_addr = base + size + return sec + def get_sec_containing(self, addr: int) -> Optional[LoadedMemorySection]: """ Returns the section that contains the address addr diff --git a/riscemu/__main__.py b/riscemu/__main__.py index 1aa5966..9ec6d4d 100644 --- a/riscemu/__main__.py +++ b/riscemu/__main__.py @@ -62,13 +62,13 @@ if __name__ == '__main__': help="Instruction sets to load, available are: {}. All are enabled by default" .format(", ".join(all_ins_names)), keys={k: True for k in all_ins_names}, omit_empty=True) - parser.add_argument('--default_stack_size', type=int, help='Default stack size of loaded programs', default=None, - metavar='default-stack-size', nargs='?') + parser.add_argument('--stack_size', type=int, help='Stack size of loaded programs, defaults to 8MB', nargs='?') args = parser.parse_args() - cfg = RunConfig( - preffered_stack_size=args.default_stack_size, + # create a RunConfig from the cli args + cfg_dict = dict( + stack_size=args.stack_size, debug_instruction=not args.options['disable_debug'], include_scall_symbols=not args.options['no_syscall_symbols'], debug_on_exception=not args.options['fail_on_ex'], @@ -76,6 +76,11 @@ if __name__ == '__main__': scall_fs=args.syscall_opts['fs_access'], scall_input=not args.syscall_opts['disable_input'] ) + for k, v in dict(cfg_dict).items(): + if v is None: + del cfg_dict[k] + + cfg = RunConfig(**cfg_dict) if not hasattr(args, 'ins'): setattr(args, 'ins', {k: True for k in all_ins_names})