Skip to main content

alioth/virtio/
vhost.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fs::File;
16use std::os::fd::AsRawFd;
17use std::path::Path;
18use std::sync::Arc;
19
20use snafu::{ResultExt, Snafu};
21
22use crate::errors::{BoxTrace, DebugTrace, trace_error};
23use crate::mem::mapped::Ram;
24use crate::mem::{self, LayoutUpdated};
25use crate::sys::vhost::{
26    MemoryMultipleRegion, MemoryRegion, VhostFeature, VirtqAddr, VirtqFile, VirtqState,
27    vhost_get_backend_features, vhost_get_features, vhost_set_backend_features, vhost_set_features,
28    vhost_set_mem_table, vhost_set_owner, vhost_set_virtq_addr, vhost_set_virtq_base,
29    vhost_set_virtq_call, vhost_set_virtq_err, vhost_set_virtq_kick, vhost_set_virtq_num,
30    vhost_vsock_set_guest_cid, vhost_vsock_set_running,
31};
32
33#[trace_error]
34#[derive(Snafu, DebugTrace)]
35#[snafu(module, visibility(pub(crate)), context(suffix(false)))]
36pub enum Error {
37    #[snafu(display("Error from OS"), context(false))]
38    System { error: std::io::Error },
39    #[snafu(display("Cannot access device {path:?}"))]
40    AccessDevice {
41        path: Box<Path>,
42        error: std::io::Error,
43    },
44    #[snafu(display("vhost backend is missing device feature {feature:#x}"))]
45    VhostMissingDeviceFeature { feature: u128 },
46    #[snafu(display("vhost-{dev} signals an error of queue {index:#x}"))]
47    VhostQueueErr { dev: &'static str, index: u16 },
48}
49
50type Result<T, E = Error> = std::result::Result<T, E>;
51
52#[derive(Debug)]
53pub struct VhostDev {
54    fd: File,
55}
56
57impl VhostDev {
58    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
59        let fd = File::open(&path).context(error::AccessDevice {
60            path: path.as_ref(),
61        })?;
62        Ok(VhostDev { fd })
63    }
64
65    pub fn get_features(&self) -> Result<u64> {
66        let feat = unsafe { vhost_get_features(&self.fd) }?;
67        Ok(feat)
68    }
69
70    pub fn set_features(&self, val: &u64) -> Result<()> {
71        unsafe { vhost_set_features(&self.fd, val) }?;
72        Ok(())
73    }
74
75    pub fn get_backend_features(&self) -> Result<VhostFeature> {
76        let feat = unsafe { vhost_get_backend_features(&self.fd) }?;
77        Ok(VhostFeature::from_bits_retain(feat))
78    }
79
80    pub fn set_backend_features(&self, val: &VhostFeature) -> Result<()> {
81        unsafe { vhost_set_backend_features(&self.fd, &val.bits()) }?;
82        Ok(())
83    }
84
85    pub fn set_owner(&self) -> Result<()> {
86        unsafe { vhost_set_owner(&self.fd) }?;
87        Ok(())
88    }
89
90    pub fn set_virtq_num(&self, state: &VirtqState) -> Result<()> {
91        unsafe { vhost_set_virtq_num(&self.fd, state) }?;
92        Ok(())
93    }
94
95    pub fn set_virtq_addr(&self, addr: &VirtqAddr) -> Result<()> {
96        unsafe { vhost_set_virtq_addr(&self.fd, addr) }?;
97        Ok(())
98    }
99
100    pub fn set_virtq_base(&self, state: &VirtqState) -> Result<()> {
101        unsafe { vhost_set_virtq_base(&self.fd, state) }?;
102        Ok(())
103    }
104
105    pub fn set_virtq_kick(&self, file: &VirtqFile) -> Result<()> {
106        unsafe { vhost_set_virtq_kick(&self.fd, file) }?;
107        Ok(())
108    }
109
110    pub fn set_virtq_call(&self, file: &VirtqFile) -> Result<()> {
111        unsafe { vhost_set_virtq_call(&self.fd, file) }?;
112        Ok(())
113    }
114
115    pub fn set_virtq_err(&self, file: &VirtqFile) -> Result<()> {
116        unsafe { vhost_set_virtq_err(&self.fd, file) }?;
117        Ok(())
118    }
119
120    pub fn set_mem_table<const N: usize>(&self, table: &MemoryMultipleRegion<N>) -> Result<()> {
121        unsafe { vhost_set_mem_table(&self.fd, table) }?;
122        Ok(())
123    }
124
125    pub fn vsock_set_guest_cid(&self, cid: u64) -> Result<()> {
126        unsafe { vhost_vsock_set_guest_cid(&self.fd, &cid) }?;
127        Ok(())
128    }
129
130    pub fn vsock_set_running(&self, val: bool) -> Result<()> {
131        unsafe { vhost_vsock_set_running(&self.fd, &(val as _)) }?;
132        Ok(())
133    }
134}
135
136#[derive(Debug)]
137pub struct UpdateVsockMem {
138    pub dev: Arc<VhostDev>,
139}
140
141impl LayoutUpdated for UpdateVsockMem {
142    fn ram_updated(&self, ram: &Ram) -> mem::Result<()> {
143        let mut table = MemoryMultipleRegion {
144            num: 0,
145            _padding: 0,
146            regions: [MemoryRegion::default(); 64],
147        };
148        for (index, (gpa, user_mem)) in ram.iter().enumerate() {
149            table.num += 1;
150            table.regions[index].gpa = gpa;
151            table.regions[index].hva = user_mem.addr() as u64;
152            table.regions[index].size = user_mem.size();
153        }
154        let ret = self.dev.set_mem_table(&table);
155        ret.box_trace(mem::error::ChangeLayout)?;
156        log::trace!(
157            "vhost-{}: updated mem table to {:x?}",
158            self.dev.fd.as_raw_fd(),
159            &table.regions[..table.num as usize]
160        );
161        Ok(())
162    }
163}