1use super::HypercallIo;
7use crate::support::AsHandler;
8
9pub struct X64RegisterIo<T> {
11 inner: T,
12 is_64bit: bool,
13 advance_ip: bool,
14}
15
16impl<T: X64RegisterState> X64RegisterIo<T> {
17 pub fn new(t: T, is_64bit: bool, advance_ip: bool) -> Self {
23 Self {
24 inner: t,
25 is_64bit,
26 advance_ip,
27 }
28 }
29
30 fn gp_pair(&mut self, high: X64HypercallRegister, low: X64HypercallRegister) -> u64 {
31 (self.inner.gp(high) << 32) | (self.inner.gp(low) & 0xffff_ffff)
32 }
33
34 fn mask(&self, value: u64) -> u64 {
35 value
36 & if self.is_64bit {
37 u64::MAX
38 } else {
39 u32::MAX as u64
40 }
41 }
42
43 fn set_control(&mut self, control: u64) {
44 if self.is_64bit {
45 self.inner.set_gp(X64HypercallRegister::Rcx, control);
46 } else {
47 self.inner.set_gp(X64HypercallRegister::Rdx, control >> 32);
48 self.inner
49 .set_gp(X64HypercallRegister::Rax, control & u32::MAX as u64);
50 }
51 }
52}
53
54impl<T> AsHandler<T> for X64RegisterIo<T> {
55 fn as_handler(&mut self) -> &mut T {
56 &mut self.inner
57 }
58}
59
60impl<T> AsHandler<T> for X64RegisterIo<&mut T> {
61 fn as_handler(&mut self) -> &mut T {
62 &mut *self.inner
63 }
64}
65
66impl<T: X64RegisterState> HypercallIo for X64RegisterIo<T> {
67 fn advance_ip(&mut self) {
68 if self.advance_ip {
69 let rip = self.inner.rip().wrapping_add(3);
70 self.inner.set_rip(self.mask(rip));
71 }
72 }
73
74 fn retry(&mut self, control: u64) {
75 self.set_control(control)
77
78 }
80
81 fn control(&mut self) -> u64 {
82 if self.is_64bit {
83 self.inner.gp(X64HypercallRegister::Rcx)
84 } else {
85 self.gp_pair(X64HypercallRegister::Rdx, X64HypercallRegister::Rax)
86 }
87 }
88
89 fn vtl_input(&mut self) -> u64 {
90 let name = if self.is_64bit {
91 X64HypercallRegister::Rax
92 } else {
93 X64HypercallRegister::Rcx
94 };
95
96 let value = self.inner.gp(name);
97 self.mask(value)
98 }
99
100 fn set_result(&mut self, n: u64) {
101 if self.is_64bit {
102 self.inner.set_gp(X64HypercallRegister::Rax, n);
103 } else {
104 self.inner.set_gp(X64HypercallRegister::Rdx, n >> 32);
105 self.inner
106 .set_gp(X64HypercallRegister::Rax, n & u32::MAX as u64);
107 }
108 }
109
110 fn input_gpa(&mut self) -> u64 {
111 if self.is_64bit {
112 self.inner.gp(X64HypercallRegister::Rdx)
113 } else {
114 self.gp_pair(X64HypercallRegister::Rbx, X64HypercallRegister::Rcx)
115 }
116 }
117
118 fn output_gpa(&mut self) -> u64 {
119 if self.is_64bit {
120 self.inner.gp(X64HypercallRegister::R8)
121 } else {
122 self.gp_pair(X64HypercallRegister::Rdi, X64HypercallRegister::Rsi)
123 }
124 }
125
126 fn fast_register_pair_count(&mut self) -> usize {
127 if self.is_64bit { 7 } else { 1 }
128 }
129
130 fn extended_fast_hypercalls_ok(&mut self) -> bool {
131 self.is_64bit
132 }
133
134 fn fast_input(&mut self, buf: &mut [[u64; 2]], _output_register_pairs: usize) -> usize {
135 self.fast_regs(0, buf);
136 buf.len()
137 }
138
139 fn fast_output(&mut self, starting_pair_index: usize, buf: &[[u64; 2]]) {
140 for (i, &[low, high]) in buf.iter().enumerate() {
142 let index = i + starting_pair_index;
143 if index == 0 {
144 self.inner.set_gp(X64HypercallRegister::Rdx, low);
145 self.inner.set_gp(X64HypercallRegister::R8, high);
146 } else {
147 let x = low as u128 | ((high as u128) << 64);
148 self.inner.set_xmm(index - 1, x)
149 }
150 }
151 }
152
153 fn fast_regs(&mut self, starting_pair_index: usize, buf: &mut [[u64; 2]]) {
154 if self.is_64bit {
155 for (i, [low, high]) in buf.iter_mut().enumerate() {
156 let index = i + starting_pair_index;
157 if index == 0 {
158 *low = self.inner.gp(X64HypercallRegister::Rdx);
159 *high = self.inner.gp(X64HypercallRegister::R8);
160 } else {
161 let value = self.inner.xmm(index - 1);
162 *low = value as u64;
163 *high = (value >> 64) as u64;
164 }
165 }
166 } else if let [[low, high], ..] = buf {
167 *low = self.gp_pair(X64HypercallRegister::Rbx, X64HypercallRegister::Rcx);
168 *high = self.gp_pair(X64HypercallRegister::Rdi, X64HypercallRegister::Rsi);
169 }
170 }
171}
172
173pub trait X64RegisterState {
175 fn rip(&mut self) -> u64;
177
178 fn set_rip(&mut self, rip: u64);
180
181 fn gp(&mut self, n: X64HypercallRegister) -> u64;
183
184 fn set_gp(&mut self, n: X64HypercallRegister, value: u64);
186
187 fn xmm(&mut self, n: usize) -> u128;
189
190 fn set_xmm(&mut self, n: usize, value: u128);
192}
193
194impl<T: X64RegisterState> X64RegisterState for &'_ mut T {
195 fn rip(&mut self) -> u64 {
196 (**self).rip()
197 }
198
199 fn set_rip(&mut self, rip: u64) {
200 (**self).set_rip(rip)
201 }
202
203 fn gp(&mut self, n: X64HypercallRegister) -> u64 {
204 (**self).gp(n)
205 }
206
207 fn set_gp(&mut self, n: X64HypercallRegister, value: u64) {
208 (**self).set_gp(n, value)
209 }
210
211 fn xmm(&mut self, n: usize) -> u128 {
212 (**self).xmm(n)
213 }
214
215 fn set_xmm(&mut self, n: usize, value: u128) {
216 (**self).set_xmm(n, value)
217 }
218}
219
220pub enum X64HypercallRegister {
224 Rax = 0,
226 Rcx = 1,
228 Rdx = 2,
230 Rbx = 3,
232 Rsi = 6,
234 Rdi = 7,
236 R8 = 8,
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use crate::tests::TestHypercallIo;
244 use crate::tests::TestRegisterState;
245 use test_with_tracing::test;
246
247 struct NoRipState {
248 gp: [u64; 9],
249 }
250
251 impl X64RegisterState for NoRipState {
252 fn rip(&mut self) -> u64 {
253 panic!("RIP must not be read")
254 }
255
256 fn set_rip(&mut self, _rip: u64) {
257 panic!("RIP must not be written")
258 }
259
260 fn gp(&mut self, n: X64HypercallRegister) -> u64 {
261 self.gp[n as usize]
262 }
263
264 fn set_gp(&mut self, n: X64HypercallRegister, value: u64) {
265 self.gp[n as usize] = value;
266 }
267
268 fn xmm(&mut self, _n: usize) -> u128 {
269 0
270 }
271
272 fn set_xmm(&mut self, _n: usize, _value: u128) {}
273 }
274
275 #[test]
276 fn no_ip_advance_does_not_access_rip() {
277 let mut io = X64RegisterIo::new(NoRipState { gp: [0; 9] }, true, false);
278
279 io.advance_ip();
280 io.retry(0x1234);
281
282 assert_eq!(io.control(), 0x1234);
283 }
284
285 impl<T: X64RegisterState + TestRegisterState> TestHypercallIo for X64RegisterIo<T> {
287 fn get_result(&mut self) -> u64 {
288 if self.is_64bit {
289 self.inner.gp(X64HypercallRegister::Rax)
290 } else {
291 self.gp_pair(X64HypercallRegister::Rdx, X64HypercallRegister::Rax)
292 }
293 }
294
295 fn set_control(&mut self, control: u64) {
296 X64RegisterIo::set_control(self, control);
297 }
298
299 fn set_input_gpa(&mut self, gpa: u64) {
300 if self.is_64bit {
301 self.inner.set_gp(X64HypercallRegister::Rdx, gpa);
302 } else {
303 self.inner.set_gp(X64HypercallRegister::Rbx, gpa >> 32);
304 self.inner
305 .set_gp(X64HypercallRegister::Rcx, gpa & u32::MAX as u64);
306 }
307 }
308
309 fn set_output_gpa(&mut self, gpa: u64) {
310 if self.is_64bit {
311 self.inner.set_gp(X64HypercallRegister::R8, gpa);
312 } else {
313 self.inner.set_gp(X64HypercallRegister::Rdi, gpa >> 32);
314 self.inner
315 .set_gp(X64HypercallRegister::Rsi, gpa & u32::MAX as u64);
316 }
317 }
318
319 fn set_fast_input(&mut self, buf: &[[u64; 2]]) {
320 if self.is_64bit {
321 let (gp, xmm) = buf.split_at(1);
322 let rdx = gp[0][0];
323 let r8 = gp[0][1];
324 self.inner.set_gp(X64HypercallRegister::Rdx, rdx);
325 self.inner.set_gp(X64HypercallRegister::R8, r8);
326 for (i, [low, high]) in xmm.iter().enumerate() {
327 let value = *low as u128 | ((*high as u128) << 64);
328 self.inner.set_xmm(i, value);
329 }
330 } else {
331 let [low, high] = buf[0];
332 self.inner.set_gp(X64HypercallRegister::Rbx, low >> 32);
333 self.inner
334 .set_gp(X64HypercallRegister::Rcx, low & u32::MAX as u64);
335 self.inner.set_gp(X64HypercallRegister::Rdi, high >> 32);
336 self.inner
337 .set_gp(X64HypercallRegister::Rsi, high & u32::MAX as u64);
338 }
339 }
340
341 fn get_fast_output(&mut self, input_register_pairs: usize, buf: &mut [[u64; 2]]) {
342 for (i, [low, high]) in buf.iter_mut().enumerate() {
344 if i + input_register_pairs == 0 {
345 *low = self.inner.gp(X64HypercallRegister::Rdx);
346 *high = self.inner.gp(X64HypercallRegister::R8);
347 } else {
348 let x = self.inner.xmm(i + input_register_pairs - 1);
349 *low = x as u64;
350 *high = (x >> 64) as u64;
351 }
352 }
353 }
354
355 fn get_modified_mask(&self) -> u64 {
356 self.inner.get_modified_mask()
357 }
358
359 fn clear_modified_mask(&mut self) {
360 self.inner.clear_modified_mask()
361 }
362
363 fn get_io_register_mask(&self) -> u64 {
364 if self.is_64bit {
365 1u64 << X64HypercallRegister::Rcx as usize
366 | 1u64 << X64HypercallRegister::Rax as usize
367 } else {
368 1u64 << X64HypercallRegister::Rdx as usize
369 | 1u64 << X64HypercallRegister::Rax as usize
370 }
371 }
372
373 fn get_name(&self) -> String {
374 format!("x86_{}", if self.is_64bit { "64" } else { "32" })
375 }
376
377 fn set_vtl_input(&mut self, vtl_input: u64) {
378 if self.is_64bit {
379 self.inner.set_gp(X64HypercallRegister::Rax, vtl_input);
380 } else {
381 self.inner
382 .set_gp(X64HypercallRegister::Rcx, vtl_input >> 32);
383 }
384 }
385
386 fn auto_advance_ip(&mut self) {}
387 }
388}