unicorn-engine / unicorn

Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
http://www.unicorn-engine.org
GNU General Public License v2.0
7.33k stars 1.31k forks source link

Cannot switch exception level to EL3 in aarch64 (python) #1884

Open malexkiy opened 9 months ago

malexkiy commented 9 months ago

I have a problem with switching to EL3 using Python bindings.

Tried to switch to EL3 via modifying PSTATE register and CurrentEL register, but fails. First CODE tries to read SCR_EL3 but raises an exception unicorn.unicorn.UcError: Unhandled CPU exception (UC_ERR_EXCEPTION). Second CODE checks current EL and jumps to the corresponding location with hook enabled.

My output:

CurrentEL: 0
PSTATE: 0x400003C5

address: 0xC0000010
X22: 0x00000004
EL3 test failed

Here is the code:

from dataclasses import dataclass

from unicorn import *
from unicorn.arm64_const import *

@dataclass
class SystemReg:
    op0: int
    op1: int
    crn: int
    crm: int
    op2: int

    def to_tuple(self):
        return self.crn, self.crm, self.op0, self.op1, self.op2

    def with_value(self, value: int):
        return self.to_tuple() + (value,)

def hook_ok(uc: Uc, address, size, user_data):
    print(f"address: 0x{address:08X}")
    print(f"X22: 0x{uc.reg_read(UC_ARM64_REG_X22):08X}")
    print("EL3 test ok")

    uc.emu_stop()

def hook_bad(uc, address, size, user_data):
    print(f"address: 0x{address:08X}")
    print(f"X22: 0x{uc.reg_read(UC_ARM64_REG_X22):08X}")
    print("EL3 test failed")

    uc.emu_stop()

def main():
    uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM)

    ADDRESS = 0xC000_0000
    MEM_SIZE = 0x20_0000

    # Throws an exception: unicorn.unicorn.UcError: Unhandled CPU exception (UC_ERR_EXCEPTION)
    CODE = (b"\x20\x00\x80\xd2"   # MOV X0, #1
            b"\xe1\xff\xbf\xd2"   # MOV X1, #0xFFFF0000
            b"\x02\x11\x3e\xd5"   # MRS X2, SCR_EL3
            b"\x02\x11\x1e\xd5"   # MSR SCR_EL3, X2
            b"\x00\x00\x00\x14")  # B .

    CODE = (b"\x56\x42\x38\xd5"   # MRS X22, CurrentEL
            b"\xdf\x32\x00\xf1"   # CMP X22, #0xC
            b"\x41\x00\x00\x54"   # BNE +#0x8
            b"\x00\x00\x00\x14"   # B .     ; EL3
            b"\x00\x00\x00\x14")  # B .     ; Not EL3

    uc.mem_map(ADDRESS, MEM_SIZE)

    uc.mem_write(ADDRESS, CODE)

    CurrentEl = SystemReg(0b11, 0b000, 0b0100, 0b0010, 0b010)

    el = uc.reg_read(UC_ARM64_REG_CP_REG, CurrentEl.to_tuple())
    print(f"CurrentEL: {el}")
    el = 3 << 2
    uc.reg_write(UC_ARM64_REG_CP_REG, CurrentEl.with_value(el))

    pstate = uc.reg_read(UC_ARM64_REG_PSTATE)
    print(f"PSTATE: 0x{pstate:08X}\n")
    pstate |= 3 << 2
    uc.reg_write(UC_ARM64_REG_PSTATE, pstate)

    uc.hook_add(UC_HOOK_CODE, hook_bad, begin=ADDRESS + 0x10, end=ADDRESS + 0x10)
    uc.hook_add(UC_HOOK_CODE, hook_ok, begin=ADDRESS + 0xC, end=ADDRESS + 0xC)

    uc.emu_start(ADDRESS, ADDRESS + len(CODE))

if __name__ == "__main__":
    main()