Skip to main content

hv1_hypercall/
x86.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! X86-64 hypercall support.
5
6use super::HypercallIo;
7use crate::support::AsHandler;
8
9/// An implementation of [`HypercallIo`] on X64 register state.
10pub struct X64RegisterIo<T> {
11    inner: T,
12    is_64bit: bool,
13}
14
15impl<T: X64RegisterState> X64RegisterIo<T> {
16    /// Returns a register accessor backed by `t`.
17    ///
18    /// Uses the 64-bit calling convention if `is_64bit`, otherwise the 32-bit
19    /// one.
20    pub fn new(t: T, is_64bit: bool) -> Self {
21        Self { inner: t, is_64bit }
22    }
23
24    fn gp_pair(&mut self, high: X64HypercallRegister, low: X64HypercallRegister) -> u64 {
25        (self.inner.gp(high) << 32) | (self.inner.gp(low) & 0xffff_ffff)
26    }
27
28    fn mask(&self, value: u64) -> u64 {
29        value
30            & if self.is_64bit {
31                u64::MAX
32            } else {
33                u32::MAX as u64
34            }
35    }
36
37    fn set_control(&mut self, control: u64) {
38        if self.is_64bit {
39            self.inner.set_gp(X64HypercallRegister::Rcx, control);
40        } else {
41            self.inner.set_gp(X64HypercallRegister::Rdx, control >> 32);
42            self.inner
43                .set_gp(X64HypercallRegister::Rax, control & u32::MAX as u64);
44        }
45    }
46}
47
48impl<T> AsHandler<T> for X64RegisterIo<T> {
49    fn as_handler(&mut self) -> &mut T {
50        &mut self.inner
51    }
52}
53
54impl<T> AsHandler<T> for X64RegisterIo<&mut T> {
55    fn as_handler(&mut self) -> &mut T {
56        &mut *self.inner
57    }
58}
59
60impl<T: X64RegisterState> HypercallIo for X64RegisterIo<T> {
61    fn advance_ip(&mut self) {
62        let rip = self.inner.rip().wrapping_add(3);
63        self.inner.set_rip(self.mask(rip));
64    }
65
66    fn retry(&mut self, control: u64) {
67        // Update the input control.
68        self.set_control(control)
69
70        // rip is still at the vmcall/vmmcall instruction, nothing to do.
71    }
72
73    fn control(&mut self) -> u64 {
74        if self.is_64bit {
75            self.inner.gp(X64HypercallRegister::Rcx)
76        } else {
77            self.gp_pair(X64HypercallRegister::Rdx, X64HypercallRegister::Rax)
78        }
79    }
80
81    fn vtl_input(&mut self) -> u64 {
82        let name = if self.is_64bit {
83            X64HypercallRegister::Rax
84        } else {
85            X64HypercallRegister::Rcx
86        };
87
88        let value = self.inner.gp(name);
89        self.mask(value)
90    }
91
92    fn set_result(&mut self, n: u64) {
93        if self.is_64bit {
94            self.inner.set_gp(X64HypercallRegister::Rax, n);
95        } else {
96            self.inner.set_gp(X64HypercallRegister::Rdx, n >> 32);
97            self.inner
98                .set_gp(X64HypercallRegister::Rax, n & u32::MAX as u64);
99        }
100    }
101
102    fn input_gpa(&mut self) -> u64 {
103        if self.is_64bit {
104            self.inner.gp(X64HypercallRegister::Rdx)
105        } else {
106            self.gp_pair(X64HypercallRegister::Rbx, X64HypercallRegister::Rcx)
107        }
108    }
109
110    fn output_gpa(&mut self) -> u64 {
111        if self.is_64bit {
112            self.inner.gp(X64HypercallRegister::R8)
113        } else {
114            self.gp_pair(X64HypercallRegister::Rdi, X64HypercallRegister::Rsi)
115        }
116    }
117
118    fn fast_register_pair_count(&mut self) -> usize {
119        if self.is_64bit { 7 } else { 1 }
120    }
121
122    fn extended_fast_hypercalls_ok(&mut self) -> bool {
123        self.is_64bit
124    }
125
126    fn fast_input(&mut self, buf: &mut [[u64; 2]], _output_register_pairs: usize) -> usize {
127        self.fast_regs(0, buf);
128        buf.len()
129    }
130
131    fn fast_output(&mut self, starting_pair_index: usize, buf: &[[u64; 2]]) {
132        // Continue after the input registers.
133        for (i, &[low, high]) in buf.iter().enumerate() {
134            let index = i + starting_pair_index;
135            if index == 0 {
136                self.inner.set_gp(X64HypercallRegister::Rdx, low);
137                self.inner.set_gp(X64HypercallRegister::R8, high);
138            } else {
139                let x = low as u128 | ((high as u128) << 64);
140                self.inner.set_xmm(index - 1, x)
141            }
142        }
143    }
144
145    fn fast_regs(&mut self, starting_pair_index: usize, buf: &mut [[u64; 2]]) {
146        if self.is_64bit {
147            for (i, [low, high]) in buf.iter_mut().enumerate() {
148                let index = i + starting_pair_index;
149                if index == 0 {
150                    *low = self.inner.gp(X64HypercallRegister::Rdx);
151                    *high = self.inner.gp(X64HypercallRegister::R8);
152                } else {
153                    let value = self.inner.xmm(index - 1);
154                    *low = value as u64;
155                    *high = (value >> 64) as u64;
156                }
157            }
158        } else if let [[low, high], ..] = buf {
159            *low = self.gp_pair(X64HypercallRegister::Rbx, X64HypercallRegister::Rcx);
160            *high = self.gp_pair(X64HypercallRegister::Rdi, X64HypercallRegister::Rsi);
161        }
162    }
163}
164
165/// Register state access for x86/x64.
166pub trait X64RegisterState {
167    /// RIP register.
168    fn rip(&mut self) -> u64;
169
170    /// Sets the RIP register.
171    fn set_rip(&mut self, rip: u64);
172
173    /// Gets a general purpose register.
174    fn gp(&mut self, n: X64HypercallRegister) -> u64;
175
176    /// Sets a general purpose register.
177    fn set_gp(&mut self, n: X64HypercallRegister, value: u64);
178
179    /// Gets an XMM register, `n` in `0..5`.
180    fn xmm(&mut self, n: usize) -> u128;
181
182    /// Sets an XMM register, `n` in `0..5`.
183    fn set_xmm(&mut self, n: usize, value: u128);
184}
185
186impl<T: X64RegisterState> X64RegisterState for &'_ mut T {
187    fn rip(&mut self) -> u64 {
188        (**self).rip()
189    }
190
191    fn set_rip(&mut self, rip: u64) {
192        (**self).set_rip(rip)
193    }
194
195    fn gp(&mut self, n: X64HypercallRegister) -> u64 {
196        (**self).gp(n)
197    }
198
199    fn set_gp(&mut self, n: X64HypercallRegister, value: u64) {
200        (**self).set_gp(n, value)
201    }
202
203    fn xmm(&mut self, n: usize) -> u128 {
204        (**self).xmm(n)
205    }
206
207    fn set_xmm(&mut self, n: usize, value: u128) {
208        (**self).set_xmm(n, value)
209    }
210}
211
212/// An x64 GP register. This just contains the subset used in the hypercall ABI.
213/// These are defined with discriminants that match the x86-64 architecture's
214/// register ordering.
215pub enum X64HypercallRegister {
216    /// RAX
217    Rax = 0,
218    /// RCX
219    Rcx = 1,
220    /// RDX
221    Rdx = 2,
222    /// RBX
223    Rbx = 3,
224    /// RSI
225    Rsi = 6,
226    /// RDI
227    Rdi = 7,
228    /// R8
229    R8 = 8,
230}
231
232#[cfg(test)]
233mod tests {
234    use super::*;
235    use crate::tests::TestHypercallIo;
236    use crate::tests::TestRegisterState;
237
238    /// Test hypercall IO for x86.
239    impl<T: X64RegisterState + TestRegisterState> TestHypercallIo for X64RegisterIo<T> {
240        fn get_result(&mut self) -> u64 {
241            if self.is_64bit {
242                self.inner.gp(X64HypercallRegister::Rax)
243            } else {
244                self.gp_pair(X64HypercallRegister::Rdx, X64HypercallRegister::Rax)
245            }
246        }
247
248        fn set_control(&mut self, control: u64) {
249            X64RegisterIo::set_control(self, control);
250        }
251
252        fn set_input_gpa(&mut self, gpa: u64) {
253            if self.is_64bit {
254                self.inner.set_gp(X64HypercallRegister::Rdx, gpa);
255            } else {
256                self.inner.set_gp(X64HypercallRegister::Rbx, gpa >> 32);
257                self.inner
258                    .set_gp(X64HypercallRegister::Rcx, gpa & u32::MAX as u64);
259            }
260        }
261
262        fn set_output_gpa(&mut self, gpa: u64) {
263            if self.is_64bit {
264                self.inner.set_gp(X64HypercallRegister::R8, gpa);
265            } else {
266                self.inner.set_gp(X64HypercallRegister::Rdi, gpa >> 32);
267                self.inner
268                    .set_gp(X64HypercallRegister::Rsi, gpa & u32::MAX as u64);
269            }
270        }
271
272        fn set_fast_input(&mut self, buf: &[[u64; 2]]) {
273            if self.is_64bit {
274                let (gp, xmm) = buf.split_at(1);
275                let rdx = gp[0][0];
276                let r8 = gp[0][1];
277                self.inner.set_gp(X64HypercallRegister::Rdx, rdx);
278                self.inner.set_gp(X64HypercallRegister::R8, r8);
279                for (i, [low, high]) in xmm.iter().enumerate() {
280                    let value = *low as u128 | ((*high as u128) << 64);
281                    self.inner.set_xmm(i, value);
282                }
283            } else {
284                let [low, high] = buf[0];
285                self.inner.set_gp(X64HypercallRegister::Rbx, low >> 32);
286                self.inner
287                    .set_gp(X64HypercallRegister::Rcx, low & u32::MAX as u64);
288                self.inner.set_gp(X64HypercallRegister::Rdi, high >> 32);
289                self.inner
290                    .set_gp(X64HypercallRegister::Rsi, high & u32::MAX as u64);
291            }
292        }
293
294        fn get_fast_output(&mut self, input_register_pairs: usize, buf: &mut [[u64; 2]]) {
295            // Continue after the input registers.
296            for (i, [low, high]) in buf.iter_mut().enumerate() {
297                if i + input_register_pairs == 0 {
298                    *low = self.inner.gp(X64HypercallRegister::Rdx);
299                    *high = self.inner.gp(X64HypercallRegister::R8);
300                } else {
301                    let x = self.inner.xmm(i + input_register_pairs - 1);
302                    *low = x as u64;
303                    *high = (x >> 64) as u64;
304                }
305            }
306        }
307
308        fn get_modified_mask(&self) -> u64 {
309            self.inner.get_modified_mask()
310        }
311
312        fn clear_modified_mask(&mut self) {
313            self.inner.clear_modified_mask()
314        }
315
316        fn get_io_register_mask(&self) -> u64 {
317            if self.is_64bit {
318                1u64 << X64HypercallRegister::Rcx as usize
319                    | 1u64 << X64HypercallRegister::Rax as usize
320            } else {
321                1u64 << X64HypercallRegister::Rdx as usize
322                    | 1u64 << X64HypercallRegister::Rax as usize
323            }
324        }
325
326        fn get_name(&self) -> String {
327            format!("x86_{}", if self.is_64bit { "64" } else { "32" })
328        }
329
330        fn set_vtl_input(&mut self, vtl_input: u64) {
331            if self.is_64bit {
332                self.inner.set_gp(X64HypercallRegister::Rax, vtl_input);
333            } else {
334                self.inner
335                    .set_gp(X64HypercallRegister::Rcx, vtl_input >> 32);
336            }
337        }
338
339        fn auto_advance_ip(&mut self) {}
340    }
341}