Skip to main content

vmbus_channel/
bus.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Vmbus bus definitions.
5
6use async_trait::async_trait;
7use guestmem::GuestMemory;
8use guid::Guid;
9use inspect::Inspect;
10use mesh::MeshPayload;
11use mesh::payload::Protobuf;
12use mesh::rpc::FailableRpc;
13use mesh::rpc::Rpc;
14use std::fmt::Display;
15use std::time::Duration;
16use vmbus_core::protocol;
17use vmbus_core::protocol::GpadlId;
18use vmbus_core::protocol::PipeFlags;
19use vmbus_core::protocol::PipeUserDefinedData;
20use vmbus_core::protocol::UserDefinedData;
21use vmcore::interrupt::Interrupt;
22
23/// Input for creating a channel offer.
24#[derive(Debug)]
25pub struct OfferInput {
26    /// Parameters describing the offer.
27    pub params: OfferParams,
28    /// The event to signal when the guest needs attention.
29    pub event: Interrupt,
30    /// A mesh channel to send channel-related requests to.
31    pub request_send: mesh::Sender<ChannelRequest>,
32    /// A mesh channel to receive channel-related requests to.
33    pub server_request_recv: mesh::Receiver<ChannelServerRequest>,
34}
35
36/// Resources for an offered channel.
37#[derive(Debug, Default)]
38pub struct OfferResources {
39    /// Untrusted guest memory access.
40    untrusted_memory: GuestMemory,
41    /// Private guest memory access. This will be `None` unless running in a paravisor of a hardware
42    /// isolated VM.
43    private_memory: Option<GuestMemory>,
44}
45
46impl OfferResources {
47    /// Creates a new `OfferResources`.
48    pub fn new(untrusted_memory: GuestMemory, private_memory: Option<GuestMemory>) -> Self {
49        OfferResources {
50            untrusted_memory,
51            private_memory,
52        }
53    }
54
55    /// Returns the `GuestMemory` to use based on the whether the open request requests confidential
56    /// memory.
57    ///
58    /// The open request reflects both whether the device indicated it supports confidential
59    /// external memory when it was offered, and whether the currently connected vmbus client
60    /// supports it. As such, you must not attempt to get the guest memory until a channel is
61    /// opened, and you should not retain the guest memory after it is closed, as the client and
62    /// its capabilities may change across opens.
63    pub fn guest_memory(&self, open_request: &OpenRequest) -> &GuestMemory {
64        self.get_memory(open_request.use_confidential_external_memory)
65    }
66
67    pub(crate) fn ring_memory(&self, open_request: &OpenRequest) -> &GuestMemory {
68        self.get_memory(open_request.use_confidential_ring)
69    }
70
71    fn get_memory(&self, private: bool) -> &GuestMemory {
72        if private {
73            self.private_memory
74                .as_ref()
75                .expect("private memory should be present if confidential memory is requested")
76        } else {
77            &self.untrusted_memory
78        }
79    }
80}
81
82/// A request from the VMBus control plane.
83#[derive(Debug, MeshPayload)]
84pub enum ChannelRequest {
85    /// Open the channel.
86    Open(Rpc<OpenRequest, bool>),
87    /// Close the channel.
88    ///
89    /// Although there is no response from the host, this is still modeled as an
90    /// RPC so that the caller can know that the vmbus client's state has been
91    /// updated.
92    Close(Rpc<(), ()>),
93    /// Create a new GPADL.
94    Gpadl(Rpc<GpadlRequest, bool>),
95    /// Tear down an existing GPADL.
96    TeardownGpadl(Rpc<GpadlId, ()>),
97    /// Modify the channel's target VP.
98    Modify(Rpc<ModifyRequest, i32>),
99}
100
101/// GPADL information from the guest.
102#[derive(Debug, MeshPayload)]
103pub struct GpadlRequest {
104    /// The GPADL ID.
105    pub id: GpadlId,
106    /// The number of ranges in the GPADL.
107    pub count: u16,
108    /// The GPA range buffer.
109    pub buf: Vec<u64>,
110}
111
112/// Modify channel request.
113#[derive(Debug, MeshPayload)]
114pub enum ModifyRequest {
115    /// Change the target VP to `target_vp`.
116    TargetVp {
117        /// The new target VP.
118        target_vp: u32,
119    },
120}
121
122/// A request to the VMBus control plane.
123#[derive(mesh::MeshPayload)]
124pub enum ChannelServerRequest {
125    /// A request to restore the channel.
126    ///
127    /// The input parameter indicates if the channel was saved open.
128    Restore(FailableRpc<bool, RestoreResult>),
129    /// A request to revoke the channel.
130    ///
131    /// A channel can also be revoked by dropping it. This request is only necessary if you need to
132    /// wait for the revoke operation to complete.
133    Revoke(Rpc<(), ()>),
134}
135
136/// The result of a [`ChannelServerRequest::Restore`] operation.
137#[derive(Debug, MeshPayload)]
138pub struct RestoreResult {
139    /// The open request, if the channel was opened restored.
140    pub open_request: Option<OpenRequest>,
141    /// The active GPADLs.
142    pub gpadls: Vec<RestoredGpadl>,
143}
144
145/// A restored GPADL.
146#[derive(Debug, MeshPayload)]
147pub struct RestoredGpadl {
148    /// The GPADL request.
149    pub request: GpadlRequest,
150    /// Whether the GPADL was saved in the accepted state.
151    ///
152    /// If true, failure to restore this is fatal to the restore operation. If
153    /// false, the device will later get another GPADL offer for this same
154    /// GPADL.
155    ///
156    /// This is needed because the device may have saved itself with a
157    /// dependency on this GPADL even if the response did not make it into the
158    /// vmbus server saved state.
159    pub accepted: bool,
160}
161
162/// Trait implemented by VMBus servers.
163#[async_trait]
164pub trait ParentBus: Send + Sync {
165    /// Offers a new channel.
166    async fn add_child(&self, request: OfferInput) -> anyhow::Result<OfferResources>;
167
168    /// Clones the bus.
169    ///
170    /// TODO: This is needed for now to support transparent subchannel offers.
171    /// Remove this once subchannels can be pre-created at primary channel offer
172    /// time.
173    fn clone_bus(&self) -> Box<dyn ParentBus>;
174
175    /// Returns whether [`OfferInput::event`] needs to be backed by an OS event.
176    ///
177    /// TODO: Remove this and just return the appropriate notify type directly
178    /// once subchannel creation and enable are separated.
179    fn use_event(&self) -> bool {
180        true
181    }
182}
183
184/// Channel open-specific data.
185#[derive(Debug, Copy, Clone, mesh::MeshPayload)]
186pub struct OpenData {
187    /// The target VP for interrupts to the guest, or `None` if interrupts are disabled.
188    pub target_vp: Option<u32>,
189    /// The page offset into the ring GPADL of the host-to-guest ring buffer.
190    pub ring_offset: u32,
191    /// The ring buffer's GPADL ID.
192    pub ring_gpadl_id: GpadlId,
193    /// The event flag used to notify the guest.
194    pub event_flag: u16,
195    /// An connection ID used when the guest notifies the host.
196    pub connection_id: u32,
197    /// User data provided by the opener.
198    pub user_data: UserDefinedData,
199}
200
201/// Information provided to devices when a channel is opened.
202#[derive(Debug, Clone, mesh::MeshPayload)]
203pub struct OpenRequest {
204    /// Channel open-specific data.
205    pub open_data: OpenData,
206    /// The interrupt used to signal the guest.
207    pub interrupt: Interrupt,
208    /// Indicates if the currently connected vmbus client, as well as the channel the request is
209    /// for, supports the use of confidential ring buffers.
210    pub use_confidential_ring: bool,
211    /// Indicates if the currently connected vmbus client, as well as the channel the request is
212    /// for, supports the use of confidential external memory.
213    pub use_confidential_external_memory: bool,
214    /// Indicates if the currently connected vmbus client is expected to pin any external memory
215    /// used by the channel. This is only true if the vmbus client supports GPA pinning and the
216    /// channel indicated it requires pinned external memory. It can only be true for paravisor
217    /// channels in a VM that supports the GPA pinning hypercalls.
218    pub is_external_memory_pinned: bool,
219}
220
221impl OpenRequest {
222    /// Creates a new `OpenRequest`.
223    pub fn new(
224        open_data: OpenData,
225        interrupt: Interrupt,
226        feature_flags: protocol::FeatureFlags,
227        offer_flags: protocol::OfferFlags,
228    ) -> Self {
229        Self {
230            open_data,
231            interrupt,
232            use_confidential_ring: feature_flags.confidential_channels()
233                && offer_flags.confidential_ring_buffer(),
234            use_confidential_external_memory: feature_flags.confidential_channels()
235                && offer_flags.confidential_external_memory(),
236            is_external_memory_pinned: feature_flags.gpa_pinning()
237                && offer_flags.require_pinned_external_memory(),
238        }
239    }
240}
241
242#[derive(Debug, Default, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Protobuf, Inspect)]
243/// The identifying IDs for a channel offer.
244#[mesh(package = "vmbus")]
245pub struct OfferKey {
246    /// The interface ID describing the type of channel.
247    #[mesh(1)]
248    pub interface_id: Guid,
249    /// The unique instance ID for the channel.
250    #[mesh(2)]
251    pub instance_id: Guid,
252    /// The subchannel index. Index 0 indicates a primary (normal channel).
253    #[mesh(3)]
254    pub subchannel_index: u16,
255}
256
257impl Display for OfferKey {
258    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259        write!(
260            f,
261            "{{{}}}-{{{}}}-{}",
262            self.interface_id, self.instance_id, self.subchannel_index
263        )
264    }
265}
266
267impl From<&protocol::OfferChannel> for OfferKey {
268    fn from(offer: &protocol::OfferChannel) -> Self {
269        Self {
270            interface_id: offer.interface_id,
271            instance_id: offer.instance_id,
272            subchannel_index: offer.subchannel_index,
273        }
274    }
275}
276
277/// Channel offer parameters.
278#[derive(Debug, Clone, Default, mesh::MeshPayload)]
279pub struct OfferParams {
280    /// An informational string describing the channel type.
281    pub interface_name: String,
282    /// The unique instance ID for the channel.
283    pub instance_id: Guid,
284    /// The interface ID describing the type of channel.
285    pub interface_id: Guid,
286    /// The amount of MMIO space needed by the channel, in megabytes.
287    pub mmio_megabytes: u16,
288    /// The amount of optional MMIO space used by the channel, in megabytes.
289    pub mmio_megabytes_optional: u16,
290    /// The channel's type.
291    pub channel_type: ChannelType,
292    /// The subchannel index. Index 0 indicates a primary (normal channel).
293    pub subchannel_index: u16,
294    /// Indicates whether the channel's interrupts should use monitor pages,
295    /// and the interrupt latency if it's enabled.
296    pub mnf_interrupt_latency: Option<Duration>,
297    /// The order in which channels with the same interface will be offered to
298    /// the guest (optional).
299    pub offer_order: Option<u64>,
300    /// Indicates whether the channel supports using encrypted memory for any
301    /// external GPADLs and GPA direct ranges. This is only used when hardware
302    /// isolation is in use.
303    pub allow_confidential_external_memory: bool,
304}
305
306impl OfferParams {
307    /// Gets the offer key for this offer.
308    pub fn key(&self) -> OfferKey {
309        OfferKey {
310            interface_id: self.interface_id,
311            instance_id: self.instance_id,
312            subchannel_index: self.subchannel_index,
313        }
314    }
315}
316
317/// The channel type.
318#[derive(Debug, Copy, Clone, MeshPayload, Inspect)]
319#[inspect(external_tag)]
320pub enum ChannelType {
321    /// A channel representing a device.
322    Device {
323        /// If true, the ring buffer packets should contain pipe headers.
324        pipe_packets: bool,
325    },
326    /// A channel representing an interface for the guest to open.
327    Interface {
328        /// Interface-specific user-defined data to put in the channel offer.
329        user_defined: UserDefinedData,
330    },
331    /// A channel representing a pipe.
332    Pipe {
333        /// If true, the pipe uses message mode. Otherwise, it uses byte mode.
334        message_mode: bool,
335        /// Provider-defined offer data.
336        user_defined: PipeUserDefinedData,
337        /// Pipe capabilities.
338        pipe_flags: PipeFlags,
339    },
340    /// A channel representing a Hyper-V socket.
341    HvSocket {
342        /// If true, this is a connect to the guest. Otherwise, this is a
343        /// connect from the guest.
344        is_connect: bool,
345        /// If true, the connection is for a container in the guest.
346        is_for_container: bool,
347        /// The silo ID to connect to. Use `Guid::ZERO` to not specify a silo ID.
348        silo_id: Guid,
349    },
350}
351
352impl Default for ChannelType {
353    fn default() -> Self {
354        Self::Device {
355            pipe_packets: false,
356        }
357    }
358}