1 // SPDX-License-Identifier: GPL-2.0-only 2 3 //! Abstractions for the fwctl subsystem. 4 //! 5 //! C header: `include/linux/fwctl.h` 6 7 use crate::{ 8 bindings, 9 container_of, 10 device, 11 prelude::*, 12 sync::aref::{ 13 ARef, 14 AlwaysRefCounted, // 15 }, 16 types::Opaque, // 17 }; 18 use core::{ 19 alloc::Layout, 20 cell::UnsafeCell, 21 marker::PhantomData, 22 ptr::NonNull, 23 slice, // 24 }; 25 26 /// Returns a kmalloc-compatible allocation size for `T`. 27 const fn kmalloc_aligned_size<T>() -> usize { 28 Layout::new::<T>().pad_to_align().size() 29 } 30 31 /// Represents a fwctl device type. 32 /// 33 /// Corresponds to the C `enum fwctl_device_type`. All non-error UAPI values are represented so 34 /// Rust drivers can select a device type without passing an untyped integer, while 35 /// `FWCTL_DEVICE_TYPE_ERROR` remains unrepresentable. 36 #[repr(u32)] 37 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 38 pub enum DeviceType { 39 /// Mellanox ConnectX (mlx5) device. 40 Mlx5 = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_MLX5, 41 /// CXL (Compute Express Link) device. 42 Cxl = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_CXL, 43 /// AMD/Pensando PDS device. 44 Pds = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_PDS, 45 /// Broadcom NetXtreme (bnxt) device. 46 Bnxt = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_BNXT, 47 } 48 49 /// Scope of access for an RPC request. 50 /// 51 /// Corresponds to the C `enum fwctl_rpc_scope`. 52 #[repr(u32)] 53 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 54 pub enum RpcScope { 55 /// Read/write access to device configuration. 56 Configuration = bindings::fwctl_rpc_scope_FWCTL_RPC_CONFIGURATION, 57 /// Read-only access to debug information. 58 DebugReadOnly = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_READ_ONLY, 59 /// Write access to lockdown-compatible debug information. 60 DebugWrite = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_WRITE, 61 /// Full read/write access to all debug information (requires `CAP_SYS_RAWIO`). 62 DebugWriteFull = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_WRITE_FULL, 63 } 64 65 impl TryFrom<u32> for RpcScope { 66 type Error = Error; 67 68 #[inline] 69 fn try_from(value: u32) -> Result<Self, Error> { 70 match value { 71 v if v == Self::Configuration as u32 => Ok(Self::Configuration), 72 v if v == Self::DebugReadOnly as u32 => Ok(Self::DebugReadOnly), 73 v if v == Self::DebugWrite as u32 => Ok(Self::DebugWrite), 74 v if v == Self::DebugWriteFull as u32 => Ok(Self::DebugWriteFull), 75 _ => Err(EINVAL), 76 } 77 } 78 } 79 80 /// Response from a [`Operations::fw_rpc`] call. 81 pub enum FwRpcResponse { 82 /// Reuse the input buffer as the output, with the given output length. 83 /// 84 /// The callback returns `EINVAL` if the output length exceeds the input buffer length. 85 InPlace(usize), 86 /// Return a newly allocated buffer as the output. 87 NewBuffer(KVVec<u8>), 88 } 89 90 /// Trait implemented by each Rust driver that integrates with the fwctl subsystem. 91 /// 92 /// The implementing type **is** the per-FD user context: one instance is 93 /// created for each `open()` call and dropped when the FD is closed. 94 /// 95 /// Each implementation corresponds to a specific device type and provides the 96 /// vtable used by the core `fwctl` layer to manage per-FD user contexts and 97 /// handle RPC requests. 98 pub trait Operations: Sized + Send + Sync + 'static { 99 /// Data owned by the [`Registration`] and accessible during callbacks. 100 /// 101 /// The lifetime `'a` is tied to the [`Registration`] scope (which lives within the parent bus 102 /// device binding scope). Drivers use it to store references to resources bound to this scope, 103 /// such as PCI BARs or typed bus device references. 104 type RegistrationData<'a>: Send + Sync + 'a 105 where 106 Self: 'a; 107 108 /// fwctl device type identifier. 109 const DEVICE_TYPE: DeviceType; 110 111 /// Called when a new user context is opened. 112 /// 113 /// Returns a [`PinInit`] initializer for `Self`. The instance is dropped 114 /// automatically when the FD is closed (after [`close`](Self::close)). 115 fn open<'a>( 116 device: &Device<Self>, 117 reg_data: &Self::RegistrationData<'a>, 118 ) -> impl PinInit<Self, Error>; 119 120 /// Called when the user context is closed. 121 /// 122 /// The driver may perform additional cleanup here that requires access 123 /// to the owning [`Device`]. `Self` is dropped automatically after this 124 /// returns. 125 fn close<'a>( 126 _this: Pin<&mut Self>, 127 _device: &Device<Self>, 128 _reg_data: &Self::RegistrationData<'a>, 129 ) { 130 } 131 132 /// Return device information to userspace. 133 /// 134 /// The default implementation returns no device-specific data. 135 fn info<'a>( 136 _this: Pin<&Self>, 137 _device: &Device<Self>, 138 _reg_data: &Self::RegistrationData<'a>, 139 ) -> Result<KVec<u8>, Error> { 140 Ok(KVec::new()) 141 } 142 143 /// Handle a userspace RPC request. 144 /// 145 /// `max_output_len` is the size of the userspace output buffer. A driver may return a larger 146 /// response to report the required size; the fwctl core copies only the bytes that fit and 147 /// reports the full response length to userspace. 148 fn fw_rpc<'a>( 149 this: Pin<&Self>, 150 device: &Device<Self>, 151 reg_data: &Self::RegistrationData<'a>, 152 scope: RpcScope, 153 rpc_buf: &mut [u8], 154 max_output_len: usize, 155 ) -> Result<FwRpcResponse, Error>; 156 } 157 158 /// A fwctl device. 159 /// 160 /// `#[repr(C)]` with the `fwctl_device` at offset 0, matching the C `fwctl_alloc_device()` layout 161 /// convention. Contains a pointer to the [`Registration`]'s data, set at registration time and 162 /// cleared on unregistration. 163 /// 164 /// # Invariants 165 /// 166 /// - `dev` is embedded at offset 0 and is initialised by fwctl. 167 /// - The fwctl refcount owns the allocation lifetime. 168 /// - `registration_data` is either [`NonNull::dangling()`] (before registration / after 169 /// unregistration) or points to valid data owned by the [`Registration`]. 170 #[repr(C)] 171 pub struct Device<T: Operations> { 172 dev: Opaque<bindings::fwctl_device>, 173 registration_data: UnsafeCell<NonNull<T::RegistrationData<'static>>>, 174 } 175 176 impl<T: Operations> Device<T> { 177 /// Allocate a new fwctl device. 178 /// 179 /// Returns an [`ARef`] that can be passed to [`Registration::new()`] 180 /// to make the device visible to userspace. 181 pub fn new(parent: &device::Device<device::Bound>) -> Result<ARef<Self>> { 182 const_assert!( 183 core::mem::offset_of!(Self, dev) == 0, 184 "struct fwctl_device must be at offset 0" 185 ); 186 187 let size = kmalloc_aligned_size::<Self>(); 188 let ops = core::ptr::from_ref::<bindings::fwctl_ops>(&VTable::<T>::VTABLE).cast_mut(); 189 190 // SAFETY: `ops` is static, `parent` is bound, and `size` is padded so the allocation made 191 // by `_fwctl_alloc_device` satisfies the size and alignment required by `Device<T>`. 192 let raw = unsafe { bindings::_fwctl_alloc_device(parent.as_raw(), ops, size) }; 193 let this = NonNull::new(raw.cast::<Self>()).ok_or(ENOMEM)?; 194 195 // INVARIANT: Set `registration_data` to dangling (no registration yet). 196 // SAFETY: `this` points to the allocation just returned by fwctl. 197 unsafe { 198 (&raw mut (*this.as_ptr()).registration_data) 199 .write(UnsafeCell::new(NonNull::dangling())); 200 }; 201 202 // SAFETY: `this` owns the initial reference. 203 Ok(unsafe { ARef::from_raw(this) }) 204 } 205 206 /// Returns the underlying `fwctl_device` pointer. 207 #[inline] 208 fn as_raw(&self) -> *mut bindings::fwctl_device { 209 self.dev.get() 210 } 211 212 /// Borrows a Rust fwctl device from its raw C pointer. 213 /// 214 /// # Safety 215 /// 216 /// `ptr` must point to a valid `fwctl_device` embedded in a [`Device<T>`]. 217 #[inline] 218 unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_device) -> &'a Self { 219 // SAFETY: The caller upholds the offset-0 `Device<T>` invariant. 220 unsafe { &*ptr.cast() } 221 } 222 223 /// Invokes `f` with the registration data. 224 /// 225 /// The higher-ranked callback prevents the erased registration lifetime from escaping and 226 /// permits registration data that is invariant over its lifetime parameter. 227 /// 228 /// # Safety 229 /// 230 /// The caller must ensure that the device is registered and that this is called from a fwctl 231 /// callback protected by `registration_lock`. 232 #[inline] 233 unsafe fn with_registration_data<R>( 234 &self, 235 f: impl for<'a> FnOnce(&Device<T>, &'a T::RegistrationData<'a>) -> R, 236 ) -> R { 237 // SAFETY: Caller guarantees the device is registered, so the pointer is valid. 238 // Lifetimes do not affect layout. The higher-ranked callback prevents the shortened 239 // lifetime from escaping or being selected by the caller. 240 let reg_data = unsafe { 241 (*self.registration_data.get()) 242 .cast::<T::RegistrationData<'_>>() 243 .as_ref() 244 }; 245 246 f(self, reg_data) 247 } 248 } 249 250 impl<T: Operations> AsRef<device::Device> for Device<T> { 251 #[inline] 252 fn as_ref(&self) -> &device::Device { 253 // SAFETY: `self` contains a live fwctl_device. 254 let dev = unsafe { &raw mut (*self.as_raw()).dev }; 255 // SAFETY: The embedded device is initialised by fwctl. 256 unsafe { device::Device::from_raw(dev) } 257 } 258 } 259 260 // SAFETY: `fwctl_get` increments the refcount of a valid fwctl_device. 261 // `fwctl_put` decrements it and frees the device when it reaches zero. 262 unsafe impl<T: Operations> AlwaysRefCounted for Device<T> { 263 #[inline] 264 fn inc_ref(&self) { 265 // SAFETY: `self` holds a live reference. 266 unsafe { bindings::fwctl_get(self.as_raw()) }; 267 } 268 269 #[inline] 270 unsafe fn dec_ref(obj: NonNull<Self>) { 271 // SAFETY: The caller owns a live reference. 272 unsafe { bindings::fwctl_put(obj.cast().as_ptr()) }; 273 } 274 } 275 276 // SAFETY: `Device<T>` is refcounted by the fwctl core and may be released from any thread. 277 unsafe impl<T: Operations> Send for Device<T> {} 278 279 // SAFETY: Shared access to the embedded `fwctl_device` is protected by the fwctl core. The 280 // `registration_data` field is only mutated before registration and after unregistration (both 281 // single-threaded with respect to callbacks). 282 unsafe impl<T: Operations> Sync for Device<T> {} 283 284 /// A registered fwctl device. 285 /// 286 /// Owns the [`RegistrationData`](Operations::RegistrationData) made available to driver callbacks. 287 /// The parent device lifetime ensures that [`fwctl_unregister`] runs before the parent driver 288 /// unbinds. 289 /// 290 /// On drop the device is unregistered (all user contexts are closed and `ops` is set to `NULL`) 291 /// and the registration data is dropped. 292 /// 293 /// [`fwctl_unregister`]: srctree/drivers/fwctl/main.c 294 pub struct Registration<'a, T: Operations> { 295 dev: ARef<Device<T>>, 296 _reg_data: Pin<KBox<T::RegistrationData<'a>>>, 297 } 298 299 impl<'a, T: Operations> Registration<'a, T> { 300 /// Register a previously allocated fwctl device with the given registration data. 301 /// 302 /// The `reg_data` is owned by the registration and accessible during callbacks. 303 /// 304 /// # Safety 305 /// 306 /// Callers must not `mem::forget()` the returned [`Registration`] or otherwise prevent its 307 /// [`Drop`] implementation from running, since `fwctl_unregister` must be called before the 308 /// parent device is unbound. 309 /// 310 /// `dev` must be an unregistered [`Device`] that is not associated with any live 311 /// [`Registration`], and no other thread may attempt to register the same device concurrently. 312 pub unsafe fn new( 313 parent: &'a device::Device<device::Bound>, 314 dev: &Device<T>, 315 reg_data: impl PinInit<T::RegistrationData<'a>, Error>, 316 ) -> Result<Self> { 317 let actual_parent = dev.as_ref().parent().ok_or(EINVAL)?; 318 let parent_device: &device::Device = parent; 319 if !core::ptr::eq(actual_parent, parent_device) { 320 return Err(EINVAL); 321 } 322 323 let reg_data: Pin<KBox<T::RegistrationData<'a>>> = KBox::pin_init(reg_data, GFP_KERNEL)?; 324 325 // Store the registration data pointer in the device before registration, so that it is 326 // visible once callbacks can be invoked. The `'static` type is only an erased storage 327 // handle; callbacks access the pointer through a higher-ranked closure. 328 let ptr: NonNull<T::RegistrationData<'static>> = 329 NonNull::from(Pin::get_ref(reg_data.as_ref())).cast(); 330 331 // SAFETY: No concurrent access; the device is not yet registered. 332 unsafe { *dev.registration_data.get() = ptr }; 333 334 // SAFETY: `dev` is a valid fwctl_device backed by an ARef. 335 let ret = unsafe { bindings::fwctl_register(dev.as_raw()) }; 336 if ret != 0 { 337 // SAFETY: No concurrent readers; registration failed. 338 unsafe { *dev.registration_data.get() = NonNull::dangling() }; 339 return Err(Error::from_errno(ret)); 340 } 341 342 Ok(Self { 343 dev: dev.into(), 344 _reg_data: reg_data, 345 }) 346 } 347 } 348 349 impl<T: Operations> Drop for Registration<'_, T> { 350 fn drop(&mut self) { 351 // SAFETY: The Registration lifetime guarantees that the parent device is still bound. 352 // `fwctl_unregister` takes the write lock, closes all user contexts, and sets ops=NULL. 353 // After it returns, no callbacks can be running or will run. 354 unsafe { bindings::fwctl_unregister(self.dev.as_raw()) }; 355 356 // SAFETY: `fwctl_unregister` guarantees no concurrent readers. 357 unsafe { *self.dev.registration_data.get() = NonNull::dangling() }; 358 359 // `self._reg_data` is dropped here, after callbacks have stopped. 360 } 361 } 362 363 /// Internal per-FD user context wrapping `struct fwctl_uctx` and `T`. 364 /// 365 /// Not exposed to drivers; they work with `&T` / `Pin<&mut T>` directly. 366 #[repr(C)] 367 #[pin_data] 368 struct UserCtx<T: Operations> { 369 #[pin] 370 fwctl_uctx: Opaque<bindings::fwctl_uctx>, 371 #[pin] 372 uctx: T, 373 } 374 375 impl<T: Operations> UserCtx<T> { 376 /// Borrows a pinned Rust user context from its raw C pointer. 377 /// 378 /// # Safety 379 /// 380 /// `ptr` must point to a `fwctl_uctx` embedded in a live, pinned `UserCtx<T>` that remains 381 /// valid and does not move for the duration of `'a`. 382 #[inline] 383 unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_uctx) -> Pin<&'a Self> { 384 // SAFETY: The caller upholds the `UserCtx<T>` embedding, lifetime, and pinning invariants. 385 unsafe { Pin::new_unchecked(&*container_of!(Opaque::cast_from(ptr), Self, fwctl_uctx)) } 386 } 387 388 /// Mutably borrows a pinned Rust user context from its raw C pointer. 389 /// 390 /// # Safety 391 /// 392 /// - `ptr` must point to a `fwctl_uctx` embedded in a live, pinned `UserCtx<T>` that remains 393 /// valid and does not move for the duration of `'a`. 394 /// - The caller must ensure exclusive access to the `UserCtx<T>` for the duration of `'a`. 395 #[inline] 396 unsafe fn from_raw_mut<'a>(ptr: *mut bindings::fwctl_uctx) -> Pin<&'a mut Self> { 397 // SAFETY: The caller upholds the embedding, lifetime, pinning, and exclusivity invariants. 398 unsafe { 399 Pin::new_unchecked( 400 &mut *container_of!(Opaque::cast_from(ptr), Self, fwctl_uctx).cast_mut(), 401 ) 402 } 403 } 404 405 /// Returns a reference to the fwctl [`Device`] that owns this context. 406 #[inline] 407 fn device(self: Pin<&Self>) -> &Device<T> { 408 // SAFETY: fwctl initialises this pointer before any driver callback. 409 let raw_fwctl = unsafe { (*self.fwctl_uctx.get()).fwctl }; 410 // SAFETY: Rust fwctl devices use the offset-0 `Device<T>` layout. 411 unsafe { Device::from_raw(raw_fwctl) } 412 } 413 414 /// Returns a pinned reference to the driver context. 415 #[inline] 416 fn uctx(self: Pin<&Self>) -> Pin<&T> { 417 ::pin_init::assert_pinned!(UserCtx<T>, uctx, T, inline); 418 419 // SAFETY: `uctx` is structurally pinned. 420 unsafe { self.map_unchecked(|ctx| &ctx.uctx) } 421 } 422 } 423 424 /// Static vtable mapping Rust trait methods to C callbacks. 425 struct VTable<T: Operations>(PhantomData<T>); 426 427 impl<T: Operations> VTable<T> { 428 /// The fwctl operations vtable for this driver type. 429 const VTABLE: bindings::fwctl_ops = bindings::fwctl_ops { 430 // CAST: `DeviceType` has the same `u32` representation as the C enum field. 431 device_type: T::DEVICE_TYPE as u32, 432 uctx_size: kmalloc_aligned_size::<UserCtx<T>>(), 433 open_uctx: Some(Self::open_uctx_callback), 434 close_uctx: Some(Self::close_uctx_callback), 435 info: Some(Self::info_callback), 436 fw_rpc: Some(Self::fw_rpc_callback), 437 }; 438 439 /// Initialises a newly opened Rust user context. 440 /// 441 /// # Safety 442 /// 443 /// `uctx` must be a valid `fwctl_uctx` embedded in a `UserCtx<T>` with 444 /// sufficient allocated space for the uctx field. 445 unsafe extern "C" fn open_uctx_callback(uctx: *mut bindings::fwctl_uctx) -> ffi::c_int { 446 const_assert!( 447 core::mem::offset_of!(UserCtx<T>, fwctl_uctx) == 0, 448 "struct fwctl_uctx must be at offset 0" 449 ); 450 451 // SAFETY: fwctl sets this pointer before calling `open_uctx`. 452 let raw_fwctl = unsafe { (*uctx).fwctl }; 453 // SAFETY: Rust fwctl devices use the offset-0 `Device<T>` layout. 454 let device = unsafe { Device::<T>::from_raw(raw_fwctl) }; 455 456 let uctx_offset = core::mem::offset_of!(UserCtx<T>, uctx); 457 // SAFETY: `uctx_size` reserves space for the full `UserCtx<T>`. 458 let uctx_ptr: *mut T = unsafe { uctx.byte_add(uctx_offset).cast() }; 459 460 // SAFETY: `open_uctx` is called under `registration_lock` read, so the device is 461 // registered. `uctx_ptr` addresses the uninitialised pinned context reserved by 462 // `uctx_size`. 463 unsafe { 464 device.with_registration_data(|device, reg_data| { 465 match pin_init::raw_try_init(uctx_ptr, T::open(device, reg_data)) { 466 Ok(()) => 0, 467 Err(e) => e.to_errno(), 468 } 469 }) 470 } 471 } 472 473 /// Closes and drops an opened Rust user context. 474 /// 475 /// # Safety 476 /// 477 /// `uctx` must point to a fully initialised `UserCtx<T>`. 478 unsafe extern "C" fn close_uctx_callback(uctx: *mut bindings::fwctl_uctx) { 479 // SAFETY: fwctl keeps the owning device live for this callback. 480 let device = unsafe { Device::<T>::from_raw((*uctx).fwctl) }; 481 482 // SAFETY: close is called for an opened Rust user context. 483 let mut ctx = unsafe { UserCtx::<T>::from_raw_mut(uctx) }; 484 485 // SAFETY: `close_uctx` is called under `registration_lock` write (from 486 // `fwctl_unregister`) or read (from `fwctl_fops_release`), so the device is registered. 487 unsafe { 488 device.with_registration_data(|device, reg_data| { 489 T::close(ctx.as_mut().project().uctx, device, reg_data); 490 }); 491 } 492 493 // SAFETY: close is the last callback before fwctl frees the allocation. 494 unsafe { core::ptr::drop_in_place(ctx.project().uctx.get_unchecked_mut()) }; 495 } 496 497 /// Returns device-specific information for an opened Rust user context. 498 /// 499 /// # Safety 500 /// 501 /// - `uctx` must point to a fully initialised `UserCtx<T>`. 502 /// - `length` must be a valid pointer. 503 unsafe extern "C" fn info_callback( 504 uctx: *mut bindings::fwctl_uctx, 505 length: *mut usize, 506 ) -> *mut ffi::c_void { 507 // SAFETY: info is called for an opened Rust user context. 508 let ctx = unsafe { UserCtx::<T>::from_raw(uctx) }; 509 let device = ctx.device(); 510 511 // SAFETY: `info` is called under `registration_lock` read, so the device is registered. 512 let result = unsafe { 513 device.with_registration_data(|device, reg_data| T::info(ctx.uctx(), device, reg_data)) 514 }; 515 516 match result { 517 Ok(kvec) if kvec.is_empty() => { 518 // SAFETY: `length` is a valid out-parameter. 519 unsafe { *length = 0 }; 520 // Return NULL for empty data; kfree(NULL) is safe. 521 core::ptr::null_mut() 522 } 523 Ok(kvec) => { 524 let (ptr, len, _cap) = kvec.into_raw_parts(); 525 // SAFETY: `length` is a valid out-parameter. 526 unsafe { *length = len }; 527 ptr.cast::<ffi::c_void>() 528 } 529 Err(e) => Error::to_ptr(e), 530 } 531 } 532 533 /// Dispatches a firmware RPC for an opened Rust user context. 534 /// 535 /// # Safety 536 /// 537 /// - `uctx` must point to a fully initialised `UserCtx<T>`. 538 /// - `rpc_in` must be valid, initialised, and exclusively accessible for `in_len` bytes. 539 /// - `out_len` must be valid for reading and writing an initialised `usize`. 540 unsafe extern "C" fn fw_rpc_callback( 541 uctx: *mut bindings::fwctl_uctx, 542 scope: u32, 543 rpc_in: *mut ffi::c_void, 544 in_len: usize, 545 out_len: *mut usize, 546 ) -> *mut ffi::c_void { 547 let scope = match RpcScope::try_from(scope) { 548 Ok(s) => s, 549 Err(e) => return Error::to_ptr(e), 550 }; 551 552 // SAFETY: `out_len` points to an initialised `usize` supplied by fwctl. 553 let max_output_len = unsafe { *out_len }; 554 555 // SAFETY: RPC is called for an opened Rust user context. 556 let ctx = unsafe { UserCtx::<T>::from_raw(uctx) }; 557 let device = ctx.device(); 558 559 // SAFETY: fwctl passes an exclusively owned buffer that is valid and initialised for 560 // `in_len` bytes. It remains live for the duration of this callback. 561 let rpc_buf = unsafe { slice::from_raw_parts_mut(rpc_in.cast::<u8>(), in_len) }; 562 563 // SAFETY: `fw_rpc` is called under `registration_lock` read, so the device is registered. 564 let result = unsafe { 565 device.with_registration_data(|device, reg_data| { 566 T::fw_rpc(ctx.uctx(), device, reg_data, scope, rpc_buf, max_output_len) 567 }) 568 }; 569 570 let (response, response_len) = match result { 571 Ok(FwRpcResponse::InPlace(len)) => { 572 if len > in_len { 573 return Error::to_ptr(EINVAL); 574 } 575 576 (rpc_in, len) 577 } 578 Ok(FwRpcResponse::NewBuffer(kvec)) if kvec.is_empty() => { 579 // Return NULL for empty data; kvfree(NULL) is safe. 580 (core::ptr::null_mut(), 0) 581 } 582 Ok(FwRpcResponse::NewBuffer(kvec)) => { 583 let (ptr, len, _cap) = kvec.into_raw_parts(); 584 (ptr.cast::<ffi::c_void>(), len) 585 } 586 Err(e) => return Error::to_ptr(e), 587 }; 588 589 // SAFETY: `out_len` is a valid out-parameter. 590 unsafe { *out_len = response_len }; 591 response 592 } 593 } 594