vmcore/
synic.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Synic interface definitions used by VmBus.
5
6#![forbid(unsafe_code)]
7#![expect(missing_docs)]
8
9use crate::interrupt::Interrupt;
10use crate::monitor::MonitorId;
11use hvdef::Vtl;
12use inspect::Inspect;
13use std::sync::Arc;
14use std::task::Context;
15use std::task::Poll;
16use std::time::Duration;
17use thiserror::Error;
18
19pub trait MessagePort: Send + Sync {
20    /// Handles a message received on the message port.
21    ///
22    /// A message is `trusted` if it was was received from the guest without using host-visible
23    /// mechanisms on a hardware-isolated VM. The `trusted` parameter is always `false` if not
24    /// running in the paravisor of a hardware-isolated VM.
25    fn poll_handle_message(&self, cx: &mut Context<'_>, msg: &[u8], trusted: bool) -> Poll<()>;
26}
27
28pub trait EventPort: Send + Sync {
29    fn handle_event(&self, flag: u16);
30    fn os_event(&self) -> Option<&pal_event::Event> {
31        None
32    }
33}
34
35#[derive(Debug, Error)]
36#[error("hypervisor error")]
37pub struct HypervisorError(#[from] pub Box<dyn std::error::Error + Send + Sync>);
38
39#[derive(Debug, Error)]
40pub enum Error {
41    #[error("connection ID in use: {0}")]
42    ConnectionIdInUse(u32),
43    #[error(transparent)]
44    Hypervisor(HypervisorError),
45}
46
47/// Trait for accessing partition's synic ports.
48pub trait SynicPortAccess: Send + Sync {
49    /// Adds a host message port, which gets notified when the guest calls
50    /// `HvPostMessage`.
51    fn add_message_port(
52        &self,
53        connection_id: u32,
54        minimum_vtl: Vtl,
55        port: Arc<dyn MessagePort>,
56    ) -> Result<Box<dyn Sync + Send>, Error>;
57
58    /// Adds a host event port, which gets notified when the guest calls
59    /// `HvSignalEvent`.
60    ///
61    /// The `monitor_info` parameter is ignored if the synic does not support MnF.
62    ///
63    /// # Panics
64    ///
65    /// Depending on the implementation, this may panic if the monitor ID indicated in
66    /// `monitor_info` is already in use.
67    fn add_event_port(
68        &self,
69        connection_id: u32,
70        minimum_vtl: Vtl,
71        port: Arc<dyn EventPort>,
72        monitor_info: Option<MonitorInfo>,
73    ) -> Result<Box<dyn Sync + Send>, Error>;
74
75    /// Creates a [`GuestMessagePort`] for posting messages to the guest.
76    fn new_guest_message_port(
77        &self,
78        vtl: Vtl,
79        vp: u32,
80        sint: u8,
81    ) -> Result<Box<dyn GuestMessagePort>, HypervisorError>;
82
83    /// Creates a [`GuestEventPort`] for signaling VMBus channels in the guest.
84    ///
85    /// The `monitor_info` parameter is ignored if the synic does not support outgoing monitored
86    /// interrupts.
87    fn new_guest_event_port(
88        &self,
89        port_id: u32,
90        vtl: Vtl,
91        vp: u32,
92        sint: u8,
93        flag: u16,
94        monitor_info: Option<MonitorInfo>,
95    ) -> Result<Box<dyn GuestEventPort>, HypervisorError>;
96
97    /// Returns whether callers should pass an OS event when creating event
98    /// ports, as opposed to passing a function to call.
99    ///
100    /// This is true when the hypervisor can more quickly dispatch an OS event
101    /// and resume the VP than it can take an intercept into user mode and call
102    /// a function.
103    fn prefer_os_events(&self) -> bool;
104
105    /// Returns an object for manipulating the monitor page, or None if monitor pages aren't
106    /// supported.
107    fn monitor_support(&self) -> Option<&dyn SynicMonitorAccess> {
108        None
109    }
110}
111
112/// Provides monitor page functionality for a `SynicPortAccess` implementation.
113pub trait SynicMonitorAccess: SynicPortAccess {
114    /// Sets the GPA of the monitor page currently in use.
115    fn set_monitor_page(&self, vtl: Vtl, gpa: Option<MonitorPageGpas>) -> anyhow::Result<()>;
116
117    /// Allocates monitor pages and sets them as currently in use. If allocating monitor pages is
118    /// not supported, returns `Ok(None)`.
119    ///
120    /// The pages will be deallocated if the monitor page is subsequently changed or cleared using
121    /// [`SynicMonitorAccess::set_monitor_page`].
122    ///
123    /// Allocating a host-to-guest monitor page is optional so [`MonitorPageGpas::parent_to_child`]
124    /// may be zero.
125    fn allocate_monitor_page(&self, vtl: Vtl) -> anyhow::Result<Option<MonitorPageGpas>> {
126        let _ = vtl;
127        Ok(None)
128    }
129}
130
131/// A guest event port, created by [`SynicPortAccess::new_guest_event_port`].
132pub trait GuestEventPort: Send + Sync {
133    /// Returns an interrupt object used to signal the guest.
134    fn interrupt(&self) -> Interrupt;
135
136    /// Updates the target VP for the event port.
137    fn set_target_vp(&mut self, vp: u32) -> Result<(), HypervisorError>;
138}
139
140/// A guest message port, created by [`SynicPortAccess::new_guest_message_port`].
141pub trait GuestMessagePort: Send + Sync + Inspect {
142    /// Posts a message to the guest.
143    ///
144    /// It is the caller's responsibility to not queue too many messages. Not all transport layers
145    /// are guaranteed to support backpressure.
146    fn poll_post_message(&mut self, cx: &mut Context<'_>, typ: u32, payload: &[u8]) -> Poll<()>;
147
148    /// Changes the virtual processor to which messages are sent.
149    fn set_target_vp(&mut self, vp: u32) -> Result<(), HypervisorError>;
150}
151
152/// Represents the GPA of the outgoing and incoming monitor pages.
153#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Inspect)]
154pub struct MonitorPageGpas {
155    /// The GPA of the incoming monitor page.
156    #[inspect(hex)]
157    pub parent_to_child: u64,
158    /// The GPA of the outgoing monitor page.
159    #[inspect(hex)]
160    pub child_to_parent: u64,
161}
162
163/// Provides information about monitor usage for a synic event port.
164#[derive(Clone, Copy, Debug, PartialEq, Eq)]
165pub struct MonitorInfo {
166    // The monitor ID used by the port.
167    pub monitor_id: MonitorId,
168    /// The nterrupt latency.
169    pub latency: Duration,
170}