1#![allow(unused_macros, unused_imports)]
2use crate::prelude::*;
3use crate::regs::Reg::{self, *};
4
5use crate::mem::{virtual_memory_read, virtual_memory_write};
6use crate::regs;
7
8use std::convert::TryInto;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11static IS_SYSENTER: AtomicBool = AtomicBool::new(false);
12
13#[allow(dead_code)]
14pub(crate) fn set_is_sysenter(is_sysenter: bool) {
15 IS_SYSENTER.store(is_sysenter, Ordering::SeqCst);
16}
17
18fn is_sysenter() -> bool {
19 IS_SYSENTER.load(Ordering::SeqCst)
20}
21
22struct Stack;
23
24pub mod syscall {
25 use super::*;
26
27 macro_rules! reg_or_stack {
28 (Stack) => {
29 StorageLocation::StackOffset(0)
30 };
31 ($reg:ident) => {
32 StorageLocation::Reg($reg)
33 };
34 }
35
36 macro_rules! syscall_regs {
37 {
38 const { $syscall_args:ident, $syscall_ret:ident, $syscall_num_reg:ident, $syscall_args_len:ident };
39 $(
40 #[cfg($(arch = $arch:literal),+)] {
41 args$(: $size:literal)? = [$( $args:ident $(@ $offset:literal)? ),*];
42 return = $ret:ident;
43 syscall_number = $sys_num:ident;
44 }
45 )*
46 } => {
47 $(
48 #[cfg(any($(feature = $arch),*))]
50 pub const $syscall_args_len: usize = 6 $(+ ($size - 6))?;
51
52 #[cfg(any($(feature = $arch),*))]
54 pub const $syscall_args: [StorageLocation; $syscall_args_len] = [$(
55 reg_or_stack!($args) $(.with_offset($offset))?
56 ),*];
57
58 #[cfg(any($(feature = $arch),*))]
60 pub const $syscall_ret: Reg = $ret;
61
62 #[cfg(any($(feature = $arch),*))]
64 pub const $syscall_num_reg: Reg = $sys_num;
65 )*
66 }
67 }
68
69 syscall_regs! {
70 const {SYSCALL_ARGS, SYSCALL_RET, SYSCALL_NUM_REG, SYSCALL_ARGS_LEN};
71
72 #[cfg(arch = "x86_64")] {
73 args = [RDI, RSI, RDX, R10, R8, R9];
74 return = RAX;
75 syscall_number = RAX;
76 }
77
78 #[cfg(arch = "i386")] {
79 args = [EBX, ECX @ 0x8, EDX @ 0x4, ESI, EDI, EBP @ 0x0];
80 return = EAX;
81 syscall_number = EAX;
82 }
83
84 #[cfg(arch = "arm")] {
86 args = [R0, R1, R2, R3, R4, R5];
87 return = R0;
88 syscall_number = R7;
89 }
90
91 #[cfg(arch = "aarch64")] {
92 args = [X0, X1, X2, X3, X4, X5];
93 return = X0;
94 syscall_number = X8;
95 }
96
97 #[cfg(arch = "mips", arch = "mipsel")] {
100 args: 8 = [A0, A1, A2, A3, Stack@0x10, Stack@0x14, Stack@0x18, Stack@0x1c];
105 return = V0;
106 syscall_number = V0;
107 }
108
109 #[cfg(arch = "mips64", arch = "mips64el")] {
110 args = [A0, A1, A2, A3, T0, T1];
112 return = V0;
113 syscall_number = V0;
114 }
115 }
116}
117
118#[derive(Clone, Copy, PartialEq, Debug)]
119pub enum StorageLocation {
120 Reg(Reg),
121 StackOffset(target_ulong),
122 StackReg(Reg, target_ulong),
123}
124
125impl From<Reg> for StorageLocation {
126 fn from(reg: Reg) -> Self {
127 Self::Reg(reg)
128 }
129}
130
131impl From<(Reg, target_ulong)> for StorageLocation {
132 fn from((reg, offset): (Reg, target_ulong)) -> Self {
133 Self::StackReg(reg, offset)
134 }
135}
136
137impl From<Stack> for StorageLocation {
138 fn from(_: Stack) -> Self {
139 Self::StackOffset(0)
140 }
141}
142
143const REG_SIZE: usize = std::mem::size_of::<target_ulong>();
144
145fn is_little_endian() -> bool {
146 crate::ARCH_ENDIAN == crate::enums::Endian::Little
147}
148
149impl StorageLocation {
150 #[allow(dead_code)]
151 pub(crate) const fn with_offset(self, offset: target_ulong) -> Self {
152 if let Self::Reg(reg) | Self::StackReg(reg, _) = self {
153 Self::StackReg(reg, offset)
154 } else {
155 Self::StackOffset(offset)
156 }
157 }
158
159 fn is_stack(&self) -> bool {
160 matches!(self, Self::StackOffset(_)) || is_sysenter()
161 }
162
163 pub fn read(self, cpu: &mut CPUState) -> target_ulong {
164 match self {
165 Self::StackReg(_, offset) | Self::StackOffset(offset) if self.is_stack() => {
166 let sp = regs::get_reg(cpu, regs::reg_sp());
167
168 let bytes = virtual_memory_read(cpu, sp + offset, REG_SIZE)
169 .expect("Failed to read syscall argument from stack")
170 .try_into()
171 .unwrap();
172
173 if is_little_endian() {
174 target_ulong::from_le_bytes(bytes)
175 } else {
176 target_ulong::from_be_bytes(bytes)
177 }
178 }
179 Self::StackOffset(_offset) => unreachable!(),
180 Self::Reg(reg) | Self::StackReg(reg, _) => regs::get_reg(cpu, reg),
181 }
182 }
183
184 pub fn write(self, cpu: &mut CPUState, val: target_ulong) {
185 match self {
186 Self::StackReg(reg, offset) if is_sysenter() => {
187 let sp = regs::get_reg(cpu, regs::reg_sp());
188
189 virtual_memory_write(cpu, sp + offset, &val.to_le_bytes());
190
191 #[cfg(feature = "i386")]
192 if reg == Reg::EBP {
193 return;
194 }
195
196 regs::set_reg(cpu, reg, val);
197 }
198 Self::StackOffset(offset) => {
199 let sp = regs::get_reg(cpu, regs::reg_sp());
200
201 let bytes = if is_little_endian() {
202 val.to_le_bytes()
203 } else {
204 val.to_be_bytes()
205 };
206
207 virtual_memory_write(cpu, sp + offset, &bytes);
208 }
209 Self::Reg(reg) | Self::StackReg(reg, _) => regs::set_reg(cpu, reg, val),
210 }
211 }
212}