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}