kernel-mode #1
@ -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…
Reference in New Issue
Block a user