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.53k stars 1.33k forks source link

Cannot switch exception level in A64 #1843

Closed smx-smx closed 1 year ago

smx-smx commented 1 year ago

Hi In A64, when writing UC_ARM64_REG_PSTATE with a new EL value, nothing happens and the running code still sees the old value. Snippet:

    // set EL3
    uint64_t pstate = 0;
    uc_reg_read(uc, UC_ARM64_REG_PSTATE, &pstate);

    pstate = pstate & ~(3 << 2);
    pstate |= 3 << 2;
    if(uc_reg_write(uc, UC_ARM64_REG_PSTATE, &pstate) != UC_ERR_OK){
        fprintf(stderr, "uc_reg_write(CurrentEL) failed\n");
        std::terminate();
    }

A hint is in unicorn\qemu\target\arm\cpu64.c, at the end of cpu_aarch64_init:

    // Backward compatability to enable FULL 64bits address space.
    env->pstate = PSTATE_MODE_EL1h;

    arm_rebuild_hflags(env);

    return cpu;

This confirms that the default EL is EL1, and also that arm_rebuild_hflags should be called if it's ever updated (something that doesn't happen in pstate_write).

I confirm that the following (very dirty) hack fixes the problem (calling arm_rebuild_hflags manually - unicorn.dll is unstripped in MinGW):

    // set EL3
    uint64_t pstate = 0;
    uc_reg_read(uc, UC_ARM64_REG_PSTATE, &pstate);

    pstate = pstate & ~(3 << 2);
    pstate |= 3 << 2;
    if(uc_reg_write(uc, UC_ARM64_REG_PSTATE, &pstate) != UC_ERR_OK){
        fprintf(stderr, "uc_reg_write(CurrentEL) failed\n");
        std::terminate();
    }

    typedef void (*pfnRebuildFlags)(void *env);
    HMODULE handle = GetModuleHandleA("libunicorn.dll");
    pfnRebuildFlags pfn = (pfnRebuildFlags)GetProcAddress(handle, "arm_rebuild_hflags_aarch64");
    printf("the func %p\n", pfn);

    uint8_t *uc_int = (uint8_t *)uc;
    uint8_t *p_cpu = *(uint8_t **)&uc_int[384];
    uint8_t *env = &p_cpu[38800];
    pfn(env);

Proposal: change pstate_write to the following:

static inline void pstate_write(CPUARMState *env, uint32_t val)
{
    env->ZF = (~val) & PSTATE_Z;
    env->NF = val;
    env->CF = (val >> 29) & 1;
    env->VF = (val << 3) & 0x80000000;
    env->daif = val & PSTATE_DAIF;
    env->btype = (val >> 10) & 3;
    env->pstate = val & ~CACHED_PSTATE_BITS;
    arm_rebuild_hflags(env); // <-- inserted call to rebuild hflags
}

Thanks in advance

wtdcode commented 1 year ago

Closed due to fix pushed (see PR).