Add mrc and movt
* Add MOVT instruction * Add new ARM operand types for MRC * mrc implementation seems to work * UXTB inst implemented, provisional implementations of LDREX and STREX * Added unit tests for MRC, MOVT, and UXTB * Added mode swapping from blx instruction on arm cpu * Make tests pass
This commit is contained in:
parent
5ea4a87ddb
commit
7f550becc6
@ -55,7 +55,9 @@ class Armv7Operand(Operand):
|
|||||||
def type(self):
|
def type(self):
|
||||||
type_map = { ARM_OP_REG: 'register',
|
type_map = { ARM_OP_REG: 'register',
|
||||||
ARM_OP_MEM: 'memory',
|
ARM_OP_MEM: 'memory',
|
||||||
ARM_OP_IMM: 'immediate'}
|
ARM_OP_IMM: 'immediate',
|
||||||
|
ARM_OP_PIMM:'coprocessor',
|
||||||
|
ARM_OP_CIMM:'immediate'}
|
||||||
|
|
||||||
return type_map[self.op.type]
|
return type_map[self.op.type]
|
||||||
|
|
||||||
@ -90,7 +92,9 @@ class Armv7Operand(Operand):
|
|||||||
if withCarry:
|
if withCarry:
|
||||||
return imm, self._getExpandImmCarry(carry)
|
return imm, self._getExpandImmCarry(carry)
|
||||||
return imm
|
return imm
|
||||||
|
elif self.type == 'coprocessor':
|
||||||
|
imm = self.op.imm
|
||||||
|
return imm
|
||||||
elif self.type == 'memory':
|
elif self.type == 'memory':
|
||||||
val = self.cpu.read_int(self.address(), nbits)
|
val = self.cpu.read_int(self.address(), nbits)
|
||||||
if withCarry:
|
if withCarry:
|
||||||
@ -190,6 +194,9 @@ class Armv7RegisterFile(RegisterFile):
|
|||||||
self._regs['APSR_C'] = Register(1)
|
self._regs['APSR_C'] = Register(1)
|
||||||
self._regs['APSR_V'] = Register(1)
|
self._regs['APSR_V'] = Register(1)
|
||||||
|
|
||||||
|
#MMU Coprocessor -- to support MCR/MRC for TLS
|
||||||
|
self._regs['P15_C13'] = Register(32)
|
||||||
|
|
||||||
def _read_APSR(self):
|
def _read_APSR(self):
|
||||||
def make_apsr_flag(flag_expr, offset):
|
def make_apsr_flag(flag_expr, offset):
|
||||||
'Helper for constructing an expression for the APSR register'
|
'Helper for constructing an expression for the APSR register'
|
||||||
@ -246,7 +253,8 @@ class Armv7RegisterFile(RegisterFile):
|
|||||||
return super(Armv7RegisterFile, self).all_registers + \
|
return super(Armv7RegisterFile, self).all_registers + \
|
||||||
('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','D0','D1','D2',
|
('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','D0','D1','D2',
|
||||||
'D3','D4','D5','D6','D7','D8','D9','D10','D11','D12','D13','D14','D15','D16','D17','D18','D19','D20',
|
'D3','D4','D5','D6','D7','D8','D9','D10','D11','D12','D13','D14','D15','D16','D17','D18','D19','D20',
|
||||||
'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR','APSR_N','APSR_Z','APSR_C','APSR_V')
|
'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR','APSR_N','APSR_Z','APSR_C','APSR_V',
|
||||||
|
'P15_C13')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def canonical_registers(self):
|
def canonical_registers(self):
|
||||||
@ -322,6 +330,11 @@ class Armv7Cpu(Cpu):
|
|||||||
self._last_flags = state['_last_flags']
|
self._last_flags = state['_last_flags']
|
||||||
self._force_next = state['_force_next']
|
self._force_next = state['_force_next']
|
||||||
|
|
||||||
|
def _set_mode(self, new_mode):
|
||||||
|
assert new_mode in (CS_MODE_ARM, CS_MODE_THUMB)
|
||||||
|
self.mode = new_mode
|
||||||
|
self._md.mode = new_mode
|
||||||
|
|
||||||
# Flags that are the result of arithmetic instructions. Unconditionally
|
# Flags that are the result of arithmetic instructions. Unconditionally
|
||||||
# set, but conditionally committed.
|
# set, but conditionally committed.
|
||||||
#
|
#
|
||||||
@ -418,6 +431,9 @@ class Armv7Cpu(Cpu):
|
|||||||
def write(self, addr, data):
|
def write(self, addr, data):
|
||||||
return self.write_bytes(addr, data)
|
return self.write_bytes(addr, data)
|
||||||
|
|
||||||
|
def set_arm_tls(self, data):
|
||||||
|
self.regfile.write('P15_C13', data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def canonicalize_instruction_name(instr):
|
def canonicalize_instruction_name(instr):
|
||||||
name = instr.insn_name().upper()
|
name = instr.insn_name().upper()
|
||||||
@ -492,6 +508,96 @@ class Armv7Cpu(Cpu):
|
|||||||
dest.write(result)
|
dest.write(result)
|
||||||
cpu.setFlags(C=carry_out, N=HighBit(result), Z=(result == 0))
|
cpu.setFlags(C=carry_out, N=HighBit(result), Z=(result == 0))
|
||||||
|
|
||||||
|
@instruction
|
||||||
|
def MOVT(cpu, dest, src):
|
||||||
|
'''
|
||||||
|
MOVT writes imm16 to Rd[31:16]. The write does not affect Rd[15:0].
|
||||||
|
|
||||||
|
:param Armv7Operand dest: The destination operand; register
|
||||||
|
:param Armv7Operand src: The source operand; 16-bit immediate
|
||||||
|
'''
|
||||||
|
assert src.type == 'immediate'
|
||||||
|
imm = src.read()
|
||||||
|
low_halfword = dest.read() & Mask(16)
|
||||||
|
dest.write((imm << 16) | low_halfword)
|
||||||
|
|
||||||
|
@instruction
|
||||||
|
def MRC(cpu, coprocessor, opcode1, dest, coprocessor_reg_n, coprocessor_reg_m, opcode2):
|
||||||
|
'''
|
||||||
|
MRC moves to ARM register from coprocessor.
|
||||||
|
|
||||||
|
:param Armv7Operand coprocessor: The name of the coprocessor; immediate
|
||||||
|
:param Armv7Operand opcode1: coprocessor specific opcode; 3-bit immediate
|
||||||
|
:param Armv7Operand dest: the destination operand: register
|
||||||
|
:param Armv7Operand coprocessor_reg_n: the coprocessor register; immediate
|
||||||
|
:param Armv7Operand coprocessor_reg_m: the coprocessor register; immediate
|
||||||
|
:param Armv7Operand opcode2: coprocessor specific opcode; 3-bit immediate
|
||||||
|
'''
|
||||||
|
assert coprocessor.type == 'coprocessor'
|
||||||
|
assert opcode1.type == 'immediate'
|
||||||
|
assert opcode2.type == 'immediate'
|
||||||
|
assert dest.type == 'register'
|
||||||
|
imm_coprocessor = coprocessor.read()
|
||||||
|
imm_opcode1 = opcode1.read()
|
||||||
|
imm_opcode2 = opcode2.read()
|
||||||
|
coprocessor_n_name = coprocessor_reg_n.read()
|
||||||
|
coprocessor_m_name = coprocessor_reg_m.read()
|
||||||
|
|
||||||
|
if 15 == imm_coprocessor: #MMU
|
||||||
|
if 0 == imm_opcode1:
|
||||||
|
if 13 == coprocessor_n_name:
|
||||||
|
if 3 == imm_opcode2:
|
||||||
|
dest.write(cpu.regfile.read('P15_C13'))
|
||||||
|
return
|
||||||
|
raise NotImplementedError("MRC: unimplemented combination of coprocessor, opcode, and coprocessor register")
|
||||||
|
|
||||||
|
@instruction
|
||||||
|
def LDREX(cpu, dest, src, offset=None):
|
||||||
|
'''
|
||||||
|
LDREX loads data from memory.
|
||||||
|
* If the physical address has the shared TLB attribute, LDREX
|
||||||
|
tags the physical address as exclusive access for the current
|
||||||
|
processor, and clears any exclusive access tag for this
|
||||||
|
processor for any other physical address.
|
||||||
|
* Otherwise, it tags the fact that the executing processor has
|
||||||
|
an outstanding tagged physical address.
|
||||||
|
|
||||||
|
:param Armv7Operand dest: the destination register; register
|
||||||
|
:param Armv7Operand src: the source operand: register
|
||||||
|
'''
|
||||||
|
#TODO: add lock mechanism to underlying memory --GR, 2017-06-06
|
||||||
|
cpu._LDR(dest, src, 32, False, offset)
|
||||||
|
|
||||||
|
@instruction
|
||||||
|
def STREX(cpu, status, *args):
|
||||||
|
'''
|
||||||
|
STREX performs a conditional store to memory.
|
||||||
|
:param Armv7Operand status: the destination register for the returned status; register
|
||||||
|
'''
|
||||||
|
#TODO: implement conditional return with appropriate status --GR, 2017-06-06
|
||||||
|
status.write(0)
|
||||||
|
return cpu._STR(cpu.address_bit_size, *args)
|
||||||
|
|
||||||
|
@instruction
|
||||||
|
def UXTB(cpu, dest, src):
|
||||||
|
'''
|
||||||
|
UXTB extracts an 8-bit value from a register, zero-extends
|
||||||
|
it to the size of the register, and writes the result to the destination register.
|
||||||
|
|
||||||
|
:param ARMv7Operand dest: the destination register; register
|
||||||
|
:param ARMv7Operand dest: the source register; register
|
||||||
|
'''
|
||||||
|
val = GetNBits(src.read(), 8)
|
||||||
|
word = Operators.ZEXTEND(val, cpu.address_bit_size)
|
||||||
|
dest.write(word)
|
||||||
|
|
||||||
|
@instruction
|
||||||
|
def PLD(cpu, addr, offset=None):
|
||||||
|
'''
|
||||||
|
PLD instructs the cpu that the address at addr might be loaded soon.
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
def _compute_writeback(cpu, operand, offset):
|
def _compute_writeback(cpu, operand, offset):
|
||||||
if offset:
|
if offset:
|
||||||
off = offset.read()
|
off = offset.read()
|
||||||
@ -645,11 +751,22 @@ class Armv7Cpu(Cpu):
|
|||||||
## 2 and LSB of LR set, but we currently do not distinguish between
|
## 2 and LSB of LR set, but we currently do not distinguish between
|
||||||
## THUMB and regular modes, so we use the addresses as is. TODO: Handle
|
## THUMB and regular modes, so we use the addresses as is. TODO: Handle
|
||||||
## thumb correctly and fix this
|
## thumb correctly and fix this
|
||||||
|
address = cpu.PC
|
||||||
target = dest.read()
|
target = dest.read()
|
||||||
next_instr_addr = cpu.regfile.read('PC') #- 2
|
next_instr_addr = cpu.regfile.read('PC') #- 2
|
||||||
cpu.regfile.write('LR', next_instr_addr) # | 1)
|
cpu.regfile.write('LR', next_instr_addr) # | 1)
|
||||||
cpu.regfile.write('PC', target & ~1)
|
cpu.regfile.write('PC', target & ~1)
|
||||||
|
|
||||||
|
## The `blx <label>` form of this instruction forces a state swap
|
||||||
|
if dest.type=='immediate':
|
||||||
|
logger.debug("swapping ds mode due to BLX at inst 0x{:x}".format(address))
|
||||||
|
#swap from arm to thumb or back
|
||||||
|
assert cpu.mode in (CS_MODE_ARM, CS_MODE_THUMB)
|
||||||
|
if cpu.mode == CS_MODE_ARM:
|
||||||
|
cpu._set_mode(CS_MODE_THUMB)
|
||||||
|
else:
|
||||||
|
cpu._set_mode(CS_MODE_ARM)
|
||||||
|
|
||||||
@instruction
|
@instruction
|
||||||
def CMP(cpu, reg, cmp):
|
def CMP(cpu, reg, cmp):
|
||||||
notcmp = ~cmp.read() & Mask(cpu.address_bit_size)
|
notcmp = ~cmp.read() & Mask(cpu.address_bit_size)
|
||||||
|
|||||||
@ -1244,6 +1244,7 @@ class Linux(Platform):
|
|||||||
def sys_ARM_NR_set_tls(self, val):
|
def sys_ARM_NR_set_tls(self, val):
|
||||||
if hasattr(self, '_arm_tls_memory'):
|
if hasattr(self, '_arm_tls_memory'):
|
||||||
self.current.write_int(self._arm_tls_memory, val)
|
self.current.write_int(self._arm_tls_memory, val)
|
||||||
|
self.current.set_arm_tls(val)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
#Signals..
|
#Signals..
|
||||||
|
|||||||
@ -41,7 +41,10 @@ class UnicornEmulator(object):
|
|||||||
|
|
||||||
def _unicorn(self):
|
def _unicorn(self):
|
||||||
if self._cpu.arch == CS_ARCH_ARM:
|
if self._cpu.arch == CS_ARCH_ARM:
|
||||||
|
if self._cpu.mode == CS_MODE_ARM:
|
||||||
return Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
return Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||||
|
elif self._cpu.mode == CS_MODE_THUMB:
|
||||||
|
return Uc(UC_ARCH_ARM, UC_MODE_THUMB)
|
||||||
elif self._cpu.arch == CS_ARCH_X86:
|
elif self._cpu.arch == CS_ARCH_X86:
|
||||||
if self._cpu.mode == CS_MODE_32:
|
if self._cpu.mode == CS_MODE_32:
|
||||||
return Uc(UC_ARCH_X86, UC_MODE_32)
|
return Uc(UC_ARCH_X86, UC_MODE_32)
|
||||||
|
|||||||
@ -1390,3 +1390,21 @@ class Armv7CpuInstructions(unittest.TestCase):
|
|||||||
self.assertEqual(self.cpu.D9, 21)
|
self.assertEqual(self.cpu.D9, 21)
|
||||||
self.assertEqual(self.cpu.D10, 20)
|
self.assertEqual(self.cpu.D10, 20)
|
||||||
self.assertEqual(self.cpu.R1, pre)
|
self.assertEqual(self.cpu.R1, pre)
|
||||||
|
|
||||||
|
@itest_setregs("R3=3")
|
||||||
|
@itest("movt R3, #9")
|
||||||
|
def test_movt(self):
|
||||||
|
self.assertEqual(self.cpu.R3, 0x90003)
|
||||||
|
|
||||||
|
@itest_custom("mrc p15, #0, r2, c13, c0, #3")
|
||||||
|
def test_mrc(self):
|
||||||
|
self.cpu.set_arm_tls(0x55555)
|
||||||
|
self.cpu.write_register('R2', 0)
|
||||||
|
self.cpu.execute()
|
||||||
|
self.assertEqual(self.cpu.R2, 0x55555)
|
||||||
|
|
||||||
|
@itest_setregs("R1=0x45", "R2=0x55555555")
|
||||||
|
@itest("uxtb r1, r2")
|
||||||
|
def test_uxtb(self):
|
||||||
|
self.assertEqual(self.cpu.R2, 0x55555555)
|
||||||
|
self.assertEqual(self.cpu.R1, 0x55)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user