kernel-mode #1
@ -0,0 +1 @@
|
||||
pyelftools~=0.27
|
@ -1,9 +1,118 @@
|
||||
from ..Executable import Executable, MemorySection, MemoryFlags
|
||||
from ..Executable import Executable, MemorySection, MemoryFlags, LoadedExecutable, LoadedMemorySection
|
||||
from ..Exceptions import RiscemuBaseException
|
||||
from ..helpers import FMT_PARSE, FMT_NONE
|
||||
from ..colors import FMT_GREEN, FMT_BOLD
|
||||
|
||||
FMT_ELF = FMT_GREEN + FMT_BOLD
|
||||
|
||||
from .Exceptions import *
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from typing import List, Dict, Union
|
||||
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.sections import Section, SymbolTableSection
|
||||
|
||||
from ..decoder import decode
|
||||
|
||||
# This requires pyelftools package!
|
||||
|
||||
class ElfExecutable(Executable):
|
||||
def __init__(self, name):
|
||||
from elftools.elf.elffile import ELFFile
|
||||
INCLUDE_SEC = ('.text', '.stack', '.bss')
|
||||
|
||||
with open(f)
|
||||
|
||||
class ElfExecutable:
|
||||
sections: List['ElfLoadedMemorySection']
|
||||
sections_by_name: Dict[str, 'ElfLoadedMemorySection']
|
||||
symbols: Dict[str, int]
|
||||
run_ptr: int
|
||||
|
||||
def __init__(self, name: str):
|
||||
self.sections = list()
|
||||
self.sections_by_name = dict()
|
||||
self.symbols = dict()
|
||||
|
||||
with open(name, 'rb') as f:
|
||||
print(FMT_ELF + "[ElfLoader] Loading elf executable from: {}".format(name) + FMT_NONE)
|
||||
self._read_elf(ELFFile(f))
|
||||
|
||||
def _read_elf(self, elf: ELFFile):
|
||||
if not elf.header.e_machine == 'EM_RISCV':
|
||||
raise InvalidElfException("Not a RISC-V elf file!")
|
||||
if not elf.header.e_ident.EI_CLASS == 'ELFCLASS32':
|
||||
raise InvalidElfException("Only 32bit executables are supported!")
|
||||
|
||||
self.run_ptr = elf.header.e_entry;
|
||||
|
||||
for sec in elf.iter_sections():
|
||||
if isinstance(sec, SymbolTableSection):
|
||||
self._parse_symtab(sec)
|
||||
continue
|
||||
|
||||
if sec.name not in INCLUDE_SEC:
|
||||
continue
|
||||
|
||||
sec_ = self._lms_from_elf_sec(sec, 'kernel')
|
||||
self.sections.append(sec_)
|
||||
self.sections_by_name[sec.name] = sec_
|
||||
|
||||
def _lms_from_elf_sec(self, sec: Section, owner: str):
|
||||
is_code = sec.name in ('.text',)
|
||||
data = sec.data()
|
||||
flags = MemoryFlags(is_code, is_code)
|
||||
print(FMT_ELF + "[ElfLoader] Section {} at: {:X}".format(sec.name, sec.header.sh_addr) + FMT_NONE)
|
||||
return ElfLoadedMemorySection(
|
||||
sec.name,
|
||||
sec.header.sh_addr,
|
||||
sec.data_size,
|
||||
data,
|
||||
flags,
|
||||
owner
|
||||
)
|
||||
|
||||
def _parse_symtab(self, symtab: SymbolTableSection):
|
||||
self.symbols = {
|
||||
sym.name: sym.entry.st_value for sym in symtab.iter_symbols() if sym.name
|
||||
}
|
||||
|
||||
def load_user_elf(self, name: str):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidElfException(RiscemuBaseException):
|
||||
def __init__(self, msg: str):
|
||||
super().__init__()
|
||||
self.msg = msg
|
||||
|
||||
def message(self):
|
||||
return FMT_PARSE + "{}(\"{}\")".format(self.__class__.__name__, self.msg) + FMT_NONE
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ElfInstruction:
|
||||
name: str
|
||||
args: List[Union[int, str]]
|
||||
|
||||
def get_imm(self, num: int):
|
||||
return self.args[-1]
|
||||
|
||||
def get_imm_reg(self, num: int):
|
||||
return self.args[-1], self.args[-2]
|
||||
|
||||
def get_reg(self, num: int):
|
||||
return self.args[num]
|
||||
|
||||
def __repr__(self):
|
||||
return "{:<8} {}".format(
|
||||
self.name,
|
||||
", ".join(map(str, self.args))
|
||||
)
|
||||
|
||||
class ElfLoadedMemorySection(LoadedMemorySection):
|
||||
def read_instruction(self, offset):
|
||||
if not self.flags.executable:
|
||||
print(FMT_PARSE + "Reading instruction from non-executable memory!" + FMT_NONE)
|
||||
raise InstructionAccessFault(offset + self.base)
|
||||
if offset % 4 != 0:
|
||||
raise InstructionAddressMisalignedTrap(offset + self.base)
|
||||
return ElfInstruction(*decode(self.content[offset:offset + 4]))
|
||||
|
@ -21,3 +21,15 @@ class CpuTrap(BaseException):
|
||||
class IllegalInstructionTrap(CpuTrap):
|
||||
def __init__(self):
|
||||
super().__init__(0, 2, 0)
|
||||
|
||||
|
||||
class InstructionAddressMisalignedTrap(CpuTrap):
|
||||
def __init__(self, addr: int):
|
||||
super().__init__(0, 0, addr)
|
||||
|
||||
|
||||
class InstructionAccessFault(CpuTrap):
|
||||
def __init__(self, addr: int):
|
||||
super().__init__(0, 1, addr)
|
||||
|
||||
|
||||
|
@ -11,6 +11,9 @@ from .Exceptions import *
|
||||
from .CSR import CSR
|
||||
from .PrivRV32I import PrivRV32I
|
||||
from ..instructions.RV32M import RV32M
|
||||
from .PrivMMU import PrivMMU
|
||||
from .ElfLoader import ElfExecutable
|
||||
from .privmodes import PrivModes
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
@ -20,13 +23,6 @@ if typing.TYPE_CHECKING:
|
||||
from riscemu import Executable, LoadedExecutable, LoadedInstruction
|
||||
from riscemu.instructions.InstructionSet import InstructionSet
|
||||
|
||||
|
||||
class PrivModes(IntEnum):
|
||||
USER = 0
|
||||
SUPER = 1
|
||||
MACHINE = 3
|
||||
|
||||
|
||||
class PrivCPU(CPU):
|
||||
"""
|
||||
This is a CPU that has different modes, instruction sets and registers.
|
||||
@ -38,7 +34,7 @@ class PrivCPU(CPU):
|
||||
|
||||
csr: CSR
|
||||
|
||||
INS_XLEN = 1
|
||||
INS_XLEN = 4
|
||||
"""
|
||||
Size of an instruction in memory. Should be 4, but since our loading code is shit, instruction take up
|
||||
the equivalent of "1 byte" (this is actually impossible)
|
||||
@ -48,12 +44,18 @@ class PrivCPU(CPU):
|
||||
super().__init__(conf, [PrivRV32I, RV32M])
|
||||
self.mode: PrivModes = PrivModes.MACHINE
|
||||
|
||||
exec = ElfExecutable('kernel')
|
||||
self.mmu = PrivMMU(exec)
|
||||
self.pc = exec.run_ptr
|
||||
self.syscall_int = None
|
||||
|
||||
# set up CSR
|
||||
self.csr = CSR()
|
||||
# TODO: Actually populate the CSR with real data (vendorID, heartID, machine implementation etc)
|
||||
self.csr.set('mhartid', 0) # core id
|
||||
self.csr.set('mimpid', 1) # implementation id
|
||||
self.csr.set('misa', ()) # available ISA
|
||||
# TODO: set correct misa
|
||||
self.csr.set('misa', 1) # available ISA
|
||||
|
||||
def _run(self, verbose=False):
|
||||
if self.pc <= 0:
|
||||
@ -66,7 +68,7 @@ class PrivCPU(CPU):
|
||||
ins = self.mmu.read_ins(self.pc)
|
||||
if verbose:
|
||||
print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins))
|
||||
self.pc += 1
|
||||
self.pc += self.INS_XLEN
|
||||
self.run_instruction(ins)
|
||||
except CpuTrap as trap:
|
||||
mie = self.csr.get_mstatus('mie')
|
||||
@ -97,8 +99,6 @@ class PrivCPU(CPU):
|
||||
else:
|
||||
# standard mode
|
||||
self.pc = (mtvec >> 2)
|
||||
|
||||
|
||||
except RiscemuBaseException as ex:
|
||||
if not isinstance(ex, LaunchDebuggerException):
|
||||
print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc - 1, ins) + FMT_NONE)
|
||||
@ -119,3 +119,17 @@ class PrivCPU(CPU):
|
||||
else:
|
||||
print()
|
||||
print(FMT_CPU + "Program stopped without exiting - perhaps you stopped the debugger?" + FMT_NONE)
|
||||
|
||||
def load(self, e: riscemu.Executable):
|
||||
raise NotImplementedError("Not supported!")
|
||||
|
||||
def run_loaded(self, le: 'riscemu.LoadedExecutable'):
|
||||
raise NotImplementedError("Not supported!")
|
||||
|
||||
def get_tokenizer(self, tokenizer_input):
|
||||
raise NotImplementedError("Not supported!")
|
||||
|
||||
def run(self):
|
||||
print(FMT_CPU + '[CPU] Started running from 0x{:08X} ({})'.format(self.pc, "kernel") + FMT_NONE)
|
||||
self._run(True)
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
from ..MMU import *
|
||||
|
||||
import typing
|
||||
|
||||
from .ElfLoader import ElfExecutable
|
||||
|
||||
class PrivMMU(MMU):
|
||||
def __init__(self, elf: ElfExecutable):
|
||||
super(PrivMMU, self).__init__(conf=RunConfig())
|
||||
|
||||
self.binaries.append(elf)
|
||||
for sec in elf.sections:
|
||||
self.sections.append(sec)
|
||||
|
||||
def load_bin(self, exe: Executable) -> LoadedExecutable:
|
||||
raise NotImplementedError("This is a privMMU, it's initialized with a single ElfExecutable!")
|
||||
|
||||
def allocate_section(self, name: str, req_size: int, flag: MemoryFlags):
|
||||
raise NotImplementedError("Not supported!")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -4,24 +4,28 @@ RiscEmu (c) 2021 Anton Lydike
|
||||
SPDX-License-Identifier: MIT
|
||||
"""
|
||||
|
||||
from riscemu.instructions.RV32I import *
|
||||
from ..instructions.RV32I import *
|
||||
from ..Exceptions import INS_NOT_IMPLEMENTED
|
||||
from riscemu.priv.PrivCPU import PrivModes, PrivCPU
|
||||
from .Exceptions import *
|
||||
from .privmodes import PrivModes
|
||||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from riscemu.priv.PrivCPU import PrivCPU
|
||||
|
||||
|
||||
class PrivRV32I(RV32I):
|
||||
cpu: PrivCPU
|
||||
cpu: 'PrivCPU'
|
||||
"""
|
||||
This is an extension of RV32I, written for the PrivCPU class
|
||||
"""
|
||||
|
||||
def instruction_csrrw(self, ins: 'LoadedInstruction'):
|
||||
rd, off, rs = self.parse_crs_ins(ins)
|
||||
rd, rs, ind = self.parse_crs_ins(ins)
|
||||
if rd != 'zero':
|
||||
old_val = int_from_bytes(self.cpu.csr.read(off, 4))
|
||||
old_val = int_from_bytes(self.cpu.csr[ind])
|
||||
self.regs.set(rd, old_val)
|
||||
self.cpu.csr.write(off, 4, int_to_bytes(rs))
|
||||
self.cpu.csr.set(ind, rs)
|
||||
|
||||
def instruction_csrrs(self, ins: 'LoadedInstruction'):
|
||||
INS_NOT_IMPLEMENTED(ins)
|
||||
@ -63,17 +67,64 @@ class PrivRV32I(RV32I):
|
||||
Overwrite the scall from userspace RV32I
|
||||
"""
|
||||
if self.cpu.mode == PrivModes.USER:
|
||||
raise CpuTrap(0, 8) # ecall from U mode
|
||||
raise CpuTrap(0, 8) # ecall from U mode
|
||||
elif self.cpu.mode == PrivModes.SUPER:
|
||||
raise CpuTrap(0, 9) # ecall from S mode - should not happen
|
||||
raise CpuTrap(0, 9) # ecall from S mode - should not happen
|
||||
elif self.cpu.mode == PrivModes.MACHINE:
|
||||
raise CpuTrap(0, 11) # ecall from M mode
|
||||
raise CpuTrap(0, 11) # ecall from M mode
|
||||
|
||||
def instruction_beq(self, ins: 'LoadedInstruction'):
|
||||
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
|
||||
if rs1 == rs2:
|
||||
self.pc += dst
|
||||
|
||||
def instruction_bne(self, ins: 'LoadedInstruction'):
|
||||
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
|
||||
if rs1 != rs2:
|
||||
self.pc += dst
|
||||
|
||||
def instruction_blt(self, ins: 'LoadedInstruction'):
|
||||
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
|
||||
if rs1 < rs2:
|
||||
self.pc += dst
|
||||
|
||||
def instruction_bge(self, ins: 'LoadedInstruction'):
|
||||
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
|
||||
if rs1 >= rs2:
|
||||
self.pc += dst
|
||||
|
||||
def instruction_bltu(self, ins: 'LoadedInstruction'):
|
||||
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
|
||||
if rs1 < rs2:
|
||||
self.pc += dst
|
||||
|
||||
def instruction_bgeu(self, ins: 'LoadedInstruction'):
|
||||
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
|
||||
if rs1 >= rs2:
|
||||
self.pc += dst
|
||||
|
||||
# technically deprecated
|
||||
def instruction_j(self, ins: 'LoadedInstruction'):
|
||||
raise NotImplementedError("Should never be reached!")
|
||||
|
||||
def instruction_jal(self, ins: 'LoadedInstruction'):
|
||||
ASSERT_LEN(ins.args, 2)
|
||||
reg = ins.get_reg(0)
|
||||
addr = ins.get_imm(1)
|
||||
self.regs.set(reg, self.pc)
|
||||
self.pc += addr
|
||||
|
||||
def instruction_jalr(self, ins: 'LoadedInstruction'):
|
||||
ASSERT_LEN(ins.args, 3)
|
||||
rd, rs, imm = self.parse_rd_rs_imm(ins)
|
||||
self.regs.set(rd, self.pc)
|
||||
self.pc = rs + imm
|
||||
|
||||
def parse_crs_ins(self, ins: 'LoadedInstruction'):
|
||||
ASSERT_LEN(ins.args, 3)
|
||||
return ins.get_reg(0), ins.get_imm(1), self.get_reg_content(ins, 2)
|
||||
return ins.get_reg(0), self.get_reg_content(ins, 1), ins.get_imm(2)
|
||||
|
||||
def parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]:
|
||||
ASSERT_LEN(ins.args, 3)
|
||||
print("dop")
|
||||
return ins.get_reg(1), self.get_reg_content(ins, 0) + ins.get_imm(2)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from .PrivCPU import *
|
||||
from .PrivCPU import PrivCPU, RunConfig
|
||||
|
||||
from ..Tokenizer import RiscVInput
|
||||
from ..ExecutableParser import ExecutableParser
|
||||
@ -11,17 +11,6 @@ if __name__ == '__main__':
|
||||
|
||||
cpu = PrivCPU(RunConfig())
|
||||
|
||||
try:
|
||||
loaded_exe = None
|
||||
for file in files:
|
||||
tk = cpu.get_tokenizer(RiscVInput.from_file(file))
|
||||
tk.tokenize()
|
||||
loaded_exe = cpu.load(ExecutableParser(tk).parse())
|
||||
# run the last loaded executable
|
||||
cpu.run_loaded(loaded_exe)
|
||||
except RiscemuBaseException as e:
|
||||
print("Error while parsing: {}".format(e.message()))
|
||||
import traceback
|
||||
cpu.run()
|
||||
|
||||
|
||||
traceback.print_exception(type(e), e, e.__traceback__)
|
||||
sys.exit(1)
|
||||
|
@ -0,0 +1,7 @@
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class PrivModes(IntEnum):
|
||||
USER = 0
|
||||
SUPER = 1
|
||||
MACHINE = 3
|
Loading…
Reference in New Issue
Block a user