1use std::cmp::max;
9use std::collections::BTreeMap as Map;
10use std::collections::VecDeque;
11use std::convert::TryInto;
12use std::fs::File;
13use std::mem::size_of;
14use std::sync::Arc;
15use std::sync::Condvar;
16use std::sync::Mutex;
17use std::thread;
18
19use log::error;
20use zerocopy::AsBytes;
21use zerocopy::FromBytes;
22use zerocopy::FromZeroes;
23
24use crate::cross_domain::cross_domain_protocol::*;
25use crate::cross_domain::sys::channel;
26use crate::cross_domain::sys::channel_signal;
27use crate::cross_domain::sys::channel_wait;
28use crate::cross_domain::sys::descriptor_analysis;
29use crate::cross_domain::sys::read_volatile;
30use crate::cross_domain::sys::write_volatile;
31use crate::cross_domain::sys::Receiver;
32use crate::cross_domain::sys::Sender;
33use crate::cross_domain::sys::SystemStream;
34use crate::cross_domain::sys::WaitContext;
35use crate::rutabaga_core::RutabagaComponent;
36use crate::rutabaga_core::RutabagaContext;
37use crate::rutabaga_core::RutabagaResource;
38use crate::rutabaga_os::SafeDescriptor;
39use crate::rutabaga_utils::*;
40use crate::DrmFormat;
41use crate::ImageAllocationInfo;
42use crate::ImageMemoryRequirements;
43use crate::RutabagaGralloc;
44use crate::RutabagaGrallocFlags;
45
46mod cross_domain_protocol;
47mod sys;
48
49#[allow(dead_code)]
50const WAIT_CONTEXT_MAX: usize = 16;
51
52pub struct CrossDomainEvent {
53 token: CrossDomainToken,
54 hung_up: bool,
55 readable: bool,
56}
57
58#[derive(Copy, Clone, PartialEq, Eq)]
59pub enum CrossDomainToken {
60 ContextChannel,
61 WaylandReadPipe(u32),
62 Resample,
63 Kill,
64}
65
66const CROSS_DOMAIN_DEFAULT_BUFFER_SIZE: usize = 4096;
67const CROSS_DOMAIN_MAX_SEND_RECV_SIZE: usize =
68 CROSS_DOMAIN_DEFAULT_BUFFER_SIZE - size_of::<CrossDomainSendReceive>();
69
70pub(crate) enum CrossDomainItem {
71 ImageRequirements(ImageMemoryRequirements),
72 WaylandKeymap(SafeDescriptor),
73 #[allow(dead_code)] WaylandReadPipe(File),
75 WaylandWritePipe(File),
76}
77
78pub(crate) enum CrossDomainJob {
79 HandleFence(RutabagaFence),
80 #[allow(dead_code)] AddReadPipe(u32),
82 Finish,
83}
84
85enum RingWrite<'a, T> {
86 Write(T, Option<&'a [u8]>),
87 WriteFromFile(CrossDomainReadWrite, &'a mut File, bool),
88}
89
90pub(crate) type CrossDomainResources = Arc<Mutex<Map<u32, CrossDomainResource>>>;
91type CrossDomainJobs = Mutex<Option<VecDeque<CrossDomainJob>>>;
92pub(crate) type CrossDomainItemState = Arc<Mutex<CrossDomainItems>>;
93
94pub(crate) struct CrossDomainResource {
95 #[allow(dead_code)] pub handle: Option<Arc<RutabagaHandle>>,
97 pub backing_iovecs: Option<Vec<RutabagaIovec>>,
98}
99
100pub(crate) struct CrossDomainItems {
101 descriptor_id: u32,
102 requirements_blob_id: u32,
103 read_pipe_id: u32,
104 table: Map<u32, CrossDomainItem>,
105}
106
107pub(crate) struct CrossDomainState {
108 context_resources: CrossDomainResources,
109 query_ring_id: u32,
110 channel_ring_id: u32,
111 #[allow(dead_code)] pub(crate) connection: Option<SystemStream>,
113 jobs: CrossDomainJobs,
114 jobs_cvar: Condvar,
115}
116
117struct CrossDomainWorker {
118 wait_ctx: WaitContext,
119 state: Arc<CrossDomainState>,
120 pub(crate) item_state: CrossDomainItemState,
121 fence_handler: RutabagaFenceHandler,
122}
123
124pub(crate) struct CrossDomainContext {
125 #[allow(dead_code)] pub(crate) channels: Option<Vec<RutabagaChannel>>,
127 gralloc: Arc<Mutex<RutabagaGralloc>>,
128 pub(crate) state: Option<Arc<CrossDomainState>>,
129 pub(crate) context_resources: CrossDomainResources,
130 pub(crate) item_state: CrossDomainItemState,
131 fence_handler: RutabagaFenceHandler,
132 worker_thread: Option<thread::JoinHandle<RutabagaResult<()>>>,
133 pub(crate) resample_evt: Option<Sender>,
134 kill_evt: Option<Sender>,
135}
136
137pub struct CrossDomain {
140 channels: Option<Vec<RutabagaChannel>>,
141 gralloc: Arc<Mutex<RutabagaGralloc>>,
142 fence_handler: RutabagaFenceHandler,
143}
144
145pub(crate) fn add_item(item_state: &CrossDomainItemState, item: CrossDomainItem) -> u32 {
149 let mut items = item_state.lock().unwrap();
150
151 let item_id = match item {
152 CrossDomainItem::ImageRequirements(_) => {
153 items.requirements_blob_id += 2;
154 items.requirements_blob_id
155 }
156 CrossDomainItem::WaylandReadPipe(_) => {
157 items.read_pipe_id += 1;
158 max(items.read_pipe_id, CROSS_DOMAIN_PIPE_READ_START)
159 }
160 _ => {
161 items.descriptor_id += 2;
162 items.descriptor_id
163 }
164 };
165
166 items.table.insert(item_id, item);
167
168 item_id
169}
170
171impl Default for CrossDomainItems {
172 fn default() -> Self {
173 CrossDomainItems {
175 descriptor_id: 1,
176 requirements_blob_id: 2,
177 read_pipe_id: CROSS_DOMAIN_PIPE_READ_START,
178 table: Default::default(),
179 }
180 }
181}
182
183impl CrossDomainState {
184 fn new(
185 query_ring_id: u32,
186 channel_ring_id: u32,
187 context_resources: CrossDomainResources,
188 connection: Option<SystemStream>,
189 ) -> CrossDomainState {
190 CrossDomainState {
191 query_ring_id,
192 channel_ring_id,
193 context_resources,
194 connection,
195 jobs: Mutex::new(Some(VecDeque::new())),
196 jobs_cvar: Condvar::new(),
197 }
198 }
199
200 pub(crate) fn add_job(&self, job: CrossDomainJob) {
201 let mut jobs = self.jobs.lock().unwrap();
202 if let Some(queue) = jobs.as_mut() {
203 queue.push_back(job);
204 self.jobs_cvar.notify_one();
205 }
206 }
207
208 fn wait_for_job(&self) -> Option<CrossDomainJob> {
209 let mut jobs = self.jobs.lock().unwrap();
210 loop {
211 match jobs.as_mut()?.pop_front() {
212 Some(job) => return Some(job),
213 None => jobs = self.jobs_cvar.wait(jobs).unwrap(),
214 }
215 }
216 }
217
218 fn write_to_ring<T>(&self, mut ring_write: RingWrite<T>, ring_id: u32) -> RutabagaResult<usize>
219 where
220 T: FromBytes + AsBytes,
221 {
222 let mut context_resources = self.context_resources.lock().unwrap();
223 let mut bytes_read: usize = 0;
224
225 let resource = context_resources
226 .get_mut(&ring_id)
227 .ok_or(RutabagaError::InvalidResourceId)?;
228
229 let iovecs = resource
230 .backing_iovecs
231 .as_mut()
232 .ok_or(RutabagaError::InvalidIovec)?;
233 let slice =
234 unsafe { std::slice::from_raw_parts_mut(iovecs[0].base as *mut u8, iovecs[0].len) };
237
238 match ring_write {
239 RingWrite::Write(cmd, opaque_data_opt) => {
240 if slice.len() < size_of::<T>() {
241 return Err(RutabagaError::InvalidIovec);
242 }
243 let (cmd_slice, opaque_data_slice) = slice.split_at_mut(size_of::<T>());
244 cmd_slice.copy_from_slice(cmd.as_bytes());
245 if let Some(opaque_data) = opaque_data_opt {
246 if opaque_data_slice.len() < opaque_data.len() {
247 return Err(RutabagaError::InvalidIovec);
248 }
249 opaque_data_slice[..opaque_data.len()].copy_from_slice(opaque_data);
250 }
251 }
252 RingWrite::WriteFromFile(mut cmd_read, ref mut file, readable) => {
253 if slice.len() < size_of::<CrossDomainReadWrite>() {
254 return Err(RutabagaError::InvalidIovec);
255 }
256 let (cmd_slice, opaque_data_slice) =
257 slice.split_at_mut(size_of::<CrossDomainReadWrite>());
258
259 if readable {
260 bytes_read = read_volatile(file, opaque_data_slice)?;
261 }
262
263 if bytes_read == 0 {
264 cmd_read.hang_up = 1;
265 }
266
267 cmd_read.opaque_data_size = bytes_read.try_into()?;
268 cmd_slice.copy_from_slice(cmd_read.as_bytes());
269 }
270 }
271
272 Ok(bytes_read)
273 }
274}
275
276impl CrossDomainWorker {
277 fn new(
278 wait_ctx: WaitContext,
279 state: Arc<CrossDomainState>,
280 item_state: CrossDomainItemState,
281 fence_handler: RutabagaFenceHandler,
282 ) -> CrossDomainWorker {
283 CrossDomainWorker {
284 wait_ctx,
285 state,
286 item_state,
287 fence_handler,
288 }
289 }
290
291 fn handle_fence(
294 &mut self,
295 fence: RutabagaFence,
296 thread_resample_evt: &Receiver,
297 receive_buf: &mut [u8],
298 ) -> RutabagaResult<()> {
299 let events = self.wait_ctx.wait()?;
300
301 if let Some(event) = events.first() {
317 match event.token {
318 CrossDomainToken::ContextChannel => {
319 let (len, files) = self.state.receive_msg(receive_buf)?;
320 if len != 0 || !files.is_empty() {
321 let mut cmd_receive: CrossDomainSendReceive = Default::default();
322
323 let num_files = files.len();
324 cmd_receive.hdr.cmd = CROSS_DOMAIN_CMD_RECEIVE;
325 cmd_receive.num_identifiers = files.len().try_into()?;
326 cmd_receive.opaque_data_size = len.try_into()?;
327
328 let iter = cmd_receive
329 .identifiers
330 .iter_mut()
331 .zip(cmd_receive.identifier_types.iter_mut())
332 .zip(cmd_receive.identifier_sizes.iter_mut())
333 .zip(files)
334 .take(num_files);
335
336 for (((identifier, identifier_type), identifier_size), mut file) in iter {
337 descriptor_analysis(&mut file, identifier_type, identifier_size)?;
339
340 *identifier = match *identifier_type {
341 CROSS_DOMAIN_ID_TYPE_VIRTGPU_BLOB => add_item(
342 &self.item_state,
343 CrossDomainItem::WaylandKeymap(file.into()),
344 ),
345 CROSS_DOMAIN_ID_TYPE_WRITE_PIPE => add_item(
346 &self.item_state,
347 CrossDomainItem::WaylandWritePipe(file),
348 ),
349 _ => return Err(RutabagaError::InvalidCrossDomainItemType),
350 };
351 }
352
353 self.state.write_to_ring(
354 RingWrite::Write(cmd_receive, Some(&receive_buf[0..len])),
355 self.state.channel_ring_id,
356 )?;
357 self.fence_handler.call(fence);
358 }
359 }
360 CrossDomainToken::Resample => {
361 channel_wait(thread_resample_evt)?;
372 self.state.add_job(CrossDomainJob::HandleFence(fence));
373 }
374 CrossDomainToken::WaylandReadPipe(pipe_id) => {
375 let mut items = self.item_state.lock().unwrap();
376 let mut cmd_read: CrossDomainReadWrite = Default::default();
377 let bytes_read;
378
379 cmd_read.hdr.cmd = CROSS_DOMAIN_CMD_READ;
380 cmd_read.identifier = pipe_id;
381
382 let item = items
383 .table
384 .get_mut(&pipe_id)
385 .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
386
387 match item {
388 CrossDomainItem::WaylandReadPipe(ref mut file) => {
389 let ring_write =
390 RingWrite::WriteFromFile(cmd_read, file, event.readable);
391 bytes_read = self.state.write_to_ring::<CrossDomainReadWrite>(
392 ring_write,
393 self.state.channel_ring_id,
394 )?;
395
396 if event.hung_up && bytes_read == 0 {
398 self.wait_ctx
399 .delete(CrossDomainToken::WaylandReadPipe(pipe_id), file)?;
400 }
401 }
402 _ => return Err(RutabagaError::InvalidCrossDomainItemType),
403 }
404
405 if event.hung_up && bytes_read == 0 {
406 items.table.remove(&pipe_id);
407 }
408
409 self.fence_handler.call(fence);
410 }
411 CrossDomainToken::Kill => {
412 self.fence_handler.call(fence);
413 }
414 }
415 }
416
417 Ok(())
418 }
419
420 fn run(
421 &mut self,
422 thread_kill_evt: Receiver,
423 thread_resample_evt: Receiver,
424 ) -> RutabagaResult<()> {
425 self.wait_ctx
426 .add(CrossDomainToken::Resample, &thread_resample_evt)?;
427 self.wait_ctx
428 .add(CrossDomainToken::Kill, &thread_kill_evt)?;
429 let mut receive_buf: Vec<u8> = vec![0; CROSS_DOMAIN_MAX_SEND_RECV_SIZE];
430
431 while let Some(job) = self.state.wait_for_job() {
432 match job {
433 CrossDomainJob::HandleFence(fence) => {
434 match self.handle_fence(fence, &thread_resample_evt, &mut receive_buf) {
435 Ok(()) => (),
436 Err(e) => {
437 error!("Worker halting due to: {}", e);
438 return Err(e);
439 }
440 }
441 }
442 CrossDomainJob::AddReadPipe(read_pipe_id) => {
443 let items = self.item_state.lock().unwrap();
444 let item = items
445 .table
446 .get(&read_pipe_id)
447 .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
448
449 match item {
450 CrossDomainItem::WaylandReadPipe(file) => self
451 .wait_ctx
452 .add(CrossDomainToken::WaylandReadPipe(read_pipe_id), file)?,
453 _ => return Err(RutabagaError::InvalidCrossDomainItemType),
454 }
455 }
456 CrossDomainJob::Finish => return Ok(()),
457 }
458 }
459
460 Ok(())
461 }
462}
463
464impl CrossDomain {
465 pub fn init(
468 channels: Option<Vec<RutabagaChannel>>,
469 fence_handler: RutabagaFenceHandler,
470 ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
471 let gralloc = RutabagaGralloc::new()?;
472 Ok(Box::new(CrossDomain {
473 channels,
474 gralloc: Arc::new(Mutex::new(gralloc)),
475 fence_handler,
476 }))
477 }
478}
479
480impl CrossDomainContext {
481 fn initialize(&mut self, cmd_init: &CrossDomainInit) -> RutabagaResult<()> {
482 if !self
483 .context_resources
484 .lock()
485 .unwrap()
486 .contains_key(&cmd_init.query_ring_id)
487 {
488 return Err(RutabagaError::InvalidResourceId);
489 }
490
491 let query_ring_id = cmd_init.query_ring_id;
492 let channel_ring_id = cmd_init.channel_ring_id;
493 let context_resources = self.context_resources.clone();
494
495 if cmd_init.channel_type != 0 {
497 if !self
498 .context_resources
499 .lock()
500 .unwrap()
501 .contains_key(&cmd_init.channel_ring_id)
502 {
503 return Err(RutabagaError::InvalidResourceId);
504 }
505
506 let connection = self.get_connection(cmd_init)?;
507
508 let (kill_evt, thread_kill_evt) = channel()?;
509 let (resample_evt, thread_resample_evt) = channel()?;
510
511 let mut wait_ctx = WaitContext::new()?;
512 match &connection {
513 Some(connection) => {
514 wait_ctx.add(CrossDomainToken::ContextChannel, connection)?;
515 }
516 None => return Err(RutabagaError::Unsupported),
517 };
518
519 let state = Arc::new(CrossDomainState::new(
520 query_ring_id,
521 channel_ring_id,
522 context_resources,
523 connection,
524 ));
525
526 let thread_state = state.clone();
527 let thread_items = self.item_state.clone();
528 let thread_fence_handler = self.fence_handler.clone();
529
530 let worker_result = thread::Builder::new()
531 .name("cross domain".to_string())
532 .spawn(move || -> RutabagaResult<()> {
533 CrossDomainWorker::new(
534 wait_ctx,
535 thread_state,
536 thread_items,
537 thread_fence_handler,
538 )
539 .run(thread_kill_evt, thread_resample_evt)
540 });
541
542 self.worker_thread = Some(worker_result.unwrap());
543 self.state = Some(state);
544 self.resample_evt = Some(resample_evt);
545 self.kill_evt = Some(kill_evt);
546 } else {
547 self.state = Some(Arc::new(CrossDomainState::new(
548 query_ring_id,
549 channel_ring_id,
550 context_resources,
551 None,
552 )));
553 }
554
555 Ok(())
556 }
557
558 fn get_image_requirements(
559 &mut self,
560 cmd_get_reqs: &CrossDomainGetImageRequirements,
561 ) -> RutabagaResult<()> {
562 let info = ImageAllocationInfo {
563 width: cmd_get_reqs.width,
564 height: cmd_get_reqs.height,
565 drm_format: DrmFormat::from(cmd_get_reqs.drm_format),
566 flags: RutabagaGrallocFlags::new(cmd_get_reqs.flags),
567 };
568
569 let reqs = self
570 .gralloc
571 .lock()
572 .unwrap()
573 .get_image_memory_requirements(info)?;
574
575 let mut response = CrossDomainImageRequirements {
576 strides: reqs.strides,
577 offsets: reqs.offsets,
578 modifier: reqs.modifier,
579 size: reqs.size,
580 blob_id: 0,
581 map_info: reqs.map_info,
582 memory_idx: -1,
583 physical_device_idx: -1,
584 };
585
586 if let Some(ref vk_info) = reqs.vulkan_info {
587 response.memory_idx = vk_info.memory_idx as i32;
588 response.physical_device_idx = -1;
591 }
592
593 if let Some(state) = &self.state {
594 response.blob_id = add_item(&self.item_state, CrossDomainItem::ImageRequirements(reqs));
595 state.write_to_ring(RingWrite::Write(response, None), state.query_ring_id)?;
596 Ok(())
597 } else {
598 Err(RutabagaError::InvalidCrossDomainState)
599 }
600 }
601
602 fn write(&self, cmd_write: &CrossDomainReadWrite, opaque_data: &[u8]) -> RutabagaResult<()> {
603 let mut items = self.item_state.lock().unwrap();
604
605 let item = items
609 .table
610 .remove(&cmd_write.identifier)
611 .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
612
613 let len: usize = cmd_write.opaque_data_size.try_into()?;
614 match item {
615 CrossDomainItem::WaylandWritePipe(file) => {
616 if len != 0 {
617 write_volatile(&file, opaque_data)?;
618 }
619
620 if cmd_write.hang_up == 0 {
621 items.table.insert(
622 cmd_write.identifier,
623 CrossDomainItem::WaylandWritePipe(file),
624 );
625 }
626
627 Ok(())
628 }
629 _ => Err(RutabagaError::InvalidCrossDomainItemType),
630 }
631 }
632}
633
634impl Drop for CrossDomainContext {
635 fn drop(&mut self) {
636 if let Some(state) = &self.state {
637 state.add_job(CrossDomainJob::Finish);
638 }
639
640 if let Some(kill_evt) = self.kill_evt.take() {
641 match channel_signal(&kill_evt) {
643 Ok(_) => (),
644 Err(e) => {
645 error!("failed to write cross domain kill event: {}", e);
646 }
647 }
648
649 if let Some(worker_thread) = self.worker_thread.take() {
650 let _ = worker_thread.join();
651 }
652 }
653 }
654}
655
656#[repr(C)]
657#[derive(Copy, Clone, Default, AsBytes, FromZeroes, FromBytes)]
658struct CrossDomainInitLegacy {
659 hdr: CrossDomainHeader,
660 query_ring_id: u32,
661 channel_type: u32,
662}
663
664impl RutabagaContext for CrossDomainContext {
665 fn context_create_blob(
666 &mut self,
667 resource_id: u32,
668 resource_create_blob: ResourceCreateBlob,
669 handle_opt: Option<RutabagaHandle>,
670 ) -> RutabagaResult<RutabagaResource> {
671 let item_id = resource_create_blob.blob_id as u32;
672
673 if item_id % 2 == 0 {
677 let items = self.item_state.lock().unwrap();
678 let item = items
679 .table
680 .get(&item_id)
681 .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
682
683 match item {
684 CrossDomainItem::ImageRequirements(reqs) => {
685 if reqs.size != resource_create_blob.size {
686 return Err(RutabagaError::SpecViolation("blob size mismatch"));
687 }
688
689 let hnd = match handle_opt {
694 Some(handle) => handle,
695 None => self.gralloc.lock().unwrap().allocate_memory(*reqs)?,
696 };
697
698 let info_3d = Resource3DInfo {
699 width: reqs.info.width,
700 height: reqs.info.height,
701 drm_fourcc: reqs.info.drm_format.into(),
702 strides: reqs.strides,
703 offsets: reqs.offsets,
704 modifier: reqs.modifier,
705 guest_cpu_mappable: (resource_create_blob.blob_flags
706 & RUTABAGA_BLOB_FLAG_USE_MAPPABLE)
707 != 0,
708 };
709
710 Ok(RutabagaResource {
711 resource_id,
712 handle: Some(Arc::new(hnd)),
713 blob: true,
714 blob_mem: resource_create_blob.blob_mem,
715 blob_flags: resource_create_blob.blob_flags,
716 map_info: Some(reqs.map_info | RUTABAGA_MAP_ACCESS_RW),
717 info_2d: None,
718 info_3d: Some(info_3d),
719 vulkan_info: reqs.vulkan_info,
720 backing_iovecs: None,
721 component_mask: 1 << (RutabagaComponentType::CrossDomain as u8),
722 size: resource_create_blob.size,
723 mapping: None,
724 })
725 }
726 _ => Err(RutabagaError::InvalidCrossDomainItemType),
727 }
728 } else {
729 let item = self
730 .item_state
731 .lock()
732 .unwrap()
733 .table
734 .remove(&item_id)
735 .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
736
737 match item {
738 CrossDomainItem::WaylandKeymap(descriptor) => {
739 let hnd = RutabagaHandle {
740 os_handle: descriptor,
741 handle_type: RUTABAGA_MEM_HANDLE_TYPE_SHM,
742 };
743
744 Ok(RutabagaResource {
745 resource_id,
746 handle: Some(Arc::new(hnd)),
747 blob: true,
748 blob_mem: resource_create_blob.blob_mem,
749 blob_flags: resource_create_blob.blob_flags,
750 map_info: Some(RUTABAGA_MAP_CACHE_CACHED | RUTABAGA_MAP_ACCESS_READ),
751 info_2d: None,
752 info_3d: None,
753 vulkan_info: None,
754 backing_iovecs: None,
755 component_mask: 1 << (RutabagaComponentType::CrossDomain as u8),
756 size: resource_create_blob.size,
757 mapping: None,
758 })
759 }
760 _ => Err(RutabagaError::InvalidCrossDomainItemType),
761 }
762 }
763 }
764
765 fn submit_cmd(&mut self, mut commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> {
766 if !fence_ids.is_empty() {
767 return Err(RutabagaError::Unsupported);
768 }
769
770 while !commands.is_empty() {
771 let hdr = CrossDomainHeader::read_from_prefix(commands.as_bytes())
772 .ok_or(RutabagaError::InvalidCommandBuffer)?;
773
774 match hdr.cmd {
775 CROSS_DOMAIN_CMD_INIT => {
776 let cmd_init = match CrossDomainInit::read_from_prefix(commands.as_bytes()) {
777 Some(cmd_init) => cmd_init,
778 None => {
779 if let Some(cmd_init) =
780 CrossDomainInitLegacy::read_from_prefix(commands.as_bytes())
781 {
782 CrossDomainInit {
783 hdr: cmd_init.hdr,
784 query_ring_id: cmd_init.query_ring_id,
785 channel_ring_id: cmd_init.query_ring_id,
786 channel_type: cmd_init.channel_type,
787 }
788 } else {
789 return Err(RutabagaError::InvalidCommandBuffer);
790 }
791 }
792 };
793
794 self.initialize(&cmd_init)?;
795 }
796 CROSS_DOMAIN_CMD_GET_IMAGE_REQUIREMENTS => {
797 let cmd_get_reqs =
798 CrossDomainGetImageRequirements::read_from_prefix(commands.as_bytes())
799 .ok_or(RutabagaError::InvalidCommandBuffer)?;
800
801 self.get_image_requirements(&cmd_get_reqs)?;
802 }
803 CROSS_DOMAIN_CMD_SEND => {
804 let opaque_data_offset = size_of::<CrossDomainSendReceive>();
805 let cmd_send = CrossDomainSendReceive::read_from_prefix(commands.as_bytes())
806 .ok_or(RutabagaError::InvalidCommandBuffer)?;
807
808 let opaque_data = commands
809 .get_mut(
810 opaque_data_offset
811 ..opaque_data_offset + cmd_send.opaque_data_size as usize,
812 )
813 .ok_or(RutabagaError::InvalidCommandSize(
814 cmd_send.opaque_data_size as usize,
815 ))?;
816
817 self.send(&cmd_send, opaque_data)?;
818 }
819 CROSS_DOMAIN_CMD_POLL => {
820 }
822 CROSS_DOMAIN_CMD_WRITE => {
823 let opaque_data_offset = size_of::<CrossDomainReadWrite>();
824 let cmd_write = CrossDomainReadWrite::read_from_prefix(commands.as_bytes())
825 .ok_or(RutabagaError::InvalidCommandBuffer)?;
826
827 let opaque_data = commands
828 .get_mut(
829 opaque_data_offset
830 ..opaque_data_offset + cmd_write.opaque_data_size as usize,
831 )
832 .ok_or(RutabagaError::InvalidCommandSize(
833 cmd_write.opaque_data_size as usize,
834 ))?;
835
836 self.write(&cmd_write, opaque_data)?;
837 }
838 _ => return Err(RutabagaError::SpecViolation("invalid cross domain command")),
839 }
840
841 commands = commands
842 .get_mut(hdr.cmd_size as usize..)
843 .ok_or(RutabagaError::InvalidCommandSize(hdr.cmd_size as usize))?;
844 }
845
846 Ok(())
847 }
848
849 fn attach(&mut self, resource: &mut RutabagaResource) {
850 if resource.blob_mem == RUTABAGA_BLOB_MEM_GUEST {
851 self.context_resources.lock().unwrap().insert(
852 resource.resource_id,
853 CrossDomainResource {
854 handle: None,
855 backing_iovecs: resource.backing_iovecs.take(),
856 },
857 );
858 } else if let Some(ref handle) = resource.handle {
859 self.context_resources.lock().unwrap().insert(
860 resource.resource_id,
861 CrossDomainResource {
862 handle: Some(handle.clone()),
863 backing_iovecs: None,
864 },
865 );
866 }
867 }
868
869 fn detach(&mut self, resource: &RutabagaResource) {
870 self.context_resources
871 .lock()
872 .unwrap()
873 .remove(&resource.resource_id);
874 }
875
876 fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
877 match fence.ring_idx as u32 {
878 CROSS_DOMAIN_QUERY_RING => self.fence_handler.call(fence),
879 CROSS_DOMAIN_CHANNEL_RING => {
880 if let Some(state) = &self.state {
881 state.add_job(CrossDomainJob::HandleFence(fence));
882 }
883 }
884 _ => return Err(RutabagaError::SpecViolation("unexpected ring type")),
885 }
886
887 Ok(())
888 }
889
890 fn component_type(&self) -> RutabagaComponentType {
891 RutabagaComponentType::CrossDomain
892 }
893}
894
895impl RutabagaComponent for CrossDomain {
896 fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
897 (0u32, size_of::<CrossDomainCapabilities>() as u32)
898 }
899
900 fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
901 let mut caps: CrossDomainCapabilities = Default::default();
902 if let Some(ref channels) = self.channels {
903 for channel in channels {
904 caps.supported_channels = 1 << channel.channel_type;
905 }
906 }
907
908 if self.gralloc.lock().unwrap().supports_dmabuf() {
909 caps.supports_dmabuf = 1;
910 }
911
912 if self.gralloc.lock().unwrap().supports_external_gpu_memory() {
913 caps.supports_external_gpu_memory = 1;
914 }
915
916 caps.version = 1;
918 caps.as_bytes().to_vec()
919 }
920
921 fn create_blob(
922 &mut self,
923 _ctx_id: u32,
924 resource_id: u32,
925 resource_create_blob: ResourceCreateBlob,
926 iovec_opt: Option<Vec<RutabagaIovec>>,
927 _handle_opt: Option<RutabagaHandle>,
928 ) -> RutabagaResult<RutabagaResource> {
929 if resource_create_blob.blob_mem != RUTABAGA_BLOB_MEM_GUEST
930 && resource_create_blob.blob_flags != RUTABAGA_BLOB_FLAG_USE_MAPPABLE
931 {
932 return Err(RutabagaError::SpecViolation(
933 "expected only guest memory blobs",
934 ));
935 }
936
937 Ok(RutabagaResource {
938 resource_id,
939 handle: None,
940 blob: true,
941 blob_mem: resource_create_blob.blob_mem,
942 blob_flags: resource_create_blob.blob_flags,
943 map_info: None,
944 info_2d: None,
945 info_3d: None,
946 vulkan_info: None,
947 backing_iovecs: iovec_opt,
948 component_mask: 1 << (RutabagaComponentType::CrossDomain as u8),
949 size: resource_create_blob.size,
950 mapping: None,
951 })
952 }
953
954 fn create_context(
955 &self,
956 _ctx_id: u32,
957 _context_init: u32,
958 _context_name: Option<&str>,
959 fence_handler: RutabagaFenceHandler,
960 ) -> RutabagaResult<Box<dyn RutabagaContext>> {
961 Ok(Box::new(CrossDomainContext {
962 channels: self.channels.clone(),
963 gralloc: self.gralloc.clone(),
964 state: None,
965 context_resources: Arc::new(Mutex::new(Default::default())),
966 item_state: Arc::new(Mutex::new(Default::default())),
967 fence_handler,
968 worker_thread: None,
969 resample_evt: None,
970 kill_evt: None,
971 }))
972 }
973
974 fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
978 self.fence_handler.call(fence);
979 Ok(())
980 }
981}