• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 
5 //! rutabaga_2d: Handles 2D virtio-gpu hypercalls.
6 
7 use std::cmp::max;
8 use std::cmp::min;
9 use std::cmp::Ordering;
10 use std::io::IoSliceMut;
11 
12 use crate::rutabaga_core::Rutabaga2DInfo;
13 use crate::rutabaga_core::RutabagaComponent;
14 use crate::rutabaga_core::RutabagaResource;
15 use crate::rutabaga_utils::*;
16 use crate::snapshot::RutabagaSnapshotReader;
17 use crate::snapshot::RutabagaSnapshotWriter;
18 
19 /// Transfers a resource from potentially many chunked src slices to a dst slice.
transfer_2d( resource_w: u32, resource_h: u32, rect_x: u32, rect_y: u32, rect_w: u32, rect_h: u32, dst_stride: u32, dst_offset: u64, mut dst: IoSliceMut, src_stride: u32, src_offset: u64, srcs: &[&[u8]], ) -> RutabagaResult<()>20 fn transfer_2d(
21     resource_w: u32,
22     resource_h: u32,
23     rect_x: u32,
24     rect_y: u32,
25     rect_w: u32,
26     rect_h: u32,
27     dst_stride: u32,
28     dst_offset: u64,
29     mut dst: IoSliceMut,
30     src_stride: u32,
31     src_offset: u64,
32     srcs: &[&[u8]],
33 ) -> RutabagaResult<()> {
34     if rect_w == 0 || rect_h == 0 {
35         return Ok(());
36     }
37 
38     checked_range!(checked_arithmetic!(rect_x + rect_w)?; <= resource_w)?;
39     checked_range!(checked_arithmetic!(rect_y + rect_h)?; <= resource_h)?;
40 
41     let bytes_per_pixel = 4u64;
42 
43     let rect_x = rect_x as u64;
44     let rect_y = rect_y as u64;
45     let rect_w = rect_w as u64;
46     let rect_h = rect_h as u64;
47 
48     let dst_stride = dst_stride as u64;
49     let dst_resource_offset = dst_offset + (rect_y * dst_stride) + (rect_x * bytes_per_pixel);
50 
51     let src_stride = src_stride as u64;
52     let src_resource_offset = src_offset + (rect_y * src_stride) + (rect_x * bytes_per_pixel);
53 
54     let mut next_src;
55     let mut next_line;
56     let mut current_height = 0u64;
57     let mut srcs = srcs.iter();
58     let mut src_opt = srcs.next();
59 
60     // Cumulative start offset of the current src.
61     let mut src_start_offset = 0u64;
62     while let Some(src) = src_opt {
63         if current_height >= rect_h {
64             break;
65         }
66 
67         let src_size = src.len() as u64;
68 
69         // Cumulative end offset of the current src.
70         let src_end_offset = checked_arithmetic!(src_start_offset + src_size)?;
71 
72         let src_line_vertical_offset = checked_arithmetic!(current_height * src_stride)?;
73         let src_line_horizontal_offset = checked_arithmetic!(rect_w * bytes_per_pixel)?;
74 
75         // Cumulative start/end offsets of the next line to copy within all srcs.
76         let src_line_start_offset =
77             checked_arithmetic!(src_resource_offset + src_line_vertical_offset)?;
78         let src_line_end_offset =
79             checked_arithmetic!(src_line_start_offset + src_line_horizontal_offset)?;
80 
81         // Clamp the line start/end offset to be inside the current src.
82         let src_copyable_start_offset = max(src_line_start_offset, src_start_offset);
83         let src_copyable_end_offset = min(src_line_end_offset, src_end_offset);
84 
85         if src_copyable_start_offset < src_copyable_end_offset {
86             let copyable_size =
87                 checked_arithmetic!(src_copyable_end_offset - src_copyable_start_offset)?;
88 
89             let offset_within_src = src_copyable_start_offset.saturating_sub(src_start_offset);
90 
91             match src_line_end_offset.cmp(&src_end_offset) {
92                 Ordering::Greater => {
93                     next_src = true;
94                     next_line = false;
95                 }
96                 Ordering::Equal => {
97                     next_src = true;
98                     next_line = true;
99                 }
100                 Ordering::Less => {
101                     next_src = false;
102                     next_line = true;
103                 }
104             }
105 
106             let src_end = offset_within_src + copyable_size;
107             let src_subslice = src
108                 .get(offset_within_src as usize..src_end as usize)
109                 .ok_or(RutabagaError::InvalidIovec)?;
110 
111             let dst_line_vertical_offset = checked_arithmetic!(current_height * dst_stride)?;
112             let dst_line_horizontal_offset =
113                 checked_arithmetic!(src_copyable_start_offset - src_line_start_offset)?;
114             let dst_line_offset =
115                 checked_arithmetic!(dst_line_vertical_offset + dst_line_horizontal_offset)?;
116             let dst_start_offset = checked_arithmetic!(dst_resource_offset + dst_line_offset)?;
117 
118             let dst_end_offset = dst_start_offset + copyable_size;
119             let dst_subslice = dst
120                 .get_mut(dst_start_offset as usize..dst_end_offset as usize)
121                 .ok_or(RutabagaError::InvalidIovec)?;
122 
123             dst_subslice.copy_from_slice(src_subslice);
124         } else if src_line_start_offset >= src_start_offset {
125             next_src = true;
126             next_line = false;
127         } else {
128             next_src = false;
129             next_line = true;
130         };
131 
132         if next_src {
133             src_start_offset = checked_arithmetic!(src_start_offset + src_size)?;
134             src_opt = srcs.next();
135         }
136 
137         if next_line {
138             current_height += 1;
139         }
140     }
141 
142     Ok(())
143 }
144 
145 pub struct Rutabaga2D {
146     fence_handler: RutabagaFenceHandler,
147 }
148 
149 impl Rutabaga2D {
init(fence_handler: RutabagaFenceHandler) -> RutabagaResult<Box<dyn RutabagaComponent>>150     pub fn init(fence_handler: RutabagaFenceHandler) -> RutabagaResult<Box<dyn RutabagaComponent>> {
151         Ok(Box::new(Rutabaga2D { fence_handler }))
152     }
153 }
154 
155 impl RutabagaComponent for Rutabaga2D {
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>156     fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
157         self.fence_handler.call(fence);
158         Ok(())
159     }
160 
create_3d( &self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>161     fn create_3d(
162         &self,
163         resource_id: u32,
164         resource_create_3d: ResourceCreate3D,
165     ) -> RutabagaResult<RutabagaResource> {
166         // All virtio formats are 4 bytes per pixel.
167         let resource_bpp = 4;
168         let resource_stride = resource_bpp * resource_create_3d.width;
169         let resource_size = (resource_stride as usize) * (resource_create_3d.height as usize);
170         let info_2d = Rutabaga2DInfo {
171             width: resource_create_3d.width,
172             height: resource_create_3d.height,
173             host_mem: vec![0; resource_size],
174         };
175 
176         Ok(RutabagaResource {
177             resource_id,
178             handle: None,
179             blob: false,
180             blob_mem: 0,
181             blob_flags: 0,
182             map_info: None,
183             info_2d: Some(info_2d),
184             info_3d: None,
185             vulkan_info: None,
186             backing_iovecs: None,
187             component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8),
188             size: resource_size as u64,
189             mapping: None,
190         })
191     }
192 
transfer_write( &self, _ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, ) -> RutabagaResult<()>193     fn transfer_write(
194         &self,
195         _ctx_id: u32,
196         resource: &mut RutabagaResource,
197         transfer: Transfer3D,
198     ) -> RutabagaResult<()> {
199         if transfer.is_empty() {
200             return Ok(());
201         }
202 
203         let mut info_2d = resource
204             .info_2d
205             .take()
206             .ok_or(RutabagaError::Invalid2DInfo)?;
207 
208         let iovecs = resource
209             .backing_iovecs
210             .take()
211             .ok_or(RutabagaError::InvalidIovec)?;
212 
213         // All offical virtio_gpu formats are 4 bytes per pixel.
214         let resource_bpp = 4;
215         let mut src_slices = Vec::with_capacity(iovecs.len());
216         for iovec in &iovecs {
217             // SAFETY:
218             // Safe because Rutabaga users should have already checked the iovecs.
219             let slice = unsafe { std::slice::from_raw_parts(iovec.base as *mut u8, iovec.len) };
220             src_slices.push(slice);
221         }
222 
223         let src_stride = resource_bpp * info_2d.width;
224         let src_offset = transfer.offset;
225 
226         let dst_stride = resource_bpp * info_2d.width;
227         let dst_offset = 0;
228 
229         transfer_2d(
230             info_2d.width,
231             info_2d.height,
232             transfer.x,
233             transfer.y,
234             transfer.w,
235             transfer.h,
236             dst_stride,
237             dst_offset,
238             IoSliceMut::new(info_2d.host_mem.as_mut_slice()),
239             src_stride,
240             src_offset,
241             &src_slices,
242         )?;
243 
244         resource.info_2d = Some(info_2d);
245         resource.backing_iovecs = Some(iovecs);
246         Ok(())
247     }
248 
transfer_read( &self, _ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>249     fn transfer_read(
250         &self,
251         _ctx_id: u32,
252         resource: &mut RutabagaResource,
253         transfer: Transfer3D,
254         buf: Option<IoSliceMut>,
255     ) -> RutabagaResult<()> {
256         let mut info_2d = resource
257             .info_2d
258             .take()
259             .ok_or(RutabagaError::Invalid2DInfo)?;
260 
261         // All offical virtio_gpu formats are 4 bytes per pixel.
262         let resource_bpp = 4;
263         let src_stride = resource_bpp * info_2d.width;
264         let src_offset = 0;
265         let dst_offset = 0;
266 
267         let dst_slice = buf.ok_or(RutabagaError::SpecViolation(
268             "need a destination slice for transfer read",
269         ))?;
270 
271         transfer_2d(
272             info_2d.width,
273             info_2d.height,
274             transfer.x,
275             transfer.y,
276             transfer.w,
277             transfer.h,
278             transfer.stride,
279             dst_offset,
280             dst_slice,
281             src_stride,
282             src_offset,
283             &[info_2d.host_mem.as_mut_slice()],
284         )?;
285 
286         resource.info_2d = Some(info_2d);
287         Ok(())
288     }
289 
snapshot(&self, writer: RutabagaSnapshotWriter) -> RutabagaResult<()>290     fn snapshot(&self, writer: RutabagaSnapshotWriter) -> RutabagaResult<()> {
291         let v = serde_json::Value::String("rutabaga2d".to_string());
292         writer.add_fragment("rutabaga2d_snapshot", &v)?;
293         Ok(())
294     }
295 
restore(&self, reader: RutabagaSnapshotReader) -> RutabagaResult<()>296     fn restore(&self, reader: RutabagaSnapshotReader) -> RutabagaResult<()> {
297         let _: serde_json::Value = reader.get_fragment("rutabaga2d_snapshot")?;
298         Ok(())
299     }
300 }
301