sidecar_defs/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Definitions for the sidecar kernel.
5
6#![no_std]
7#![forbid(unsafe_code)]
8
9use core::sync::atomic::AtomicU8;
10use core::sync::atomic::AtomicU32;
11use hvdef::HvMessage;
12use hvdef::HvStatus;
13use hvdef::hypercall::HvInputVtl;
14use open_enum::open_enum;
15use zerocopy::FromBytes;
16use zerocopy::FromZeros;
17use zerocopy::Immutable;
18use zerocopy::IntoBytes;
19use zerocopy::KnownLayout;
20
21/// Sidecar start input parameters.
22#[repr(C, align(4096))]
23#[derive(FromZeros, Immutable, KnownLayout)]
24pub struct SidecarParams {
25    /// The physical address of the x86-64 hypercall page.
26    pub hypercall_page: u64,
27    /// If true, enabling serial logging.
28    pub enable_logging: bool,
29    /// The number of valid nodes in `nodes`.
30    pub node_count: u32,
31    /// The node-specific input parameters.
32    pub nodes: [SidecarNodeParams; MAX_NODES],
33}
34
35/// Node-specific input parameters.
36#[repr(C)]
37#[derive(FromZeros, Immutable, KnownLayout)]
38pub struct SidecarNodeParams {
39    /// The physical address of the beginning of the reserved memory for this
40    /// node. Must be page aligned.
41    pub memory_base: u64,
42    /// The size of the reserved memory region. Must be a page multiple.
43    pub memory_size: u64,
44    /// The base VP for this node.
45    pub base_vp: u32,
46    /// The number of VPs in the node.
47    pub vp_count: u32,
48}
49
50/// The maximum number of supported sidecar nodes.
51pub const MAX_NODES: usize = 128;
52
53const _: () = assert!(size_of::<SidecarParams>() <= PAGE_SIZE);
54
55/// The output of the sidecar kernel boot process.
56#[repr(C)]
57#[derive(FromZeros, Immutable, KnownLayout)]
58pub struct SidecarOutput {
59    /// The boot error. This is only set if the entry point returns false.
60    pub error: CommandError,
61    /// The per-node output information.
62    pub nodes: [SidecarNodeOutput; MAX_NODES],
63}
64
65/// The per-node output of the sidecar kernel boot process.
66#[repr(C)]
67#[derive(FromZeros, Immutable, KnownLayout)]
68pub struct SidecarNodeOutput {
69    /// The physical address of the control page for the node.
70    pub control_page: u64,
71    /// The base physical address of the per-VP pages for the node.
72    pub shmem_pages_base: u64,
73    /// The size of the VP page region.
74    pub shmem_pages_size: u64,
75}
76
77const _: () = assert!(size_of::<SidecarOutput>() <= PAGE_SIZE);
78
79/// The page size for all sidecar objects.
80pub const PAGE_SIZE: usize = 4096;
81
82/// The per-node control page, which is used to communicate between the sidecar
83/// kernel and the main kernel sidecar kernel driver.
84#[repr(C, align(4096))]
85pub struct ControlPage {
86    /// The node index.
87    pub index: AtomicU32,
88    /// The base CPU of the node.
89    pub base_cpu: AtomicU32,
90    /// The number of CPUs in the node.
91    pub cpu_count: AtomicU32,
92    /// The vector the driver should IPI to wake up a sidecar CPU.
93    pub request_vector: AtomicU32,
94    /// The APIC ID of the CPU that the sidecar CPU should IPI to wake up the
95    /// driver.
96    pub response_cpu: AtomicU32,
97    /// The vector the sidecar CPU should IPI to wake up the driver.
98    pub response_vector: AtomicU32,
99    /// If non-zero, then a sidecar CPU has a message for the driver.
100    pub needs_attention: AtomicU32,
101    /// Reserved.
102    pub reserved: [u8; 36],
103    /// The per-CPU status.
104    pub cpu_status: [AtomicU8; 4032],
105}
106
107const _: () = assert!(size_of::<ControlPage>() == PAGE_SIZE);
108
109open_enum::open_enum! {
110    /// The CPU status.
111    pub enum CpuStatus: u8 {
112        /// The CPU is not running in the sidecar kernel.
113        REMOVED = 0,
114        /// The CPU is idle, having completed any previous commands.
115        IDLE = 1,
116        /// The CPU is running a command.
117        RUN = 2,
118        /// The CPU is being asked to stop running a command.
119        STOP = 3,
120        /// The CPU is being asked to terminate.
121        REMOVE = 4,
122    }
123}
124
125/// The number of reserved pages required for each VP.
126// 1. pml4
127// 2. pdpt
128// 3. pd
129// 4. pt
130// 5. globals
131// 6. vp assist page
132// 7. hypercall input page
133// 8. hypercall output page
134pub const PER_VP_PAGES: usize = 8 + STACK_PAGES;
135
136/// The number of per-VP shared-memory pages.
137// 1. command page
138// 2. register page
139pub const PER_VP_SHMEM_PAGES: usize = 2;
140
141/// The number of pages in the per-VP stack.
142pub const STACK_PAGES: usize = 3;
143
144/// The required memory (in bytes) for a node.
145pub const fn required_memory(vp_count: u32) -> usize {
146    // Control page + per-VP pages.
147    (1 + (PER_VP_SHMEM_PAGES + PER_VP_PAGES) * vp_count as usize) * PAGE_SIZE
148}
149
150/// The sidecar command page, containing command requests and responses.
151#[repr(C)]
152#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
153pub struct CommandPage {
154    /// The command to run.
155    pub command: SidecarCommand,
156    /// If non-zero, the command failed.
157    pub has_error: u8,
158    /// Padding bytes.
159    pub padding: [u8; 11],
160    /// The current CPU register state.
161    pub cpu_context: CpuContextX64,
162    /// The intercept message from the last VP exit.
163    pub intercept_message: HvMessage,
164    /// The error, if `has_error` is non-zero.
165    pub error: CommandError,
166    /// The request data for the command.
167    pub request_data: [u128; REQUEST_DATA_SIZE / size_of::<u128>()],
168    /// Reserved.
169    pub reserved: [u64; 190],
170}
171
172const REQUEST_DATA_SIZE: usize = 64 * size_of::<u128>();
173
174/// A string error.
175#[repr(C)]
176#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
177pub struct CommandError {
178    /// The length of the error string, in bytes.
179    pub len: u8,
180    /// The error string, encoded as UTF-8, containing `len` bytes.
181    pub buf: [u8; 255],
182}
183
184const _: () = assert!(size_of::<CommandPage>() == PAGE_SIZE);
185
186open_enum! {
187    /// The sidecar command.
188    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
189    pub enum SidecarCommand: u32 {
190        /// No command.
191        NONE = 0,
192        /// Run the VP until cancelled or an intercept occurs.
193        RUN_VP = 1,
194        /// Gets VP registers.
195        GET_VP_REGISTERS = 2,
196        /// Sets VP registers.
197        SET_VP_REGISTERS = 3,
198        /// Translates a guest virtual address.
199        TRANSLATE_GVA = 4,
200    }
201}
202
203/// A request and response for [`SidecarCommand::GET_VP_REGISTERS`] or
204/// [`SidecarCommand::SET_VP_REGISTERS`].
205///
206/// Followed by an array of [`hvdef::hypercall::HvRegisterAssoc`], which are
207/// updated in place for the get request.
208#[repr(C)]
209#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
210pub struct GetSetVpRegisterRequest {
211    /// The number of registers to get.
212    pub count: u16,
213    /// The target VTL.
214    pub target_vtl: HvInputVtl,
215    /// Reserved.
216    pub rsvd: u8,
217    /// The hypervisor result.
218    pub status: HvStatus,
219    /// Reserved.
220    pub rsvd2: [u8; 10],
221    /// Alignment field.
222    pub regs: [hvdef::hypercall::HvRegisterAssoc; 0],
223}
224
225/// The maximum number of registers that can be requested in a single
226/// [`SidecarCommand::GET_VP_REGISTERS`] or
227/// [`SidecarCommand::SET_VP_REGISTERS`].
228pub const MAX_GET_SET_VP_REGISTERS: usize = (REQUEST_DATA_SIZE
229    - size_of::<GetSetVpRegisterRequest>())
230    / size_of::<hvdef::hypercall::HvRegisterAssoc>();
231
232/// A request for [`SidecarCommand::TRANSLATE_GVA`].
233#[repr(C)]
234#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
235pub struct TranslateGvaRequest {
236    /// The guest virtual address page number.
237    pub gvn: u64,
238    /// The control flags.
239    pub control_flags: hvdef::hypercall::TranslateGvaControlFlagsX64,
240}
241
242/// A response for [`SidecarCommand::TRANSLATE_GVA`].
243#[repr(C)]
244#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
245pub struct TranslateGvaResponse {
246    /// The hypervisor result.
247    pub status: HvStatus,
248    /// Reserved.
249    pub rsvd: [u16; 7],
250    /// The output of the translation.
251    pub output: hvdef::hypercall::TranslateVirtualAddressExOutputX64,
252}
253
254/// A response for [`SidecarCommand::RUN_VP`].
255#[repr(C)]
256#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
257pub struct RunVpResponse {
258    /// If true, the VP was stopped due to an intercept.
259    pub intercept: u8,
260}
261
262/// The CPU context for x86-64.
263#[repr(C, align(16))]
264#[derive(Debug, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
265pub struct CpuContextX64 {
266    /// The general purpose registers, in the usual order, except CR2 is in
267    /// RSP's position.
268    pub gps: [u64; 16],
269    /// The `fxsave` state.
270    pub fx_state: x86defs::xsave::Fxsave,
271    /// Reserved.
272    pub reserved: [u8; 384],
273}
274
275impl CpuContextX64 {
276    /// The index of the RAX register.
277    pub const RAX: usize = 0;
278    /// The index of the RCX register.
279    pub const RCX: usize = 1;
280    /// The index of the RDX register.
281    pub const RDX: usize = 2;
282    /// The index of the RBX register.
283    pub const RBX: usize = 3;
284    /// The index of the CR2 register.
285    pub const CR2: usize = 4;
286    /// The index of the RBP register.
287    pub const RBP: usize = 5;
288    /// The index of the RSI register.
289    pub const RSI: usize = 6;
290    /// The index of the RDI register.
291    pub const RDI: usize = 7;
292    /// The index of the R8 register.
293    pub const R8: usize = 8;
294    /// The index of the R9 register.
295    pub const R9: usize = 9;
296    /// The index of the R10 register.
297    pub const R10: usize = 10;
298    /// The index of the R11 register.
299    pub const R11: usize = 11;
300    /// The index of the R12 register.
301    pub const R12: usize = 12;
302    /// The index of the R13 register.
303    pub const R13: usize = 13;
304    /// The index of the R14 register.
305    pub const R14: usize = 14;
306    /// The index of the R15 register.
307    pub const R15: usize = 15;
308}