Most of this should probably be implemented as part of the SLEIGH specification instead.
(vm: &mut Vm, nvic_changed_hook: pcode::HookId)
| 51 | |
| 52 | /// Most of this should probably be implemented as part of the SLEIGH specification instead. |
| 53 | pub(crate) fn add_arm_extras(vm: &mut Vm, nvic_changed_hook: pcode::HookId) { |
| 54 | const CPSR_IRQ_MASK_BIT: u32 = 1 << 7; |
| 55 | const CPSR_FIQ_MASK_BIT: u32 = 1 << 6; |
| 56 | |
| 57 | let xpsr_reg = vm.cpu.arch.sleigh.add_custom_reg("xpsr", 4).unwrap(); |
| 58 | let xpsr_handler = XpsrHandler::new(&mut vm.cpu); |
| 59 | vm.cpu.add_reg_handler(xpsr_reg.id, Box::new(xpsr_handler)); |
| 60 | |
| 61 | let basepri_reg = vm.cpu.arch.sleigh.add_custom_reg("basepri", 4).unwrap(); |
| 62 | let primask_reg = vm.cpu.arch.sleigh.add_custom_reg("primask", 4).unwrap(); |
| 63 | |
| 64 | vm.add_op_injector( |
| 65 | "getCurrentExceptionNumber", |
| 66 | move |_: &Arch, _, _, output: pcode::VarNode, b: &mut BlockState| { |
| 67 | b.pcode.push((output, Op::IntAnd, (xpsr_reg, 0x1ff_u32))); |
| 68 | false |
| 69 | }, |
| 70 | ); |
| 71 | |
| 72 | vm.add_op_injector("enableIRQinterrupts", move |_: &Arch, _, _, _, b: &mut BlockState| { |
| 73 | b.pcode.push((primask_reg, Op::IntAnd, (primask_reg, !CPSR_IRQ_MASK_BIT))); |
| 74 | b.pcode.push(pcode::Op::Hook(nvic_changed_hook)); |
| 75 | // Changing this variable could cause an interrupt to be triggered, so terminate the |
| 76 | // block here. |
| 77 | true |
| 78 | }); |
| 79 | |
| 80 | vm.add_op_injector("disableIRQinterrupts", move |_: &Arch, _, _, _, b: &mut BlockState| { |
| 81 | b.pcode.push((primask_reg, Op::IntOr, (primask_reg, CPSR_IRQ_MASK_BIT))); |
| 82 | b.pcode.push(pcode::Op::Hook(nvic_changed_hook)); |
| 83 | false |
| 84 | }); |
| 85 | |
| 86 | vm.add_op_injector("enableFIQinterrupts", move |_: &Arch, _, _, _, b: &mut BlockState| { |
| 87 | b.pcode.push((primask_reg, Op::IntAnd, (primask_reg, !CPSR_FIQ_MASK_BIT))); |
| 88 | b.pcode.push(pcode::Op::Hook(nvic_changed_hook)); |
| 89 | // Changing this variable could cause an interrupt to be triggered, so terminate the |
| 90 | // block here. |
| 91 | true |
| 92 | }); |
| 93 | |
| 94 | vm.add_op_injector("disableFIQinterrupts", move |_: &Arch, _, _, _, b: &mut BlockState| { |
| 95 | b.pcode.push((primask_reg, Op::IntOr, (primask_reg, CPSR_FIQ_MASK_BIT))); |
| 96 | b.pcode.push(pcode::Op::Hook(nvic_changed_hook)); |
| 97 | false |
| 98 | }); |
| 99 | |
| 100 | vm.add_op_injector( |
| 101 | "isIRQinterruptsEnabled", |
| 102 | move |_: &Arch, _, _, output, b: &mut BlockState| { |
| 103 | let shift = 31 - CPSR_IRQ_MASK_BIT.leading_zeros(); |
| 104 | b.pcode.push((output, Op::IntRight, (primask_reg, shift))); |
| 105 | // Even though the p-code operation is called "isIRQinterruptsEnabled" it is intended to |
| 106 | // returns the value of `PRIMASK` directly (which is 0 when interrupts are enabled, and |
| 107 | // 1 when they are enabled). |
| 108 | // b.pcode.push((output, Op::IntNot, output)); |
| 109 | b.pcode.push((output, Op::IntAnd, (output, 1))); |
| 110 | false |