devices/virtio/vhost_user_frontend/
handler.rs

1// Copyright 2021 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use base::error;
6use base::info;
7use base::AsRawDescriptor;
8use base::Protection;
9use base::SafeDescriptor;
10use hypervisor::MemCacheType;
11use vm_control::VmMemorySource;
12use vmm_vhost::message::VhostUserExternalMapMsg;
13use vmm_vhost::message::VhostUserGpuMapMsg;
14use vmm_vhost::message::VhostUserMMap;
15use vmm_vhost::message::VhostUserMMapFlags;
16use vmm_vhost::Frontend;
17use vmm_vhost::FrontendServer;
18use vmm_vhost::HandlerResult;
19
20use crate::virtio::Interrupt;
21use crate::virtio::SharedMemoryMapper;
22
23pub(crate) type BackendReqHandler = FrontendServer<BackendReqHandlerImpl>;
24
25struct SharedMapperState {
26    mapper: Box<dyn SharedMemoryMapper>,
27    shmid: u8,
28}
29
30pub struct BackendReqHandlerImpl {
31    interrupt: Option<Interrupt>,
32    shared_mapper_state: Option<SharedMapperState>,
33    is_remote_backend: bool,
34}
35
36impl BackendReqHandlerImpl {
37    pub(crate) fn new(is_remote_backend: bool) -> Self {
38        BackendReqHandlerImpl {
39            interrupt: None,
40            shared_mapper_state: None,
41            is_remote_backend,
42        }
43    }
44
45    pub(crate) fn set_interrupt(&mut self, interrupt: Interrupt) {
46        self.interrupt = Some(interrupt);
47    }
48
49    pub(crate) fn set_shared_mapper_state(
50        &mut self,
51        mapper: Box<dyn SharedMemoryMapper>,
52        shmid: u8,
53    ) {
54        self.shared_mapper_state = Some(SharedMapperState { mapper, shmid });
55    }
56}
57
58impl Frontend for BackendReqHandlerImpl {
59    fn shmem_map(&mut self, req: &VhostUserMMap, fd: &dyn AsRawDescriptor) -> HandlerResult<()> {
60        let shared_mapper_state = self
61            .shared_mapper_state
62            .as_mut()
63            .ok_or_else(|| std::io::Error::from_raw_os_error(libc::EINVAL))?;
64        if req.shmid != shared_mapper_state.shmid {
65            error!(
66                "bad shmid {}, expected {}",
67                req.shmid, shared_mapper_state.shmid
68            );
69            return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
70        }
71        shared_mapper_state
72            .mapper
73            .add_mapping(
74                VmMemorySource::Descriptor {
75                    descriptor: SafeDescriptor::try_from(fd)
76                        .map_err(|_| std::io::Error::from_raw_os_error(libc::EIO))?,
77                    offset: req.fd_offset,
78                    size: req.len,
79                },
80                req.shm_offset,
81                if req.flags.contains(VhostUserMMapFlags::MAP_RW) {
82                    Protection::read_write()
83                } else {
84                    Protection::read()
85                },
86                MemCacheType::CacheCoherent,
87            )
88            .map_err(|e| {
89                error!("failed to create mapping {:?}", e);
90                std::io::Error::other(e.context("add descriptor mapping"))
91            })
92    }
93
94    fn shmem_unmap(&mut self, req: &VhostUserMMap) -> HandlerResult<()> {
95        let shared_mapper_state = self
96            .shared_mapper_state
97            .as_mut()
98            .ok_or_else(|| std::io::Error::from_raw_os_error(libc::EINVAL))?;
99        if req.shmid != shared_mapper_state.shmid {
100            error!(
101                "bad shmid {}, expected {}",
102                req.shmid, shared_mapper_state.shmid
103            );
104            return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
105        }
106        shared_mapper_state
107            .mapper
108            .remove_mapping(req.shm_offset)
109            .map_err(|e| {
110                error!("failed to remove mapping {:?}", e);
111                std::io::Error::other(e.context("remove memory mapping based on shm offset"))
112            })
113    }
114
115    fn gpu_map(
116        &mut self,
117        req: &VhostUserGpuMapMsg,
118        descriptor: &dyn AsRawDescriptor,
119    ) -> HandlerResult<()> {
120        let shared_mapper_state = self
121            .shared_mapper_state
122            .as_mut()
123            .ok_or_else(|| std::io::Error::from_raw_os_error(libc::EINVAL))?;
124        if req.shmid != shared_mapper_state.shmid {
125            error!(
126                "bad shmid {}, expected {}",
127                req.shmid, shared_mapper_state.shmid
128            );
129            return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
130        }
131        shared_mapper_state
132            .mapper
133            .add_mapping(
134                VmMemorySource::Vulkan {
135                    descriptor: SafeDescriptor::try_from(descriptor)
136                        .map_err(|_| std::io::Error::from_raw_os_error(libc::EIO))?,
137                    handle_type: req.handle_type,
138                    memory_idx: req.memory_idx,
139                    device_uuid: req.device_uuid,
140                    driver_uuid: req.driver_uuid,
141                    size: req.len,
142                },
143                req.shm_offset,
144                Protection::read_write(),
145                MemCacheType::CacheCoherent,
146            )
147            .map_err(|e| {
148                error!("failed to create mapping {:?}", e);
149                std::io::Error::other(e.context("add Vulkan source mapping"))
150            })
151    }
152
153    fn external_map(&mut self, req: &VhostUserExternalMapMsg) -> HandlerResult<()> {
154        // Only allow EXTERNAL_MAP when the backend is in-process because it contains raw pointers
155        // that can't be trusted between processes.
156        if self.is_remote_backend {
157            return Err(std::io::Error::from_raw_os_error(libc::EPERM));
158        }
159
160        let shared_mapper_state = self
161            .shared_mapper_state
162            .as_mut()
163            .ok_or_else(|| std::io::Error::from_raw_os_error(libc::EINVAL))?;
164        if req.shmid != shared_mapper_state.shmid {
165            error!(
166                "bad shmid {}, expected {}",
167                req.shmid, shared_mapper_state.shmid
168            );
169            return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
170        }
171        shared_mapper_state
172            .mapper
173            .add_mapping(
174                VmMemorySource::ExternalMapping {
175                    ptr: req.ptr,
176                    size: req.len,
177                },
178                req.shm_offset,
179                Protection::read_write(),
180                MemCacheType::CacheCoherent,
181            )
182            .map_err(|e| {
183                error!("failed to create mapping {:?}", e);
184                std::io::Error::other(e.context("add external mapping"))
185            })
186    }
187
188    fn handle_config_change(&mut self) -> HandlerResult<()> {
189        info!("Handle Config Change called");
190        match &self.interrupt {
191            Some(interrupt) => {
192                interrupt.signal_config_changed();
193                Ok(())
194            }
195            None => {
196                error!("cannot send interrupt");
197                Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
198            }
199        }
200    }
201}