Skip to content

Commit ab51ee6

Browse files
committed
Add support of Cortex-M
1 parent 41459c5 commit ab51ee6

File tree

2 files changed

+163
-1
lines changed

2 files changed

+163
-1
lines changed

qiling/arch/arm_const.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#
55

66
from unicorn.arm_const import *
7+
from enum import IntEnum
78

89
reg_map = {
910
"r0": UC_ARM_REG_R0,
@@ -22,6 +23,18 @@
2223
"sp": UC_ARM_REG_SP,
2324
"lr": UC_ARM_REG_LR,
2425
"pc": UC_ARM_REG_PC,
26+
# cortex-M Special Register
27+
"msp": UC_ARM_REG_MSP,
28+
"psp": UC_ARM_REG_PSP,
29+
"xpsr": UC_ARM_REG_XPSR,
30+
"xpsr_nzcvqg": UC_ARM_REG_XPSR_NZCVQG,
31+
"apsr": UC_ARM_REG_APSR,
32+
"ipsr": UC_ARM_REG_IPSR,
33+
"epsr": UC_ARM_REG_EPSR,
34+
"primask": UC_ARM_REG_PRIMASK,
35+
"faultmask": UC_ARM_REG_FAULTMASK,
36+
"basepri": UC_ARM_REG_BASEPRI,
37+
"control": UC_ARM_REG_CONTROL,
2538
# CPSR needs to be at offset 25 for GDB, see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l34
2639
# The fp registers inbetween have become obsolete
2740
"f0": UC_ARM_REG_INVALID,
@@ -37,4 +50,28 @@
3750
"c1_c0_2": UC_ARM_REG_C1_C0_2,
3851
"c13_c0_3": UC_ARM_REG_C13_C0_3,
3952
"fpexc": UC_ARM_REG_FPEXC,
40-
}
53+
}
54+
55+
class IRQ(IntEnum):
56+
NMI = -14
57+
HARD_FAULT = -13
58+
MEMORY_MANAGEMENT_FAULT = -12
59+
BUS_FAULT = -11
60+
USAGE_FAULT = -10
61+
SVCALL = -5
62+
PENDSV = -2
63+
SYSTICK = -1
64+
65+
class CONTROL(IntEnum):
66+
FPCA = 0b100
67+
SPSEL = 0b010
68+
PRIV = 0b001
69+
70+
class EXC_RETURN(IntEnum):
71+
RETURN_SP = 0b0100
72+
RETURN_MODE = 0b1000
73+
74+
class EXCP(IntEnum):
75+
SWI = 2 # software interrupt
76+
EXCEPTION_EXIT = 8 # Return from v7M exception
77+

qiling/arch/cortex_m.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
#
5+
6+
from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM, UC_MODE_MCLASS, UC_MODE_THUMB
7+
from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_MCLASS, CS_MODE_THUMB
8+
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM, KS_MODE_THUMB
9+
10+
from qiling.const import QL_VERBOSE
11+
from qiling.exception import QlErrorNotImplemented
12+
13+
from .arm import QlArchARM
14+
from .arm_const import IRQ, EXC_RETURN, CONTROL, EXCP
15+
16+
17+
class QlArchCORTEX_M(QlArchARM):
18+
def __init__(self, ql):
19+
super().__init__(ql)
20+
21+
self.reg_context = ['xpsr', 'pc', 'lr', 'r12', 'r3', 'r2', 'r1', 'r0']
22+
23+
def intr_cb(ql, intno):
24+
if intno == EXCP.SWI:
25+
ql.hw.nvic.set_pending(IRQ.SVCALL)
26+
27+
elif intno == EXCP.EXCEPTION_EXIT:
28+
ql.emu_stop()
29+
30+
else:
31+
raise QlErrorNotImplemented(f'Unhandled interrupt number ({intno})')
32+
33+
self.intr_cb = intr_cb
34+
35+
def get_init_uc(self):
36+
return Uc(UC_ARCH_ARM, UC_MODE_ARM + UC_MODE_MCLASS + UC_MODE_THUMB)
37+
38+
def create_disassembler(self) -> Cs:
39+
return Cs(CS_ARCH_ARM, CS_MODE_ARM + CS_MODE_MCLASS + CS_MODE_THUMB)
40+
41+
def create_assembler(self) -> Ks:
42+
return Ks(KS_ARCH_ARM, KS_MODE_ARM + KS_MODE_THUMB)
43+
44+
def check_thumb(self):
45+
return UC_MODE_THUMB
46+
47+
def step(self):
48+
self.ql.emu_start(self.get_pc(), 0, count=1)
49+
self.ql.hw.step()
50+
51+
def run(self, count=-1, end=None):
52+
if type(end) is int:
53+
end |= 1
54+
55+
while count != 0:
56+
if self.get_pc() == end:
57+
break
58+
59+
self.step()
60+
count -= 1
61+
62+
def is_handler_mode(self):
63+
return self.ql.reg.read('ipsr') > 1
64+
65+
def using_psp(self):
66+
return not self.is_handler_mode() and (self.ql.reg.read('control') & CONTROL.SPSEL) > 0
67+
68+
def enter_intr(self):
69+
# Save Stack
70+
for reg in self.reg_context:
71+
val = self.ql.reg.read(reg)
72+
self.ql.arch.stack_push(val)
73+
74+
if self.ql.verbose >= QL_VERBOSE.DISASM:
75+
self.ql.log.info(f'Enter into interrupt')
76+
77+
def exit_intr(self):
78+
# Exit handler mode
79+
self.ql.reg.write('ipsr', 0)
80+
81+
# switch the stack accroding exc_return
82+
old_ctrl = self.ql.reg.read('control')
83+
if self.ql.arch.get_pc() & EXC_RETURN.RETURN_SP:
84+
self.ql.reg.write('control', old_ctrl | CONTROL.SPSEL)
85+
else:
86+
self.ql.reg.write('control', old_ctrl & ~CONTROL.SPSEL)
87+
88+
# Restore stack
89+
for reg in reversed(self.reg_context):
90+
val = self.ql.arch.stack_pop()
91+
if reg == 'xpsr':
92+
self.ql.reg.write('XPSR_NZCVQG', val)
93+
else:
94+
self.ql.reg.write(reg, val)
95+
96+
if self.ql.verbose >= QL_VERBOSE.DISASM:
97+
self.ql.log.info('Exit from interrupt')
98+
99+
def handle_interupt(self, IRQn):
100+
if IRQn > IRQ.HARD_FAULT and (self.ql.reg.read('primask') & 0x1):
101+
return
102+
103+
if IRQn != IRQ.NMI and (self.ql.reg.read('faultmask') & 0x1):
104+
return
105+
106+
basepri = self.ql.reg.read('basepri') & 0xf0
107+
if basepri != 0 and basepri <= self.ql.hw.nvic.get_priority(IRQn):
108+
return
109+
110+
if self.ql.verbose >= QL_VERBOSE.DISASM:
111+
self.ql.log.info(f'Handle the IRQn: {IRQn}')
112+
113+
isr = IRQn + 16
114+
offset = isr << 2
115+
116+
entry = self.ql.mem.read_ptr(offset)
117+
exc_return = 0xFFFFFFFD if self.ql.arch.using_psp() else 0xFFFFFFF9
118+
119+
# Enter handler mode
120+
self.ql.reg.write('ipsr', isr)
121+
122+
self.ql.reg.write('pc', entry)
123+
self.ql.reg.write('lr', exc_return)
124+
125+
self.ql.emu_start(self.ql.arch.get_pc(), 0)

0 commit comments

Comments
 (0)