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