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!
|
# This requires pyelftools package!
|
||||||
|
|
||||||
class ElfExecutable(Executable):
|
INCLUDE_SEC = ('.text', '.stack', '.bss')
|
||||||
def __init__(self, name):
|
|
||||||
from elftools.elf.elffile import ELFFile
|
|
||||||
|
|
||||||
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):
|
class IllegalInstructionTrap(CpuTrap):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(0, 2, 0)
|
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 .CSR import CSR
|
||||||
from .PrivRV32I import PrivRV32I
|
from .PrivRV32I import PrivRV32I
|
||||||
from ..instructions.RV32M import RV32M
|
from ..instructions.RV32M import RV32M
|
||||||
|
from .PrivMMU import PrivMMU
|
||||||
|
from .ElfLoader import ElfExecutable
|
||||||
|
from .privmodes import PrivModes
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
@ -20,13 +23,6 @@ if typing.TYPE_CHECKING:
|
|||||||
from riscemu import Executable, LoadedExecutable, LoadedInstruction
|
from riscemu import Executable, LoadedExecutable, LoadedInstruction
|
||||||
from riscemu.instructions.InstructionSet import InstructionSet
|
from riscemu.instructions.InstructionSet import InstructionSet
|
||||||
|
|
||||||
|
|
||||||
class PrivModes(IntEnum):
|
|
||||||
USER = 0
|
|
||||||
SUPER = 1
|
|
||||||
MACHINE = 3
|
|
||||||
|
|
||||||
|
|
||||||
class PrivCPU(CPU):
|
class PrivCPU(CPU):
|
||||||
"""
|
"""
|
||||||
This is a CPU that has different modes, instruction sets and registers.
|
This is a CPU that has different modes, instruction sets and registers.
|
||||||
@ -38,7 +34,7 @@ class PrivCPU(CPU):
|
|||||||
|
|
||||||
csr: CSR
|
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
|
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)
|
the equivalent of "1 byte" (this is actually impossible)
|
||||||
@ -48,12 +44,18 @@ class PrivCPU(CPU):
|
|||||||
super().__init__(conf, [PrivRV32I, RV32M])
|
super().__init__(conf, [PrivRV32I, RV32M])
|
||||||
self.mode: PrivModes = PrivModes.MACHINE
|
self.mode: PrivModes = PrivModes.MACHINE
|
||||||
|
|
||||||
|
exec = ElfExecutable('kernel')
|
||||||
|
self.mmu = PrivMMU(exec)
|
||||||
|
self.pc = exec.run_ptr
|
||||||
|
self.syscall_int = None
|
||||||
|
|
||||||
# set up CSR
|
# set up CSR
|
||||||
self.csr = CSR()
|
self.csr = CSR()
|
||||||
# TODO: Actually populate the CSR with real data (vendorID, heartID, machine implementation etc)
|
# TODO: Actually populate the CSR with real data (vendorID, heartID, machine implementation etc)
|
||||||
self.csr.set('mhartid', 0) # core id
|
self.csr.set('mhartid', 0) # core id
|
||||||
self.csr.set('mimpid', 1) # implementation 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):
|
def _run(self, verbose=False):
|
||||||
if self.pc <= 0:
|
if self.pc <= 0:
|
||||||
@ -66,7 +68,7 @@ class PrivCPU(CPU):
|
|||||||
ins = self.mmu.read_ins(self.pc)
|
ins = self.mmu.read_ins(self.pc)
|
||||||
if verbose:
|
if verbose:
|
||||||
print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins))
|
print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins))
|
||||||
self.pc += 1
|
self.pc += self.INS_XLEN
|
||||||
self.run_instruction(ins)
|
self.run_instruction(ins)
|
||||||
except CpuTrap as trap:
|
except CpuTrap as trap:
|
||||||
mie = self.csr.get_mstatus('mie')
|
mie = self.csr.get_mstatus('mie')
|
||||||
@ -97,8 +99,6 @@ class PrivCPU(CPU):
|
|||||||
else:
|
else:
|
||||||
# standard mode
|
# standard mode
|
||||||
self.pc = (mtvec >> 2)
|
self.pc = (mtvec >> 2)
|
||||||
|
|
||||||
|
|
||||||
except RiscemuBaseException as ex:
|
except RiscemuBaseException as ex:
|
||||||
if not isinstance(ex, LaunchDebuggerException):
|
if not isinstance(ex, LaunchDebuggerException):
|
||||||
print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc - 1, ins) + FMT_NONE)
|
print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc - 1, ins) + FMT_NONE)
|
||||||
@ -119,3 +119,17 @@ class PrivCPU(CPU):
|
|||||||
else:
|
else:
|
||||||
print()
|
print()
|
||||||
print(FMT_CPU + "Program stopped without exiting - perhaps you stopped the debugger?" + FMT_NONE)
|
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
|
SPDX-License-Identifier: MIT
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from riscemu.instructions.RV32I import *
|
from ..instructions.RV32I import *
|
||||||
from ..Exceptions import INS_NOT_IMPLEMENTED
|
from ..Exceptions import INS_NOT_IMPLEMENTED
|
||||||
from riscemu.priv.PrivCPU import PrivModes, PrivCPU
|
|
||||||
from .Exceptions import *
|
from .Exceptions import *
|
||||||
|
from .privmodes import PrivModes
|
||||||
|
import typing
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from riscemu.priv.PrivCPU import PrivCPU
|
||||||
|
|
||||||
|
|
||||||
class PrivRV32I(RV32I):
|
class PrivRV32I(RV32I):
|
||||||
cpu: PrivCPU
|
cpu: 'PrivCPU'
|
||||||
"""
|
"""
|
||||||
This is an extension of RV32I, written for the PrivCPU class
|
This is an extension of RV32I, written for the PrivCPU class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def instruction_csrrw(self, ins: 'LoadedInstruction'):
|
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':
|
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.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'):
|
def instruction_csrrs(self, ins: 'LoadedInstruction'):
|
||||||
INS_NOT_IMPLEMENTED(ins)
|
INS_NOT_IMPLEMENTED(ins)
|
||||||
@ -69,11 +73,58 @@ class PrivRV32I(RV32I):
|
|||||||
elif self.cpu.mode == PrivModes.MACHINE:
|
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'):
|
def parse_crs_ins(self, ins: 'LoadedInstruction'):
|
||||||
ASSERT_LEN(ins.args, 3)
|
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 ..Tokenizer import RiscVInput
|
||||||
from ..ExecutableParser import ExecutableParser
|
from ..ExecutableParser import ExecutableParser
|
||||||
@ -11,17 +11,6 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
cpu = PrivCPU(RunConfig())
|
cpu = PrivCPU(RunConfig())
|
||||||
|
|
||||||
try:
|
cpu.run()
|
||||||
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
|
|
||||||
|
|
||||||
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