|
| 1 | +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | +// |
| 4 | +// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. |
| 5 | +// Use of this source code is governed by a BSD-style license that can be |
| 6 | +// found in the THIRD-PARTY file. |
| 7 | + |
| 8 | +use crate::virtio::DescriptorChain; |
| 9 | +use std::io::IoSlice; |
| 10 | +use std::ops::Deref; |
| 11 | +use vm_memory::{GuestMemory, GuestMemoryError, GuestMemoryMmap}; |
| 12 | + |
| 13 | +#[derive(Debug)] |
| 14 | +pub enum Error { |
| 15 | + /// We found a write-only descriptor where read-only was expected |
| 16 | + WriteOnlyDescriptor, |
| 17 | + /// An error happened with guest memory handling |
| 18 | + GuestMemory(vm_memory::GuestMemoryError), |
| 19 | +} |
| 20 | + |
| 21 | +impl From<GuestMemoryError> for Error { |
| 22 | + fn from(err: GuestMemoryError) -> Self { |
| 23 | + Error::GuestMemory(err) |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +type Result<T> = std::result::Result<T, Error>; |
| 28 | + |
| 29 | +/// This is essentially a wrapper of a `Vec<IoSlice>` which can be passed `writev`. |
| 30 | +/// |
| 31 | +/// It describes a buffer passed to us by the guest that is scattered across multiple |
| 32 | +/// memory regions. Additionally, this wrapper provides methods that allow reading arbitrary ranges |
| 33 | +/// of data from that buffer. |
| 34 | +#[derive(Debug)] |
| 35 | +pub(crate) struct IoVecBuffer<'a> { |
| 36 | + // container of the memory regions included in this IO vector |
| 37 | + vecs: Vec<IoSlice<'a>>, |
| 38 | + // Total length of the IoVecBuffer |
| 39 | + len: usize, |
| 40 | +} |
| 41 | + |
| 42 | +impl<'a> Deref for IoVecBuffer<'a> { |
| 43 | + type Target = [IoSlice<'a>]; |
| 44 | + |
| 45 | + fn deref(&self) -> &Self::Target { |
| 46 | + self.vecs.as_slice() |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +#[cfg(test)] |
| 51 | +impl<'a> From<&'a [u8]> for IoVecBuffer<'a> { |
| 52 | + fn from(buf: &'a [u8]) -> Self { |
| 53 | + Self { |
| 54 | + vecs: vec![IoSlice::new(buf)], |
| 55 | + len: buf.len(), |
| 56 | + } |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +impl<'a> IoVecBuffer<'a> { |
| 61 | + /// Create an `IoVecBuffer` from a `DescriptorChain` |
| 62 | + pub fn from_descriptor_chain(mem: &'a GuestMemoryMmap, head: DescriptorChain) -> Result<Self> { |
| 63 | + let mut vecs = vec![]; |
| 64 | + let mut len = 0usize; |
| 65 | + |
| 66 | + let mut next_descriptor = Some(head); |
| 67 | + while let Some(desc) = next_descriptor { |
| 68 | + if desc.is_write_only() { |
| 69 | + return Err(Error::WriteOnlyDescriptor); |
| 70 | + } |
| 71 | + |
| 72 | + // This is safe since we get `ptr` from `get_slice` which also checks the length |
| 73 | + // of the descriptor |
| 74 | + let ptr = mem.get_slice(desc.addr, desc.len as usize)?.as_ptr(); |
| 75 | + let slice = unsafe { std::slice::from_raw_parts(ptr, desc.len as usize) }; |
| 76 | + vecs.push(IoSlice::new(slice)); |
| 77 | + len += desc.len as usize; |
| 78 | + |
| 79 | + next_descriptor = desc.next_descriptor(); |
| 80 | + } |
| 81 | + |
| 82 | + Ok(Self { vecs, len }) |
| 83 | + } |
| 84 | + |
| 85 | + /// Get the total length of the memory regions covered by this `IoVecBuffer` |
| 86 | + pub fn len(&self) -> usize { |
| 87 | + self.len |
| 88 | + } |
| 89 | + |
| 90 | + /// Reads a number of bytes from the `IoVecBuffer` starting at a given offset. |
| 91 | + /// |
| 92 | + /// This will try to fill `buf` reading bytes from the `IoVecBuffer` starting from |
| 93 | + /// the given offset. |
| 94 | + /// |
| 95 | + /// # Retruns |
| 96 | + /// |
| 97 | + /// The number of bytes read (if any) |
| 98 | + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> Option<usize> { |
| 99 | + // We can't read past the end of this `IoVecBuffer` |
| 100 | + if offset >= self.len() as u64 { |
| 101 | + return None; |
| 102 | + } |
| 103 | + |
| 104 | + // We try to fill up `buf` with as many bytes as we have |
| 105 | + let size = std::cmp::min(buf.len(), self.len() - offset as usize); |
| 106 | + // This is the last byte of `self` that we will reado out |
| 107 | + let end = offset as usize + size; |
| 108 | + // byte index in `self` |
| 109 | + let mut seg_start = 0; |
| 110 | + // byte index in `buf` |
| 111 | + let mut buf_start = 0; |
| 112 | + |
| 113 | + for seg in self.vecs.iter() { |
| 114 | + let seg_end = seg_start + seg.len(); |
| 115 | + |
| 116 | + let mut write_start = std::cmp::max(seg_start, offset as usize); |
| 117 | + let mut write_end = std::cmp::min(seg_end, end); |
| 118 | + |
| 119 | + if write_start < write_end { |
| 120 | + write_start -= seg_start; |
| 121 | + write_end -= seg_start; |
| 122 | + let bytes = write_end - write_start; |
| 123 | + let buf_end = buf_start + bytes; |
| 124 | + buf[buf_start..buf_end].copy_from_slice(&seg[write_start..write_end]); |
| 125 | + buf_start += bytes; |
| 126 | + } |
| 127 | + |
| 128 | + seg_start = seg_end; |
| 129 | + // The next segment is out of range, we are done here. |
| 130 | + if seg_start >= end { |
| 131 | + break; |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + Some(size) |
| 136 | + } |
| 137 | +} |
| 138 | + |
| 139 | +#[cfg(test)] |
| 140 | +pub mod tests { |
| 141 | + use super::IoVecBuffer; |
| 142 | + use crate::virtio::queue::{Queue, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; |
| 143 | + use crate::virtio::test_utils::VirtQueue; |
| 144 | + use vm_memory::{test_utils::create_anon_guest_memory, Bytes, GuestAddress, GuestMemoryMmap}; |
| 145 | + |
| 146 | + fn chain(is_write_only: bool) -> (Queue, GuestMemoryMmap) { |
| 147 | + let m = create_anon_guest_memory( |
| 148 | + &[ |
| 149 | + (GuestAddress(0), 0x10000), |
| 150 | + (GuestAddress(0x20000), 0x10000), |
| 151 | + (GuestAddress(0x40000), 0x10000), |
| 152 | + ], |
| 153 | + false, |
| 154 | + ) |
| 155 | + .unwrap(); |
| 156 | + |
| 157 | + let v: Vec<u8> = (0..=255).collect(); |
| 158 | + m.write_slice(&v, GuestAddress(0x20000)).unwrap(); |
| 159 | + |
| 160 | + let vq = VirtQueue::new(GuestAddress(0), &m, 16); |
| 161 | + |
| 162 | + let mut q = vq.create_queue(); |
| 163 | + q.ready = true; |
| 164 | + |
| 165 | + let flags = if is_write_only { |
| 166 | + VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE |
| 167 | + } else { |
| 168 | + VIRTQ_DESC_F_NEXT |
| 169 | + }; |
| 170 | + |
| 171 | + for j in 0..4 { |
| 172 | + vq.dtable[j].set(0x20000 + 64 * j as u64, 64, flags, (j + 1) as u16); |
| 173 | + } |
| 174 | + |
| 175 | + // one chain: (0, 1, 2, 3) |
| 176 | + vq.dtable[3].flags.set(flags & !VIRTQ_DESC_F_NEXT); |
| 177 | + vq.avail.ring[0].set(0); |
| 178 | + vq.avail.idx.set(1); |
| 179 | + |
| 180 | + (q, m) |
| 181 | + } |
| 182 | + |
| 183 | + #[test] |
| 184 | + fn test_access_mode() { |
| 185 | + let (mut q, mem) = chain(false); |
| 186 | + let head = q.pop(&mem).unwrap(); |
| 187 | + assert!(IoVecBuffer::from_descriptor_chain(&mem, head).is_ok()); |
| 188 | + |
| 189 | + let (mut q, mem) = chain(true); |
| 190 | + let head = q.pop(&mem).unwrap(); |
| 191 | + assert!(IoVecBuffer::from_descriptor_chain(&mem, head).is_err()); |
| 192 | + } |
| 193 | + |
| 194 | + #[test] |
| 195 | + fn test_iovec_length() { |
| 196 | + let (mut q, mem) = chain(false); |
| 197 | + let head = q.pop(&mem).unwrap(); |
| 198 | + |
| 199 | + let iovec = IoVecBuffer::from_descriptor_chain(&mem, head).unwrap(); |
| 200 | + assert_eq!(iovec.len(), 4 * 64); |
| 201 | + } |
| 202 | + |
| 203 | + #[test] |
| 204 | + fn test_iovec_read_at() { |
| 205 | + let (mut q, mem) = chain(false); |
| 206 | + let head = q.pop(&mem).unwrap(); |
| 207 | + |
| 208 | + let iovec = IoVecBuffer::from_descriptor_chain(&mem, head).unwrap(); |
| 209 | + |
| 210 | + let mut buf = vec![0; 5]; |
| 211 | + assert_eq!(iovec.read_at(&mut buf, 0), Some(5)); |
| 212 | + assert_eq!(buf, vec![0u8, 1, 2, 3, 4]); |
| 213 | + |
| 214 | + assert_eq!(iovec.read_at(&mut buf, 1), Some(5)); |
| 215 | + assert_eq!(buf, vec![1u8, 2, 3, 4, 5]); |
| 216 | + |
| 217 | + assert_eq!(iovec.read_at(&mut buf, 60), Some(5)); |
| 218 | + assert_eq!(buf, vec![60u8, 61, 62, 63, 64]); |
| 219 | + |
| 220 | + assert_eq!(iovec.read_at(&mut buf, 252), Some(4)); |
| 221 | + assert_eq!(buf[0..4], vec![252u8, 253, 254, 255]); |
| 222 | + |
| 223 | + assert_eq!(iovec.read_at(&mut buf, 256), None); |
| 224 | + } |
| 225 | +} |
0 commit comments