Under UC_TLB_VIRTUAL, MIPS Load-Linked (ll/lld) raise a spurious Address Error (EXCP_AdEL, intno 12) for addresses at/above the useg limit (>= 0x80000000), even though an ordinary load (ld/lw) at the same address goes through the UC_HOOK_TLB_FILL hook and succeeds. This breaks emulating MIPS64 code that takes a lock above 2 GB (e.g. glibc).
Reproduced on 2.1.3 and current dev; MIPS64 BE+LE, independent of CPU model.
import unicorn
from unicorn import (Uc, UC_ARCH_MIPS, UC_MODE_MIPS64, UC_MODE_BIG_ENDIAN,
UC_HOOK_TLB_FILL, UC_TLB_VIRTUAL, UC_PROT_ALL, UcError)
from unicorn.mips_const import UC_MIPS_REG_S0, UC_MIPS_REG_V1
ADDR = 0x120000000 # above the MIPS useg limit; requires the virtual TLB
def identity_tlb(uc, vaddr, access, entry, user_data):
entry.paddr = vaddr & ~0xfff
entry.perms = UC_PROT_ALL
return True
for opcode, name in ((0xde030000, "ld $v1, 0($s0)"), (0xc2030000, "ll $v1, 0($s0)")):
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS64 | UC_MODE_BIG_ENDIAN)
uc.ctl_set_tlb_mode(UC_TLB_VIRTUAL)
uc.hook_add(UC_HOOK_TLB_FILL, identity_tlb)
uc.mem_map(0x10000000, 0x1000) # code page
uc.mem_map(ADDR, 0x1000) # data page (the lock word)
uc.mem_write(0x10000000, opcode.to_bytes(4, "big"))
uc.reg_write(UC_MIPS_REG_S0, ADDR)
try:
uc.emu_start(0x10000000, 0x10000004, 0, 1)
print(" %-16s OK" % name)
except UcError as e:
print(" %-16s ERROR: %s" % (name, e))
Output:
ld $v1, 0($s0) OK
ll $v1, 0($s0) ERROR: Unhandled CPU exception (UC_ERR_EXCEPTION)
Root cause: the ll/lld helpers (HELPER_LD_ATOMIC in qemu/target/mips/op_helper.c) perform the load via the soft-TLB (which honors the virtual TLB) but additionally call do_translate_address() to populate CP0_LLAddr. That walks the MIPS segments via get_physical_address() and returns TLBRET_BADADDR for an address outside useg (UX unset), raising EXCP_AdEL and bypassing the virtual TLB. sc/scd are unaffected (they use tcg_gen_atomic_cmpxchg_tl, which never calls do_translate_address).
Fix + regression test ready; PR to follow.
Under
UC_TLB_VIRTUAL, MIPS Load-Linked (ll/lld) raise a spurious Address Error (EXCP_AdEL, intno 12) for addresses at/above the useg limit (>= 0x80000000), even though an ordinary load (ld/lw) at the same address goes through theUC_HOOK_TLB_FILLhook and succeeds. This breaks emulating MIPS64 code that takes a lock above 2 GB (e.g. glibc).Reproduced on 2.1.3 and current
dev; MIPS64 BE+LE, independent of CPU model.Output:
Root cause: the
ll/lldhelpers (HELPER_LD_ATOMICinqemu/target/mips/op_helper.c) perform the load via the soft-TLB (which honors the virtual TLB) but additionally calldo_translate_address()to populateCP0_LLAddr. That walks the MIPS segments viaget_physical_address()and returnsTLBRET_BADADDRfor an address outside useg (UX unset), raisingEXCP_AdELand bypassing the virtual TLB.sc/scdare unaffected (they usetcg_gen_atomic_cmpxchg_tl, which never callsdo_translate_address).Fix + regression test ready; PR to follow.