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