xref: /aosp_15_r20/external/crosvm/third_party/vmm_vhost/src/frontend_server.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright (C) 2019-2021 Alibaba Cloud. All rights reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use std::fs::File;
5 use std::mem;
6 
7 use base::AsRawDescriptor;
8 
9 use crate::message::*;
10 use crate::BackendReq;
11 use crate::Connection;
12 use crate::Error;
13 use crate::HandlerResult;
14 use crate::Result;
15 
16 /// Trait for vhost-user frontends to respond to requests from the backend.
17 ///
18 /// Each method corresponds to a vhost-user protocol method. See the specification for details.
19 pub trait Frontend {
20     /// Handle device configuration change notifications.
handle_config_change(&mut self) -> HandlerResult<u64>21     fn handle_config_change(&mut self) -> HandlerResult<u64> {
22         Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
23     }
24 
25     /// Handle shared memory region mapping requests.
shmem_map( &mut self, _req: &VhostUserShmemMapMsg, _fd: &dyn AsRawDescriptor, ) -> HandlerResult<u64>26     fn shmem_map(
27         &mut self,
28         _req: &VhostUserShmemMapMsg,
29         _fd: &dyn AsRawDescriptor,
30     ) -> HandlerResult<u64> {
31         Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
32     }
33 
34     /// Handle shared memory region unmapping requests.
shmem_unmap(&mut self, _req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64>35     fn shmem_unmap(&mut self, _req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> {
36         Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
37     }
38 
39     // fn handle_iotlb_msg(&mut self, iotlb: VhostUserIotlb);
40     // fn handle_vring_host_notifier(&mut self, area: VhostUserVringArea, fd: RawDescriptor);
41 
42     /// Handle GPU shared memory region mapping requests.
gpu_map( &mut self, _req: &VhostUserGpuMapMsg, _descriptor: &dyn AsRawDescriptor, ) -> HandlerResult<u64>43     fn gpu_map(
44         &mut self,
45         _req: &VhostUserGpuMapMsg,
46         _descriptor: &dyn AsRawDescriptor,
47     ) -> HandlerResult<u64> {
48         Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
49     }
50 
51     /// Handle external memory region mapping requests.
external_map(&mut self, _req: &VhostUserExternalMapMsg) -> HandlerResult<u64>52     fn external_map(&mut self, _req: &VhostUserExternalMapMsg) -> HandlerResult<u64> {
53         Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
54     }
55 }
56 
57 /// Handles requests from a vhost-user backend connection by dispatching them to [[Frontend]]
58 /// methods.
59 pub struct FrontendServer<S: Frontend> {
60     // underlying Unix domain socket for communication
61     pub(crate) sub_sock: Connection<BackendReq>,
62     // Protocol feature VHOST_USER_PROTOCOL_F_REPLY_ACK has been negotiated.
63     reply_ack_negotiated: bool,
64 
65     frontend: S,
66 }
67 
68 impl<S: Frontend> FrontendServer<S> {
69     /// Create a server to handle requests from `connection`.
new(frontend: S, connection: Connection<BackendReq>) -> Result<Self>70     pub(crate) fn new(frontend: S, connection: Connection<BackendReq>) -> Result<Self> {
71         Ok(FrontendServer {
72             sub_sock: connection,
73             reply_ack_negotiated: false,
74             frontend,
75         })
76     }
77 
78     /// Set the negotiation state of the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature.
79     ///
80     /// When the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature has been negotiated,
81     /// the "REPLY_ACK" flag will be set in the message header for every request message.
set_reply_ack_flag(&mut self, enable: bool)82     pub fn set_reply_ack_flag(&mut self, enable: bool) {
83         self.reply_ack_negotiated = enable;
84     }
85 
86     /// Get the underlying frontend
frontend_mut(&mut self) -> &mut S87     pub fn frontend_mut(&mut self) -> &mut S {
88         &mut self.frontend
89     }
90 
91     /// Process the next received request.
92     ///
93     /// The caller needs to:
94     /// - serialize calls to this function
95     /// - decide what to do when errer happens
96     /// - optional recover from failure
handle_request(&mut self) -> Result<u64>97     pub fn handle_request(&mut self) -> Result<u64> {
98         // The underlying communication channel is a Unix domain socket in
99         // stream mode, and recvmsg() is a little tricky here. To successfully
100         // receive attached file descriptors, we need to receive messages and
101         // corresponding attached file descriptors in this way:
102         // . recv messsage header and optional attached file
103         // . validate message header
104         // . recv optional message body and payload according size field in
105         //   message header
106         // . validate message body and optional payload
107         let (hdr, files) = self.sub_sock.recv_header()?;
108         self.check_attached_files(&hdr, &files)?;
109         let buf = self.sub_sock.recv_body_bytes(&hdr)?;
110         let size = buf.len();
111 
112         let res = match hdr.get_code() {
113             Ok(BackendReq::CONFIG_CHANGE_MSG) => {
114                 self.check_msg_size(&hdr, size, 0)?;
115                 self.frontend
116                     .handle_config_change()
117                     .map_err(Error::ReqHandlerError)
118             }
119             Ok(BackendReq::SHMEM_MAP) => {
120                 let msg = self.extract_msg_body::<VhostUserShmemMapMsg>(&hdr, size, &buf)?;
121                 // check_attached_files() has validated files
122                 self.frontend
123                     .shmem_map(&msg, &files[0])
124                     .map_err(Error::ReqHandlerError)
125             }
126             Ok(BackendReq::SHMEM_UNMAP) => {
127                 let msg = self.extract_msg_body::<VhostUserShmemUnmapMsg>(&hdr, size, &buf)?;
128                 self.frontend
129                     .shmem_unmap(&msg)
130                     .map_err(Error::ReqHandlerError)
131             }
132             Ok(BackendReq::GPU_MAP) => {
133                 let msg = self.extract_msg_body::<VhostUserGpuMapMsg>(&hdr, size, &buf)?;
134                 // check_attached_files() has validated files
135                 self.frontend
136                     .gpu_map(&msg, &files[0])
137                     .map_err(Error::ReqHandlerError)
138             }
139             Ok(BackendReq::EXTERNAL_MAP) => {
140                 let msg = self.extract_msg_body::<VhostUserExternalMapMsg>(&hdr, size, &buf)?;
141                 self.frontend
142                     .external_map(&msg)
143                     .map_err(Error::ReqHandlerError)
144             }
145             _ => Err(Error::InvalidMessage),
146         };
147 
148         self.send_reply(&hdr, &res)?;
149 
150         res
151     }
152 
check_msg_size( &self, hdr: &VhostUserMsgHeader<BackendReq>, size: usize, expected: usize, ) -> Result<()>153     fn check_msg_size(
154         &self,
155         hdr: &VhostUserMsgHeader<BackendReq>,
156         size: usize,
157         expected: usize,
158     ) -> Result<()> {
159         if hdr.get_size() as usize != expected
160             || hdr.is_reply()
161             || hdr.get_version() != 0x1
162             || size != expected
163         {
164             return Err(Error::InvalidMessage);
165         }
166         Ok(())
167     }
168 
check_attached_files( &self, hdr: &VhostUserMsgHeader<BackendReq>, files: &[File], ) -> Result<()>169     fn check_attached_files(
170         &self,
171         hdr: &VhostUserMsgHeader<BackendReq>,
172         files: &[File],
173     ) -> Result<()> {
174         let expected_num_files = match hdr.get_code().map_err(|_| Error::InvalidMessage)? {
175             // Expect a single file is passed.
176             BackendReq::SHMEM_MAP | BackendReq::GPU_MAP => 1,
177             _ => 0,
178         };
179 
180         if files.len() == expected_num_files {
181             Ok(())
182         } else {
183             Err(Error::InvalidMessage)
184         }
185     }
186 
extract_msg_body<T: Sized + VhostUserMsgValidator>( &self, hdr: &VhostUserMsgHeader<BackendReq>, size: usize, buf: &[u8], ) -> Result<T>187     fn extract_msg_body<T: Sized + VhostUserMsgValidator>(
188         &self,
189         hdr: &VhostUserMsgHeader<BackendReq>,
190         size: usize,
191         buf: &[u8],
192     ) -> Result<T> {
193         self.check_msg_size(hdr, size, mem::size_of::<T>())?;
194         // SAFETY: above check ensures that buf is `T` sized.
195         let msg = unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) };
196         if !msg.is_valid() {
197             return Err(Error::InvalidMessage);
198         }
199         Ok(msg)
200     }
201 
new_reply_header<T: Sized>( &self, req: &VhostUserMsgHeader<BackendReq>, ) -> Result<VhostUserMsgHeader<BackendReq>>202     fn new_reply_header<T: Sized>(
203         &self,
204         req: &VhostUserMsgHeader<BackendReq>,
205     ) -> Result<VhostUserMsgHeader<BackendReq>> {
206         Ok(VhostUserMsgHeader::new(
207             req.get_code().map_err(|_| Error::InvalidMessage)?,
208             VhostUserHeaderFlag::REPLY.bits(),
209             mem::size_of::<T>() as u32,
210         ))
211     }
212 
send_reply( &mut self, req: &VhostUserMsgHeader<BackendReq>, res: &Result<u64>, ) -> Result<()>213     fn send_reply(
214         &mut self,
215         req: &VhostUserMsgHeader<BackendReq>,
216         res: &Result<u64>,
217     ) -> Result<()> {
218         let code = req.get_code().map_err(|_| Error::InvalidMessage)?;
219         if code == BackendReq::SHMEM_MAP
220             || code == BackendReq::SHMEM_UNMAP
221             || code == BackendReq::GPU_MAP
222             || code == BackendReq::EXTERNAL_MAP
223             || (self.reply_ack_negotiated && req.is_need_reply())
224         {
225             let hdr = self.new_reply_header::<VhostUserU64>(req)?;
226             let def_err = libc::EINVAL;
227             let val = match res {
228                 Ok(n) => *n,
229                 Err(e) => match e {
230                     Error::ReqHandlerError(ioerr) => match ioerr.raw_os_error() {
231                         Some(rawerr) => -rawerr as u64,
232                         None => -def_err as u64,
233                     },
234                     _ => -def_err as u64,
235                 },
236             };
237             let msg = VhostUserU64::new(val);
238             self.sub_sock.send_message(&hdr, &msg, None)?;
239         }
240         Ok(())
241     }
242 }
243