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    advance_ip: bool,
14}
15
16impl<T: X64RegisterState> X64RegisterIo<T> {
17    /// Returns a register accessor backed by `t`.
18    ///
19    /// Uses the 64-bit calling convention if `is_64bit`, otherwise the 32-bit
20    /// one. If `advance_ip` is false, the transport is responsible for resuming
21    /// execution after the hypercall.
22    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        // Update the input control.
76        self.set_control(control)
77
78        // rip is still at the vmcall/vmmcall instruction, nothing to do.
79    }
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        // Continue after the input registers.
141        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
173/// Register state access for x86/x64.
174pub trait X64RegisterState {
175    /// RIP register.
176    fn rip(&mut self) -> u64;
177
178    /// Sets the RIP register.
179    fn set_rip(&mut self, rip: u64);
180
181    /// Gets a general purpose register.
182    fn gp(&mut self, n: X64HypercallRegister) -> u64;
183
184    /// Sets a general purpose register.
185    fn set_gp(&mut self, n: X64HypercallRegister, value: u64);
186
187    /// Gets an XMM register, `n` in `0..5`.
188    fn xmm(&mut self, n: usize) -> u128;
189
190    /// Sets an XMM register, `n` in `0..5`.
191    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
220/// An x64 GP register. This just contains the subset used in the hypercall ABI.
221/// These are defined with discriminants that match the x86-64 architecture's
222/// register ordering.
223pub enum X64HypercallRegister {
224    /// RAX
225    Rax = 0,
226    /// RCX
227    Rcx = 1,
228    /// RDX
229    Rdx = 2,
230    /// RBX
231    Rbx = 3,
232    /// RSI
233    Rsi = 6,
234    /// RDI
235    Rdi = 7,
236    /// R8
237    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    /// Test hypercall IO for x86.
286    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            // Continue after the input registers.
343            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}