1 // Copyright 2024 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 use std::collections::VecDeque; 6 use std::mem::size_of; 7 8 use zerocopy::AsBytes; 9 use zerocopy::FromBytes; 10 11 use crate::bytestream::Reader; 12 use crate::bytestream::Writer; 13 use crate::ipc::kumquat_gpu_protocol::*; 14 use crate::rutabaga_os::AsBorrowedDescriptor; 15 use crate::rutabaga_os::AsRawDescriptor; 16 use crate::rutabaga_os::OwnedDescriptor; 17 use crate::rutabaga_os::RawDescriptor; 18 use crate::rutabaga_os::Tube; 19 use crate::rutabaga_os::DEFAULT_RAW_DESCRIPTOR; 20 use crate::rutabaga_utils::RutabagaError; 21 use crate::rutabaga_utils::RutabagaHandle; 22 use crate::rutabaga_utils::RutabagaResult; 23 use crate::rutabaga_utils::RUTABAGA_FENCE_HANDLE_TYPE_EVENT_FD; 24 25 const MAX_DESCRIPTORS: usize = 1; 26 const MAX_COMMAND_SIZE: usize = 4096; 27 28 pub struct RutabagaStream { 29 stream: Tube, 30 write_buffer: [u8; MAX_COMMAND_SIZE], 31 read_buffer: [u8; MAX_COMMAND_SIZE], 32 descriptors: [RawDescriptor; MAX_DESCRIPTORS], 33 } 34 35 impl RutabagaStream { new(stream: Tube) -> RutabagaStream36 pub fn new(stream: Tube) -> RutabagaStream { 37 RutabagaStream { 38 stream, 39 write_buffer: [0; MAX_COMMAND_SIZE], 40 read_buffer: [0; MAX_COMMAND_SIZE], 41 descriptors: [DEFAULT_RAW_DESCRIPTOR; MAX_DESCRIPTORS], 42 } 43 } 44 write<T: FromBytes + AsBytes>( &mut self, encode: KumquatGpuProtocolWrite<T>, ) -> RutabagaResult<()>45 pub fn write<T: FromBytes + AsBytes>( 46 &mut self, 47 encode: KumquatGpuProtocolWrite<T>, 48 ) -> RutabagaResult<()> { 49 let mut writer = Writer::new(&mut self.write_buffer); 50 let mut num_descriptors = 0; 51 52 let _handle_opt: Option<RutabagaHandle> = match encode { 53 KumquatGpuProtocolWrite::Cmd(cmd) => { 54 writer.write_obj(cmd)?; 55 None 56 } 57 KumquatGpuProtocolWrite::CmdWithHandle(cmd, handle) => { 58 writer.write_obj(cmd)?; 59 num_descriptors = 1; 60 self.descriptors[0] = handle.os_handle.as_raw_descriptor(); 61 Some(handle) 62 } 63 KumquatGpuProtocolWrite::CmdWithData(cmd, data) => { 64 writer.write_obj(cmd)?; 65 writer.write_all(&data)?; 66 None 67 } 68 }; 69 70 let bytes_written = writer.bytes_written(); 71 self.stream.send( 72 &self.write_buffer[0..bytes_written], 73 &self.descriptors[0..num_descriptors], 74 )?; 75 Ok(()) 76 } 77 read(&mut self) -> RutabagaResult<Vec<KumquatGpuProtocol>>78 pub fn read(&mut self) -> RutabagaResult<Vec<KumquatGpuProtocol>> { 79 let mut vec: Vec<KumquatGpuProtocol> = Vec::new(); 80 let (bytes_read, descriptor_vec) = self.stream.receive(&mut self.read_buffer)?; 81 let mut descriptors: VecDeque<OwnedDescriptor> = descriptor_vec.into(); 82 83 if bytes_read == 0 { 84 vec.push(KumquatGpuProtocol::OkNoData); 85 return Ok(vec); 86 } 87 88 let mut reader = Reader::new(&self.read_buffer[0..bytes_read]); 89 while reader.available_bytes() != 0 { 90 let hdr = reader.peek_obj::<kumquat_gpu_protocol_ctrl_hdr>()?; 91 let protocol = match hdr.type_ { 92 KUMQUAT_GPU_PROTOCOL_GET_NUM_CAPSETS => { 93 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 94 KumquatGpuProtocol::GetNumCapsets 95 } 96 KUMQUAT_GPU_PROTOCOL_GET_CAPSET_INFO => { 97 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 98 KumquatGpuProtocol::GetCapsetInfo(hdr.payload) 99 } 100 KUMQUAT_GPU_PROTOCOL_GET_CAPSET => { 101 KumquatGpuProtocol::GetCapset(reader.read_obj()?) 102 } 103 KUMQUAT_GPU_PROTOCOL_CTX_CREATE => { 104 KumquatGpuProtocol::CtxCreate(reader.read_obj()?) 105 } 106 KUMQUAT_GPU_PROTOCOL_CTX_DESTROY => { 107 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 108 KumquatGpuProtocol::CtxDestroy(hdr.payload) 109 } 110 KUMQUAT_GPU_PROTOCOL_CTX_ATTACH_RESOURCE => { 111 KumquatGpuProtocol::CtxAttachResource(reader.read_obj()?) 112 } 113 KUMQUAT_GPU_PROTOCOL_CTX_DETACH_RESOURCE => { 114 KumquatGpuProtocol::CtxDetachResource(reader.read_obj()?) 115 } 116 KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_3D => { 117 KumquatGpuProtocol::ResourceCreate3d(reader.read_obj()?) 118 } 119 KUMQUAT_GPU_PROTOCOL_TRANSFER_TO_HOST_3D => { 120 let os_handle = descriptors 121 .pop_front() 122 .ok_or(RutabagaError::InvalidResourceId)?; 123 let resp: kumquat_gpu_protocol_transfer_host_3d = reader.read_obj()?; 124 125 let handle = RutabagaHandle { 126 os_handle, 127 handle_type: RUTABAGA_FENCE_HANDLE_TYPE_EVENT_FD, 128 }; 129 130 KumquatGpuProtocol::TransferToHost3d(resp, handle) 131 } 132 KUMQUAT_GPU_PROTOCOL_TRANSFER_FROM_HOST_3D => { 133 let os_handle = descriptors 134 .pop_front() 135 .ok_or(RutabagaError::InvalidResourceId)?; 136 let resp: kumquat_gpu_protocol_transfer_host_3d = reader.read_obj()?; 137 138 let handle = RutabagaHandle { 139 os_handle, 140 handle_type: RUTABAGA_FENCE_HANDLE_TYPE_EVENT_FD, 141 }; 142 143 KumquatGpuProtocol::TransferFromHost3d(resp, handle) 144 } 145 KUMQUAT_GPU_PROTOCOL_SUBMIT_3D => { 146 let cmd: kumquat_gpu_protocol_cmd_submit = reader.read_obj()?; 147 if reader.available_bytes() < cmd.size.try_into()? { 148 // Large command buffers should handled via shared memory. 149 return Err(RutabagaError::InvalidCommandBuffer); 150 } else if reader.available_bytes() != 0 { 151 let num_in_fences = cmd.num_in_fences as usize; 152 let cmd_size = cmd.size as usize; 153 let mut cmd_buf = vec![0; cmd_size]; 154 let mut fence_ids: Vec<u64> = Vec::with_capacity(num_in_fences); 155 for _ in 0..num_in_fences { 156 match reader.read_obj::<u64>() { 157 Ok(fence_id) => { 158 fence_ids.push(fence_id); 159 } 160 Err(_) => return Err(RutabagaError::InvalidIovec), 161 } 162 } 163 reader.read_exact(&mut cmd_buf[..])?; 164 KumquatGpuProtocol::CmdSubmit3d(cmd, cmd_buf, fence_ids) 165 } else { 166 KumquatGpuProtocol::CmdSubmit3d(cmd, Vec::new(), Vec::new()) 167 } 168 } 169 KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_BLOB => { 170 KumquatGpuProtocol::ResourceCreateBlob(reader.read_obj()?) 171 } 172 KUMQUAT_GPU_PROTOCOL_SNAPSHOT_SAVE => { 173 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 174 KumquatGpuProtocol::SnapshotSave 175 } 176 KUMQUAT_GPU_PROTOCOL_SNAPSHOT_RESTORE => { 177 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 178 KumquatGpuProtocol::SnapshotRestore 179 } 180 KUMQUAT_GPU_PROTOCOL_RESP_NUM_CAPSETS => { 181 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 182 KumquatGpuProtocol::RespNumCapsets(hdr.payload) 183 } 184 KUMQUAT_GPU_PROTOCOL_RESP_CAPSET_INFO => { 185 KumquatGpuProtocol::RespCapsetInfo(reader.read_obj()?) 186 } 187 KUMQUAT_GPU_PROTOCOL_RESP_CAPSET => { 188 let len: usize = hdr.payload.try_into()?; 189 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 190 let mut capset: Vec<u8> = vec![0; len]; 191 reader.read_exact(&mut capset)?; 192 KumquatGpuProtocol::RespCapset(capset) 193 } 194 KUMQUAT_GPU_PROTOCOL_RESP_CONTEXT_CREATE => { 195 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 196 KumquatGpuProtocol::RespContextCreate(hdr.payload) 197 } 198 KUMQUAT_GPU_PROTOCOL_RESP_RESOURCE_CREATE => { 199 let os_handle = descriptors 200 .pop_front() 201 .ok_or(RutabagaError::InvalidResourceId)?; 202 let resp: kumquat_gpu_protocol_resp_resource_create = reader.read_obj()?; 203 204 let handle = RutabagaHandle { 205 os_handle, 206 handle_type: resp.handle_type, 207 }; 208 209 KumquatGpuProtocol::RespResourceCreate(resp, handle) 210 } 211 KUMQUAT_GPU_PROTOCOL_RESP_CMD_SUBMIT_3D => { 212 let os_handle = descriptors 213 .pop_front() 214 .ok_or(RutabagaError::InvalidResourceId)?; 215 let resp: kumquat_gpu_protocol_resp_cmd_submit_3d = reader.read_obj()?; 216 217 let handle = RutabagaHandle { 218 os_handle, 219 handle_type: resp.handle_type, 220 }; 221 222 KumquatGpuProtocol::RespCmdSubmit3d(resp.fence_id, handle) 223 } 224 KUMQUAT_GPU_PROTOCOL_RESP_OK_SNAPSHOT => { 225 reader.consume(size_of::<kumquat_gpu_protocol_ctrl_hdr>()); 226 KumquatGpuProtocol::RespOkSnapshot 227 } 228 _ => { 229 return Err(RutabagaError::Unsupported); 230 } 231 }; 232 233 vec.push(protocol); 234 } 235 236 Ok(vec) 237 } 238 as_borrowed_descriptor(&self) -> &OwnedDescriptor239 pub fn as_borrowed_descriptor(&self) -> &OwnedDescriptor { 240 self.stream.as_borrowed_descriptor() 241 } 242 } 243