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}