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}