x86emu\emulator/
fast_path.rs1use super::instruction;
7use crate::Cpu;
8use crate::emulator::arith::ArithOp;
9use crate::emulator::arith::OrOp;
10use crate::registers::Bitness;
11use crate::registers::Segment;
12use crate::registers::bitness;
13use iced_x86::OpKind;
14
15const PAGE_SIZE: u32 = 4096;
16
17pub fn emulate_fast_path_set_bit<T: Cpu>(instruction_bytes: &[u8], cpu: &mut T) -> Option<u32> {
34 if cpu.rflags().trap() {
35 return None;
36 }
37
38 let bitness = bitness(cpu.cr0(), cpu.efer(), cpu.segment(Segment::CS));
39 let mut decoder = iced_x86::Decoder::new(bitness.into(), instruction_bytes, 0);
40 decoder.set_ip(cpu.rip());
41
42 let instr = decoder.decode();
43 let mut rflags = cpu.rflags();
44 let (address, bit) = match instr.code() {
45 iced_x86::Code::Bts_rm64_r64 | iced_x86::Code::Bts_rm32_r32
49 if instr.op0_kind() == OpKind::Memory =>
50 {
51 let op_size = instr.memory_size().size() as u8 as i64;
52
53 let bit_offset = cpu.gp_sign_extend(instr.op1_register().into());
55
56 let address_size = instruction::address_size(&instr);
57
58 let bit_base = instruction::memory_op_offset(cpu, &instr, 0);
59 let address_mask = u64::MAX >> (64 - address_size * 8);
60 let address = bit_base
61 .wrapping_add_signed(op_size * bit_offset.div_euclid(op_size * 8))
62 & address_mask;
63
64 let bit = bit_offset.rem_euclid(op_size * 8) as u32;
65
66 rflags.set_carry(false);
67 (address, bit)
68 }
69 iced_x86::Code::Or_rm32_r32
73 | iced_x86::Code::Or_rm64_r64
74 | iced_x86::Code::Or_rm8_r8
75 | iced_x86::Code::Or_rm16_r16
76 if instr.op0_kind() == OpKind::Memory =>
77 {
78 let address = instruction::memory_op_offset(cpu, &instr, 0);
79 let mask = cpu.gp(instr.op1_register().into());
80 if !mask.is_power_of_two() {
81 tracing::debug!(mask, "fast path set bit: or without exactly one bit");
82 return None;
83 }
84 OrOp::update_flags(&mut rflags, instr.memory_size().size(), mask, 0, mask);
85 (address, mask.trailing_zeros())
86 }
87 iced_x86::Code::INVALID => {
88 tracing::debug!(error = ?decoder.last_error(), "fast path set bit decode failure");
89 return None;
90 }
91 _ => {
92 tracing::debug!(bytes = ?instruction_bytes[..instr.len()], "unsupported instruction for fast path set bit");
93 return None;
94 }
95 };
96
97 let seg = cpu.segment(instr.memory_segment().into());
98 let offset = page_offset(address.wrapping_add(seg.base));
99
100 if offset > PAGE_SIZE - instr.memory_size().size() as u32 {
102 return None;
103 }
104
105 if matches!(bitness, Bitness::Bit32 | Bitness::Bit16)
108 && page_offset(seg.base | seg.limit.wrapping_add(1) as u64) != 0
109 {
110 return None;
111 }
112
113 let bit_in_page = offset * 8 + bit;
114 tracing::trace!(bit_in_page, "fast path set bit");
115
116 cpu.set_rip(instr.next_ip());
117 cpu.set_rflags(rflags);
118 Some(bit_in_page)
119}
120
121fn page_offset(address: u64) -> u32 {
122 address as u32 & (PAGE_SIZE - 1)
123}