hv1_hypercall/
aarch64.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! ARM64 hypercall support.
5
6use super::HypercallIo;
7use crate::support::AsHandler;
8
9/// Provides access to the ARM64 register state needed to parse hypercalls.
10pub trait Arm64RegisterState {
11    /// Gets the program counter.
12    fn pc(&mut self) -> u64;
13    /// Sets the program counter.
14    fn set_pc(&mut self, pc: u64);
15    /// Gets register Xn, `n <= 17`.
16    fn x(&mut self, n: u8) -> u64;
17    /// Sets register Xn, `n <= 17`.
18    fn set_x(&mut self, n: u8, v: u64);
19}
20
21impl<T: Arm64RegisterState> Arm64RegisterState for &'_ mut T {
22    fn pc(&mut self) -> u64 {
23        (**self).pc()
24    }
25
26    fn set_pc(&mut self, pc: u64) {
27        (**self).set_pc(pc)
28    }
29
30    fn x(&mut self, n: u8) -> u64 {
31        (**self).x(n)
32    }
33
34    fn set_x(&mut self, n: u8, v: u64) {
35        (**self).set_x(n, v)
36    }
37}
38
39/// An implementation of [`HypercallIo`] on top of [`Arm64RegisterState`].
40pub struct Arm64RegisterIo<T> {
41    inner: T,
42    pre_advanced: bool,
43    smccc: bool,
44}
45
46impl<T> AsHandler<T> for Arm64RegisterIo<T> {
47    fn as_handler(&mut self) -> &mut T {
48        &mut self.inner
49    }
50}
51
52impl<T> AsHandler<T> for Arm64RegisterIo<&mut T> {
53    fn as_handler(&mut self) -> &mut T {
54        &mut *self.inner
55    }
56}
57
58impl<T: Arm64RegisterState> Arm64RegisterIo<T> {
59    /// Returns a new instance.
60    ///
61    /// If `pre_advanced`, the PC has already been advanced (which the ARM
62    /// processor does automatically when the HVC instruction is executed).
63    ///
64    /// If `smccc`, this is the SMCCC calling convention (hvc #0). Otherwise, it
65    /// is the Hyper-V calling convention (hvc #1).
66    pub fn new(t: T, pre_advanced: bool, smccc: bool) -> Self {
67        Self {
68            inner: t,
69            pre_advanced,
70            smccc,
71        }
72    }
73
74    fn set_control(&mut self, control: u64) {
75        // X0 for Hyper-V, X1 for SMCCC.
76        self.inner.set_x(self.smccc as u8, control);
77    }
78}
79
80impl<T: Arm64RegisterState> HypercallIo for Arm64RegisterIo<T> {
81    fn advance_ip(&mut self) {
82        if !self.pre_advanced {
83            let pc = self.inner.pc().wrapping_add(4);
84            self.inner.set_pc(pc);
85        }
86    }
87
88    fn retry(&mut self, control: u64) {
89        self.set_control(control);
90        if self.pre_advanced {
91            let pc = self.inner.pc().wrapping_sub(4);
92            self.inner.set_pc(pc);
93        }
94    }
95
96    fn control(&mut self) -> u64 {
97        // X0 for Hyper-V, X1 for SMCCC.
98        self.inner.x(self.smccc as u8)
99    }
100
101    fn input_gpa(&mut self) -> u64 {
102        // X1 for Hyper-V, X2 for SMCCC.
103        self.inner.x(1 + self.smccc as u8)
104    }
105
106    fn output_gpa(&mut self) -> u64 {
107        // X2 for Hyper-V, X3 for SMCCC.
108        self.inner.x(2 + self.smccc as u8)
109    }
110
111    fn fast_register_pair_count(&mut self) -> usize {
112        8
113    }
114
115    fn extended_fast_hypercalls_ok(&mut self) -> bool {
116        true
117    }
118
119    fn fast_input(&mut self, buf: &mut [[u64; 2]], output_register_pairs: usize) -> usize {
120        self.fast_regs(0, buf);
121
122        if self.smccc {
123            // SMCCC: start output after the input registers.
124            buf.len()
125        } else {
126            // Hyper-V: use the last n registers for output.
127            self.fast_register_pair_count() - output_register_pairs
128        }
129    }
130
131    fn fast_output(&mut self, starting_pair_index: usize, buf: &[[u64; 2]]) {
132        // X1-X16 for Hyper-V, X2-X17 for SMCCC.
133        let start = starting_pair_index * 2 + 1 + self.smccc as usize;
134
135        for (i, &[low, high]) in buf.iter().enumerate() {
136            self.inner.set_x((start + i * 2) as u8, low);
137            self.inner.set_x((start + i * 2 + 1) as u8, high);
138        }
139    }
140
141    fn vtl_input(&mut self) -> u64 {
142        0
143    }
144
145    fn set_result(&mut self, n: u64) {
146        // Always X0.
147        self.inner.set_x(0, n)
148    }
149
150    fn fast_regs(&mut self, starting_pair_index: usize, buf: &mut [[u64; 2]]) {
151        // X1-X16 for Hyper-V, X2-X17 for SMCCC.
152        let start = starting_pair_index * 2 + 1 + self.smccc as usize;
153        for (i, [low, high]) in buf.iter_mut().enumerate() {
154            *low = self.inner.x((start + i * 2) as u8);
155            *high = self.inner.x((start + i * 2 + 1) as u8);
156        }
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use crate::tests::TestHypercallIo;
164    use crate::tests::TestRegisterState;
165
166    /// Test hypercall IO for ARM64.
167    impl<T: Arm64RegisterState + TestRegisterState> TestHypercallIo for Arm64RegisterIo<T> {
168        fn get_result(&mut self) -> u64 {
169            // Always X0.
170            self.inner.x(0)
171        }
172
173        fn set_control(&mut self, control: u64) {
174            Arm64RegisterIo::set_control(self, control);
175        }
176
177        fn set_input_gpa(&mut self, gpa: u64) {
178            // X1 for Hyper-V, X2 for SMCCC.
179            self.inner.set_x(1 + self.smccc as u8, gpa)
180        }
181
182        fn set_output_gpa(&mut self, gpa: u64) {
183            // X2 for Hyper-V, X3 for SMCCC.
184            self.inner.set_x(2 + self.smccc as u8, gpa);
185        }
186
187        fn set_fast_input(&mut self, buf: &[[u64; 2]]) {
188            // X1-X16 for Hyper-V, X2-X17 for SMCCC.
189            for (i, [low, high]) in buf.iter().enumerate() {
190                self.inner.set_x(i as u8 * 2 + 1 + self.smccc as u8, *low);
191                self.inner.set_x(i as u8 * 2 + 2 + self.smccc as u8, *high);
192            }
193        }
194
195        fn get_fast_output(&mut self, input_register_pairs: usize, buf: &mut [[u64; 2]]) {
196            let start = if self.smccc {
197                // SMCCC: start after the input registers.
198                2 + input_register_pairs * 2
199            } else {
200                // Hyper-V: use the last n registers, ending with X16.
201                17 - buf.len() * 2
202            };
203            for (i, [low, high]) in buf.iter_mut().enumerate() {
204                *low = self.inner.x((start + i * 2) as u8);
205                *high = self.inner.x((start + i * 2 + 1) as u8);
206            }
207        }
208
209        fn get_modified_mask(&self) -> u64 {
210            self.inner.get_modified_mask()
211        }
212
213        fn clear_modified_mask(&mut self) {
214            self.inner.clear_modified_mask()
215        }
216
217        fn get_io_register_mask(&self) -> u64 {
218            // X0 is always output, control is either X0 (for Hyper-V) or X1 (for SMCCC).
219            1u64 << (self.smccc as u8) | 1
220        }
221
222        fn get_name(&self) -> String {
223            format!(
224                "Arm64RegisterIo<pre_advanced={}, smccc={}>",
225                self.pre_advanced, self.smccc
226            )
227        }
228
229        fn set_vtl_input(&mut self, vtl_input: u64) {
230            // No VTL input for ARM64.
231            assert_eq!(vtl_input, 0);
232        }
233
234        fn auto_advance_ip(&mut self) {
235            if self.pre_advanced {
236                let pc = self.inner.pc().wrapping_add(4);
237                self.inner.set_pc(pc);
238            }
239        }
240    }
241}