added memory image support to priv emulator

kernel-mode
Anton Lydike 3 years ago
parent 4c352d8567
commit 1f03449694

@ -148,8 +148,7 @@ class CPU:
print(FMT_CPU + "[CPU] Returning to debugger!" + FMT_NONE)
return
if self.conf.debug_on_exception:
launch_debug_session(self, self.mmu, self.regs,
"Exception encountered, launching debug:")
launch_debug_session(self, self.mmu, self.regs, "Exception encountered, launching debug:")
if self.exit:
print()

@ -96,14 +96,19 @@ class Registers:
"""
if reg == 'zero':
return False
if reg not in Registers.all_registers():
raise InvalidRegisterException(reg)
#if reg not in Registers.all_registers():
# raise InvalidRegisterException(reg)
# replace fp register with s1, as these are the same register
if reg == 'fp':
reg = 's1'
if mark_set:
self.last_set = reg
self.vals[reg] = val & (2**32 - 1)
# check 32 bit signed bounds
if val < -2147483648:
val = -2147483648
elif val > 2147483647:
val = 2147483647
self.vals[reg] = val
return True
def get(self, reg, mark_read=True):
@ -113,8 +118,8 @@ class Registers:
:param mark_read: If the register should be markes as "last read" (only used internally)
:return: The contents of register reg
"""
if reg not in Registers.all_registers():
raise InvalidRegisterException(reg)
#if reg not in Registers.all_registers():
# raise InvalidRegisterException(reg)
if reg == 'fp':
reg = 's0'
if mark_read:

@ -45,9 +45,7 @@ def launch_debug_session(cpu: 'CPU', mmu: 'MMU', reg: 'Registers', prompt=""):
print("Invalid arg count!")
return
bin = mmu.get_bin_containing(cpu.pc)
if bin is None:
print(FMT_DEBUG + '[Debugger] Not in a section, can\'t execute instructions!' + FMT_NONE)
return
ins = LoadedInstruction(name, list(args), bin)
print(FMT_DEBUG + "Running instruction " + ins + FMT_NONE)
cpu.run_instruction(ins)

@ -23,10 +23,7 @@ STATIC_INSN: Dict[int, Tuple[str, List[int], int]] = {
def int_from_ins(insn: bytearray):
return (insn[3] << (8 * 3)) + \
(insn[2] << (8 * 2)) + \
(insn[1] << 8) + \
insn[0]
return int.from_bytes(insn, 'little')
def name_from_insn(ins: int):
@ -68,7 +65,7 @@ def name_from_insn(ins: int):
raise RuntimeError(f"Invalid instruction: {ins:x}")
def decode(ins: bytearray) -> Tuple[str, List[int], int]:
def decode(ins: Union[bytearray, bytes]) -> Tuple[str, List[int], int]:
insn = int_from_ins(ins)
if insn & 3 != 3:

@ -33,19 +33,14 @@ def int_to_bytes(val, bytes=4, unsigned=False) -> bytearray:
"""
if unsigned and val < 0:
raise NumberFormatException("unsigned negative number!")
return bytearray([
(val >> ((bytes - i - 1) * 8)) & 0xFF for i in range(bytes)
])
return bytearray(to_unsigned(val, bytes).to_bytes(bytes, 'little'))
def int_from_bytes(bytes, unsigned=False) -> int:
"""
byte -> int (two's complement)
"""
num = 0
for b in bytes:
num = num << 8
num += b
num = int.from_bytes(bytes, 'little')
if unsigned:
return num
@ -55,7 +50,7 @@ def int_from_bytes(bytes, unsigned=False) -> int:
def to_unsigned(num: int, bytes=4) -> int:
if num < 0:
return 2 ** (bytes * 8) + num
return (2 ** (bytes * 8)) + num
return num

@ -2,7 +2,7 @@ from typing import Dict, Union, Callable, Optional
from collections import defaultdict
from .privmodes import PrivModes
from .Exceptions import InstructionAccessFault
from ..helpers import to_unsigned
from ..helpers import to_signed
from ..colors import FMT_CSR, FMT_NONE
from .CSRConsts import CSR_NAME_TO_ADDR, MSTATUS_LEN_2, MSTATUS_OFFSETS
@ -38,7 +38,7 @@ class CSR:
addr = self._name_to_addr(addr)
if addr is None:
return
val = to_unsigned(val)
val = to_signed(val)
self.listeners[addr](self.regs[addr], val)
if addr == 0x300:
self.mstatus_cache_dirty = True

@ -39,7 +39,7 @@ class ElfExecutable:
if not elf.header.e_ident.EI_CLASS == 'ELFCLASS32':
raise InvalidElfException("Only 32bit executables are supported!")
self.run_ptr = elf.header.e_entry;
self.run_ptr = elf.header.e_entry
for sec in elf.iter_sections():
if isinstance(sec, SymbolTableSection):

@ -58,7 +58,7 @@ class CpuTrap(BaseException):
if (self.interrupt, self.code) in MCAUSE_TRANSLATION:
name = MCAUSE_TRANSLATION[(self.interrupt, self.code)] + "({}, {})".format(self.interrupt, self.code)
return "{} {{priv={}, type={}, mtval={}}}".format(
return "{} {{priv={}, type={}, mtval={:x}}}".format(
name, self.priv.name, self.type.name, self.mtval
)
@ -84,3 +84,8 @@ class InstructionAccessFault(CpuTrap):
class TimerInterrupt(CpuTrap):
def __init__(self):
super().__init__(7, 0, CpuTrapType.TIMER)
class EcallTrap(CpuTrap):
def __init__(self, mode: PrivModes):
super().__init__(mode.value + 8, 0, CpuTrapType.SOFTWARE)

@ -8,11 +8,14 @@ import time
from riscemu.CPU import *
from .CSR import CSR
from .ElfLoader import ElfExecutable
from .ImageLoader import ContinuousMMU
from .Exceptions import *
from .PrivMMU import PrivMMU
from ..IO import TextIO
from .PrivRV32I import PrivRV32I
from .privmodes import PrivModes
from ..instructions.RV32M import RV32M
import json
if typing.TYPE_CHECKING:
from riscemu import Executable, LoadedExecutable, LoadedInstruction
@ -48,9 +51,14 @@ class PrivCPU(CPU):
super().__init__(conf, [PrivRV32I, RV32M])
self.mode: PrivModes = PrivModes.MACHINE
kernel = ElfExecutable('kernel')
self.mmu = PrivMMU(kernel)
self.pc = kernel.run_ptr
with open('mem.img', 'rb') as memf:
data = memf.read()
with open('mem.img.dbg', 'r') as dbgf:
debug_info = json.load(dbgf)
self.mmu = ContinuousMMU(data, debug_info, self)
self.pc = 0x100
self.mmu.add_io(TextIO.TextIO(0xff0000, 64))
self.syscall_int = None
self.launch_debug = False
@ -109,7 +117,7 @@ class PrivCPU(CPU):
def run(self):
print(FMT_CPU + '[CPU] Started running from 0x{:08X} ({})'.format(self.pc, "kernel") + FMT_NONE)
self._time_start = time.perf_counter_ns() // self.TIME_RESOLUTION_NS
self._run(True)
self._run()
def _init_csr(self):
# set up CSR
@ -161,7 +169,7 @@ class PrivCPU(CPU):
def step(self, verbose=True):
try:
self.cycle += 1
if self.cycle % 10 == 0:
if self.cycle % 20 == 0:
self._timer_step()
self._check_interrupt()
ins = self.mmu.read_ins(self.pc)
@ -178,7 +186,6 @@ class PrivCPU(CPU):
if self._time_timecmp <= (time.perf_counter_ns() // self.TIME_RESOLUTION_NS) - self._time_start:
self.pending_traps.append(TimerInterrupt())
self._time_interrupt_enabled = False
print(FMT_CPU + "[CPU] raising timer interrupt: tartegt: {}, current: {}".format(self._time_timecmp, (time.perf_counter_ns() // self.TIME_RESOLUTION_NS) - self._time_start) + FMT_NONE)
def _check_interrupt(self):
if not (len(self.pending_traps) > 0 and self.csr.get_mstatus('mie')):
@ -186,10 +193,12 @@ class PrivCPU(CPU):
# select best interrupt
# TODO: actually select based on the official ranking
trap = self.pending_traps.pop() # use the most recent trap
print(FMT_CPU + "[CPU] taking trap {}!".format(trap) + FMT_NONE)
if not isinstance(trap, TimerInterrupt):
print(FMT_CPU + "[CPU] taking trap {}!".format(trap) + FMT_NONE)
if trap.priv != PrivModes.MACHINE:
print(FMT_CPU + "[CPU] Trap not targeting machine mode encountered! - undefined behaviour!" + FMT_NONE)
raise Exception("Undefined behaviour!")
if self.mode != PrivModes.USER:
print(FMT_CPU + "[CPU] Trap triggered outside of user mode?!" + FMT_NONE)
@ -198,7 +207,7 @@ class PrivCPU(CPU):
self.csr.set_mstatus('mpp', self.mode.value)
self.csr.set_mstatus('mie', 0)
self.csr.set('mcause', trap.mcause)
self.csr.set('mepc', self.pc)
self.csr.set('mepc', self.pc-self.INS_XLEN)
self.csr.set('mtval', trap.mtval)
self.mode = trap.priv
mtvec = self.csr.get('mtvec')
@ -223,16 +232,16 @@ class PrivCPU(CPU):
continue
cps = (cycle - cycled) / (time_ns - timed) * 1000000000
#print(" {:03d} cycles in {:08d}ns ({:.2f} cycles/s)".format(
# print(" {:03d} cycles in {:08d}ns ({:.2f} cycles/s)".format(
# cycle - cycled,
# time_ns - timed,
# cps
#))
# ))
cycled = cycle
timed = time_ns
cps_list.append(cps)
print(" on average {:.0f} cycles/s".format(sum(cps_list) / len(cps_list)) + FMT_NONE)
print(" on average {:.0f} instructions/s".format(sum(cps_list) / len(cps_list)) + FMT_NONE)
self._perf_counters = list()
def record_perf_profile(self):
self._perf_counters.append((time.perf_counter_ns(), self.cycle))
self._perf_counters.append((time.perf_counter_ns(), self.cycle))

@ -88,12 +88,7 @@ class PrivRV32I(RV32I):
"""
Overwrite the scall from userspace RV32I
"""
if self.cpu.mode == PrivModes.USER:
raise CpuTrap(8, 0, CpuTrapType.SOFTWARE, self.cpu.mode) # ecall from U mode
elif self.cpu.mode == PrivModes.SUPER:
raise CpuTrap(9, 0, CpuTrapType.SOFTWARE, self.cpu.mode) # ecall from S mode - should not happen
elif self.cpu.mode == PrivModes.MACHINE:
raise CpuTrap(11, 0, CpuTrapType.SOFTWARE, self.cpu.mode) # ecall from M mode
raise EcallTrap(self.cpu.mode)
def instruction_beq(self, ins: 'LoadedInstruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)

@ -6,11 +6,18 @@ from ..ExecutableParser import ExecutableParser
import sys
if __name__ == '__main__':
import argparse
files = sys.argv
#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='?')
#args = parser.parse_args()
cpu = PrivCPU(RunConfig())
cpu.run()

Loading…
Cancel
Save