[zircon][rust] Add generic Dispatcher FFI infrastructure This introduces the generic Dispatcher and ProcessDispatcher facades in Rust to support C++ to Rust object migrations. It includes: - OpaqueFacade helper in zr library. - LockToken accessor generator in ksync guarded macro. - Generic Dispatcher and ProcessDispatcher opaque facades and FFI in Rust. - Generic C++ FFI implementation for Dispatcher and ProcessDispatcher. - KernelHandle type-safe improvements (take_ref_ptr, cast). Bug: 532573303 Test: fx test core-counter-test-package Change-Id: I465350db906f4251714b5f711f86c57fb8296600 Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1705137 Fuchsia-Auto-Submit: Adam Barth <abarth@google.com> Reviewed-by: Adrian Danis <adanis@google.com> Commit-Queue: Adam Barth <abarth@google.com>
diff --git a/src/lib/ksync/macro/src/lib.rs b/src/lib/ksync/macro/src/lib.rs index eb2b9e0..2c15f28 100644 --- a/src/lib/ksync/macro/src/lib.rs +++ b/src/lib/ksync/macro/src/lib.rs
@@ -439,6 +439,11 @@ #guard_accessors #[inline] + #struct_vis fn token(&self) -> &::ksync::LockToken<'a, #class_ident> { + self.inner.token() + } + + #[inline] #struct_vis fn fields<'b>(&'b self) -> #fields_ident #fields_ty_generics { let me = self; let token = me.inner.token();
diff --git a/src/lib/zr/src/lib.rs b/src/lib/zr/src/lib.rs index 98540f7..b345404 100644 --- a/src/lib/zr/src/lib.rs +++ b/src/lib/zr/src/lib.rs
@@ -10,6 +10,6 @@ mod static_assert; mod string; -pub use opaque::Opaque; +pub use opaque::{Opaque, OpaqueFacade}; pub use opaque_bytes::OpaqueBytes; pub use string::{parse_usize, to_array};
diff --git a/src/lib/zr/src/opaque.rs b/src/lib/zr/src/opaque.rs index 023db31..94a95f0 100644 --- a/src/lib/zr/src/opaque.rs +++ b/src/lib/zr/src/opaque.rs
@@ -35,3 +35,15 @@ Self::uninit() } } + +/// A zero-sized type representing an opaque C++ object facade. +/// +/// This is used as a field in Rust facade structs that represent C++ objects +/// of unknown size. It keeps the facade struct `Sized` (size 0) so it can be +/// used in FFI (thin pointers) and with generic containers like `RefPtr`, +/// while ensuring LLVM knows the object has interior mutability. +#[repr(C)] +#[derive(Default)] +pub struct OpaqueFacade { + _unused: UnsafeCell<()>, +}
diff --git a/zircon/kernel/object/BUILD.gn b/zircon/kernel/object/BUILD.gn index 9196d29..39f7652 100644 --- a/zircon/kernel/object/BUILD.gn +++ b/zircon/kernel/object/BUILD.gn
@@ -18,6 +18,7 @@ "counter_dispatcher.cc", "diagnostics.cc", "dispatcher.cc", + "dispatcher_ffi.cc", "event_dispatcher.cc", "event_pair_dispatcher.cc", "exception.cc", @@ -49,6 +50,7 @@ "pinned_memory_token_dispatcher.cc", "port_dispatcher.cc", "process_dispatcher.cc", + "process_dispatcher_ffi.cc", "profile_dispatcher.cc", "resource.cc", "resource_dispatcher.cc", @@ -86,6 +88,7 @@ "//zircon/kernel/lib/kconcurrent", "//zircon/kernel/lib/ktl", "//zircon/kernel/lib/ktrace", + "//zircon/kernel/lib/object-constants", "//zircon/kernel/lib/object_cache", "//zircon/kernel/lib/page", "//zircon/kernel/lib/page_cache", @@ -175,8 +178,19 @@ edition = "2024" source_root = "object.rs" sources = [ + "dispatcher.rs", + "dispatcher_ffi.rs", "handle.rs", "object.rs", + "process_dispatcher.rs", + "process_dispatcher_ffi.rs", ] - deps = [ "//sdk/rust/zx-types" ] + deps = [ + "//sdk/rust/zx-status", + "//sdk/rust/zx-types", + "//src/lib/kalloc", + "//src/lib/ksync", + "//src/lib/zr", + "//zircon/system/ulib/fbl/rust:fbl", + ] }
diff --git a/zircon/kernel/object/dispatcher.rs b/zircon/kernel/object/dispatcher.rs new file mode 100644 index 0000000..4471482 --- /dev/null +++ b/zircon/kernel/object/dispatcher.rs
@@ -0,0 +1,79 @@ +// Copyright 2026 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::dispatcher_ffi::{ + cpp_dispatcher_get_ref_counted, cpp_dispatcher_on_zero_handles, cpp_dispatcher_recycle, + cpp_dispatcher_update_state, cpp_dispatcher_update_state_locked, +}; +use core::marker::{PhantomData, PhantomPinned}; +use core::ptr::NonNull; +use kalloc::AllocError; +use ksync::LockToken; + +pub trait DispatcherOps { + type LockClass; + + fn dispatcher(&self) -> *const Dispatcher; + + fn on_zero_handles(&self) { + unsafe { + cpp_dispatcher_on_zero_handles(self.dispatcher()); + } + } + + fn update_state(&self, clear_mask: u32, set_mask: u32) { + unsafe { + cpp_dispatcher_update_state(self.dispatcher(), clear_mask, set_mask); + } + } + + fn update_state_locked( + &self, + _token: &LockToken<'_, Self::LockClass>, + clear_mask: u32, + set_mask: u32, + ) { + unsafe { + cpp_dispatcher_update_state_locked(self.dispatcher(), clear_mask, set_mask); + } + } +} + +#[repr(C)] +pub struct Dispatcher { + _marker: PhantomData<PhantomPinned>, + _facade: zr::OpaqueFacade, +} + +unsafe impl Send for Dispatcher {} +unsafe impl Sync for Dispatcher {} + +impl DispatcherOps for Dispatcher { + type LockClass = (); + + fn dispatcher(&self) -> *const Dispatcher { + self + } +} + +impl fbl::HasRefCount for Dispatcher { + fn ref_count(&self) -> &fbl::RefCounted { + unsafe { + let ptr = cpp_dispatcher_get_ref_counted(self); + &*(ptr.cast::<fbl::RefCounted>()) + } + } +} + +unsafe impl fbl::Recyclable for Dispatcher { + unsafe fn recycle(ptr: NonNull<Self>) { + unsafe { + cpp_dispatcher_recycle(ptr.as_ptr()); + } + } + + fn allocate(_value: Self) -> Result<NonNull<Self>, AllocError> { + Err(AllocError) + } +}
diff --git a/zircon/kernel/object/dispatcher_ffi.cc b/zircon/kernel/object/dispatcher_ffi.cc new file mode 100644 index 0000000..75ad6c7 --- /dev/null +++ b/zircon/kernel/object/dispatcher_ffi.cc
@@ -0,0 +1,28 @@ +// Copyright 2026 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <object/dispatcher.h> + +extern "C" { + +void cpp_dispatcher_on_zero_handles(Dispatcher* disp) { disp->on_zero_handles(); } + +void cpp_dispatcher_update_state(Dispatcher* disp, zx_signals_t clear_mask, zx_signals_t set_mask) { + disp->UpdateState(clear_mask, set_mask); +} + +void cpp_dispatcher_update_state_locked(Dispatcher* disp, zx_signals_t clear_mask, + zx_signals_t set_mask) TA_NO_THREAD_SAFETY_ANALYSIS { + disp->UpdateStateLocked(clear_mask, set_mask); +} + +void* cpp_dispatcher_get_ref_counted(const Dispatcher* disp) { + return disp->get_ref_counted_base(); +} + +void cpp_dispatcher_recycle(Dispatcher* disp) { + fbl::internal::recycler<Dispatcher>::recycle(disp); +} + +} // extern "C"
diff --git a/zircon/kernel/object/dispatcher_ffi.rs b/zircon/kernel/object/dispatcher_ffi.rs new file mode 100644 index 0000000..fe2f0af --- /dev/null +++ b/zircon/kernel/object/dispatcher_ffi.rs
@@ -0,0 +1,23 @@ +// Copyright 2026 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::dispatcher::Dispatcher; + +unsafe extern "C" { + pub(crate) fn cpp_dispatcher_on_zero_handles(dispatcher: *const Dispatcher); + pub(crate) fn cpp_dispatcher_update_state( + dispatcher: *const Dispatcher, + clear_mask: u32, + set_mask: u32, + ); + pub(crate) fn cpp_dispatcher_update_state_locked( + dispatcher: *const Dispatcher, + clear_mask: u32, + set_mask: u32, + ); + pub(crate) fn cpp_dispatcher_get_ref_counted( + dispatcher: *const Dispatcher, + ) -> *mut core::ffi::c_void; + pub(crate) fn cpp_dispatcher_recycle(dispatcher: *const Dispatcher); +}
diff --git a/zircon/kernel/object/handle.cc b/zircon/kernel/object/handle.cc index b1b2ff8c..f5dbf1e 100644 --- a/zircon/kernel/object/handle.cc +++ b/zircon/kernel/object/handle.cc
@@ -11,6 +11,7 @@ #include <fbl/conditional_select_nospec.h> #include <object/dispatcher.h> +#include <object/process_dispatcher.h> namespace {
diff --git a/zircon/kernel/object/handle.rs b/zircon/kernel/object/handle.rs index 0c19561..ce418ae 100644 --- a/zircon/kernel/object/handle.rs +++ b/zircon/kernel/object/handle.rs
@@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use zx_types::zx_handle_t; +use crate::DispatcherOps; +use fbl::{HasRefCount, Recyclable, RefPtr}; +use zx_status::Status; +use zx_types::{zx_handle_t, zx_rights_t}; /// A wrapper around a handle value received from userspace. #[repr(transparent)] @@ -22,3 +25,67 @@ self.value } } + +#[repr(transparent)] +pub struct KernelHandle<T> +where + T: HasRefCount + Recyclable + DispatcherOps, +{ + ptr: *const T, +} + +impl<T> KernelHandle<T> +where + T: HasRefCount + Recyclable + DispatcherOps, +{ + pub fn new(dispatcher: RefPtr<T>) -> Self { + Self { ptr: RefPtr::into_raw(dispatcher) } + } + + /// Casts this handle to a generic Dispatcher handle. + pub fn cast(self) -> KernelHandle<crate::dispatcher::Dispatcher> { + let ptr = self.ptr.cast::<crate::dispatcher::Dispatcher>(); + core::mem::forget(self); + KernelHandle { ptr } + } + + pub fn release(mut self) -> RefPtr<T> { + let ref_ptr = self.take_ref_ptr().expect("KernelHandle was empty"); + core::mem::forget(self); + ref_ptr + } + + fn take_ref_ptr(&mut self) -> Option<RefPtr<T>> { + let ptr = core::mem::replace(&mut self.ptr, core::ptr::null()); + if ptr.is_null() { + None + } else { + // SAFETY: ptr came from RefPtr::into_raw. + Some(unsafe { RefPtr::from_raw(ptr) }) + } + } + + pub fn dispatcher(&self) -> &T { + assert!(!self.ptr.is_null()); + // SAFETY: We are holding a reference to the object, which ensures that it lives as long as + // we do. + unsafe { &*self.ptr } + } + + pub fn make_and_add_handle(self, rights: zx_rights_t) -> Result<HandleValue, Status> { + crate::process_dispatcher::ProcessDispatcher::with_current(|up| { + up.make_and_add_handle(self, rights) + }) + } +} + +impl<T> Drop for KernelHandle<T> +where + T: HasRefCount + Recyclable + DispatcherOps, +{ + fn drop(&mut self) { + if let Some(ref_ptr) = self.take_ref_ptr() { + ref_ptr.on_zero_handles(); + } + } +}
diff --git a/zircon/kernel/object/include/object/dispatcher.h b/zircon/kernel/object/include/object/dispatcher.h index c50132b..babc000 100644 --- a/zircon/kernel/object/include/object/dispatcher.h +++ b/zircon/kernel/object/include/object/dispatcher.h
@@ -31,6 +31,17 @@ #include <object/handle.h> #include <object/signal_observer.h> +class Dispatcher; + +extern "C" { +void cpp_dispatcher_on_zero_handles(Dispatcher* disp); +void cpp_dispatcher_update_state(Dispatcher* disp, zx_signals_t clear_mask, zx_signals_t set_mask); +void cpp_dispatcher_update_state_locked(Dispatcher* disp, zx_signals_t clear_mask, + zx_signals_t set_mask); +void* cpp_dispatcher_get_ref_counted(const Dispatcher* disp); +void cpp_dispatcher_recycle(Dispatcher* disp); +} + template <typename T> struct DispatchTag; template <typename T> @@ -95,6 +106,12 @@ // // You don't derive directly from this class; instead derive // from SoloDispatcher or PeeredDispatcher. +extern "C" { +void cpp_dispatcher_update_state(Dispatcher* disp, zx_signals_t clear_mask, zx_signals_t set_mask); +void cpp_dispatcher_update_state_locked(Dispatcher* disp, zx_signals_t clear_mask, + zx_signals_t set_mask); +} + class Dispatcher : private fbl::RefCountedUpgradeable<Dispatcher>, private fbl::Recyclable<Dispatcher> { public: @@ -299,6 +316,10 @@ private: friend class fbl::Recyclable<Dispatcher>; + friend void cpp_dispatcher_update_state(Dispatcher* disp, zx_signals_t clear_mask, + zx_signals_t set_mask); + friend void cpp_dispatcher_update_state_locked(Dispatcher* disp, zx_signals_t clear_mask, + zx_signals_t set_mask); void fbl_recycle(); fbl::Canary<fbl::magic("DISP")> canary_;
diff --git a/zircon/kernel/object/include/object/process_dispatcher.h b/zircon/kernel/object/include/object/process_dispatcher.h index 216db6a..bd01c97 100644 --- a/zircon/kernel/object/include/object/process_dispatcher.h +++ b/zircon/kernel/object/include/object/process_dispatcher.h
@@ -437,4 +437,11 @@ const char* StateToString(ProcessDispatcher::State state); +extern "C" { +ProcessDispatcher* cpp_process_dispatcher_current(); +zx_status_t cpp_process_dispatcher_make_and_add_handle(ProcessDispatcher* process, + KernelHandle<Dispatcher>* handle, + zx_rights_t rights, zx_handle_t* out_handle); +} + #endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_PROCESS_DISPATCHER_H_
diff --git a/zircon/kernel/object/object.rs b/zircon/kernel/object/object.rs index 3b6225e..43724f1 100644 --- a/zircon/kernel/object/object.rs +++ b/zircon/kernel/object/object.rs
@@ -4,6 +4,12 @@ #![no_std] +mod dispatcher; +mod dispatcher_ffi; mod handle; +mod process_dispatcher; +mod process_dispatcher_ffi; -pub use handle::HandleValue; +pub use dispatcher::{Dispatcher, DispatcherOps}; +pub use handle::{HandleValue, KernelHandle}; +pub use process_dispatcher::ProcessDispatcher;
diff --git a/zircon/kernel/object/process_dispatcher.rs b/zircon/kernel/object/process_dispatcher.rs new file mode 100644 index 0000000..69ffff8f --- /dev/null +++ b/zircon/kernel/object/process_dispatcher.rs
@@ -0,0 +1,52 @@ +// Copyright 2026 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::handle::{HandleValue, KernelHandle}; +use crate::process_dispatcher_ffi::{ + cpp_process_dispatcher_current, cpp_process_dispatcher_make_and_add_handle, +}; +use core::marker::{PhantomData, PhantomPinned}; +use zx_status::Status; +use zx_types::zx_rights_t; + +#[repr(C)] +pub struct ProcessDispatcher { + _marker: PhantomData<PhantomPinned>, + _facade: zr::OpaqueFacade, +} + +unsafe impl Send for ProcessDispatcher {} +unsafe impl Sync for ProcessDispatcher {} + +impl ProcessDispatcher { + /// Executes the given function with a reference to the current process. + pub fn with_current<R>(f: impl FnOnce(&ProcessDispatcher) -> R) -> R { + // SAFETY: The current process is guaranteed to be valid for the duration of the call. + let proc = unsafe { &*cpp_process_dispatcher_current() }; + f(proc) + } + + /// Creates a handle for the given dispatcher in this process's handle table. + pub fn make_and_add_handle<T>( + &self, + handle: KernelHandle<T>, + rights: zx_rights_t, + ) -> Result<HandleValue, Status> + where + T: fbl::HasRefCount + fbl::Recyclable + crate::DispatcherOps, + { + let mut handle = handle.cast(); + let mut out = HandleValue::default(); + let status = unsafe { + cpp_process_dispatcher_make_and_add_handle( + self as *const _, + &mut handle, + rights, + &mut out, + ) + }; + Status::ok(status)?; + Ok(out) + } +}
diff --git a/zircon/kernel/object/process_dispatcher_ffi.cc b/zircon/kernel/object/process_dispatcher_ffi.cc new file mode 100644 index 0000000..f3c4a15 --- /dev/null +++ b/zircon/kernel/object/process_dispatcher_ffi.cc
@@ -0,0 +1,19 @@ +// Copyright 2026 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <object/handle.h> +#include <object/process_dispatcher.h> + +extern "C" { + +ProcessDispatcher* cpp_process_dispatcher_current() { return ProcessDispatcher::GetCurrent(); } + +zx_status_t cpp_process_dispatcher_make_and_add_handle(ProcessDispatcher* process, + KernelHandle<Dispatcher>* handle, + zx_rights_t rights, + zx_handle_t* out_handle) { + return process->MakeAndAddHandle(ktl::move(*handle), rights, out_handle); +} + +} // extern "C"
diff --git a/zircon/kernel/object/process_dispatcher_ffi.rs b/zircon/kernel/object/process_dispatcher_ffi.rs new file mode 100644 index 0000000..e688708 --- /dev/null +++ b/zircon/kernel/object/process_dispatcher_ffi.rs
@@ -0,0 +1,18 @@ +// Copyright 2026 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::dispatcher::Dispatcher; +use crate::handle::{HandleValue, KernelHandle}; +use crate::process_dispatcher::ProcessDispatcher; +use zx_types::{zx_rights_t, zx_status_t}; + +unsafe extern "C" { + pub(crate) fn cpp_process_dispatcher_current() -> *const ProcessDispatcher; + pub(crate) fn cpp_process_dispatcher_make_and_add_handle( + process: *const ProcessDispatcher, + handle: *mut KernelHandle<Dispatcher>, + rights: zx_rights_t, + out_handle: *mut HandleValue, + ) -> zx_status_t; +}