Skip to main content

kernel/
restricted_state.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::ffi::c_void;
6use core::ptr::{self, NonNull};
7use debug::ltracef;
8use kalloc::Box;
9use zx_status::Status;
10use zx_types::{zx_exception_report_t, zx_restricted_state_t, zx_status_t};
11
12use arch_rs::ArchSavedNormalState;
13
14const LOCAL_TRACE: u32 = 0;
15
16unsafe extern "C" {
17    fn cpp_restricted_state_create_vmo_mapping(
18        out_vmo: *mut *mut c_void,
19        out_mapping: *mut *mut c_void,
20        out_base: *mut *mut zx_restricted_state_t,
21    ) -> zx_status_t;
22    fn cpp_restricted_state_destroy_vmo_mapping(vmo: *mut c_void, mapping: *mut c_void);
23}
24
25/// Encapsulates a thread's restricted mode state, including VMO backing and mapping.
26///
27/// The memory layout of this struct (`#[repr(C)]`) must exactly match its C++ counterpart
28/// (`class RestrictedState` in `zircon/kernel/include/kernel/restricted_state.h`), as instances
29/// allocated here are directly accessed from C++ code by pointer. Any changes to field types,
30/// names, ordering, or layout must be kept in sync between the two definitions, and verified via
31/// static assertions.
32#[repr(C)]
33pub struct RestrictedState {
34    in_restricted: bool,
35    vector_ptr: usize,
36    context: usize,
37    exception_report_ptr: *mut zx_exception_report_t,
38    vmo: NonNull<c_void>,
39    mapping: NonNull<c_void>,
40    state_mapping_ptr: NonNull<zx_restricted_state_t>,
41    arch: ArchSavedNormalState,
42}
43
44// Verify that the memory layout of Rust `RestrictedState` exactly matches the C++
45// `class RestrictedState` in `zircon/kernel/include/kernel/restricted_state.h`.
46//
47// Both types are accessed directly by pointer across the FFI boundary, so any changes to field
48// types, ordering, or layout here must also be mirrored on the C++ side, and vice versa.
49const _: () = {
50    assert!(core::mem::offset_of!(RestrictedState, in_restricted) == 0);
51    assert!(core::mem::offset_of!(RestrictedState, vector_ptr) == 8);
52    assert!(core::mem::offset_of!(RestrictedState, context) == 16);
53    assert!(core::mem::offset_of!(RestrictedState, exception_report_ptr) == 24);
54    assert!(core::mem::offset_of!(RestrictedState, vmo) == 32);
55    assert!(core::mem::offset_of!(RestrictedState, mapping) == 40);
56    assert!(core::mem::offset_of!(RestrictedState, state_mapping_ptr) == 48);
57    assert!(core::mem::offset_of!(RestrictedState, arch) == 56);
58    assert!(core::mem::align_of::<RestrictedState>() == 8);
59    #[cfg(not(target_arch = "riscv64"))]
60    assert!(core::mem::size_of::<RestrictedState>() == 72);
61    #[cfg(target_arch = "riscv64")]
62    assert!(core::mem::size_of::<RestrictedState>() == 64);
63};
64
65impl RestrictedState {
66    /// Create a new `RestrictedState`, allocating its VMO and mapping in kernel memory.
67    pub fn create(exception_report_ptr: *mut zx_exception_report_t) -> Result<Box<Self>, Status> {
68        let mut raw_vmo = ptr::null_mut();
69        let mut raw_mapping = ptr::null_mut();
70        let mut raw_base = ptr::null_mut();
71        // Allocate VMO, pin pages, map into kernel address space, and eagerly fault in mapping.
72        // SAFETY: We pass valid pointers to out variables for VMO, mapping, and base address.
73        let raw_status = unsafe {
74            cpp_restricted_state_create_vmo_mapping(&mut raw_vmo, &mut raw_mapping, &mut raw_base)
75        };
76        Status::ok(raw_status)?;
77
78        let (vmo, mapping, state_mapping_ptr) =
79            match (NonNull::new(raw_vmo), NonNull::new(raw_mapping), NonNull::new(raw_base)) {
80                (Some(vmo), Some(mapping), Some(base)) => (vmo, mapping, base),
81                _ => {
82                    // SAFETY: raw_vmo and raw_mapping were created by cpp_restricted_state_create_vmo_mapping.
83                    unsafe { cpp_restricted_state_destroy_vmo_mapping(raw_vmo, raw_mapping) };
84                    return Err(Status::NO_MEMORY);
85                }
86            };
87
88        ltracef!("mapping at {:#x}\n", state_mapping_ptr.as_ptr() as usize);
89
90        let state = Box::try_new(Self {
91            in_restricted: false,
92            vector_ptr: 0,
93            context: 0,
94            exception_report_ptr,
95            vmo,
96            mapping,
97            state_mapping_ptr,
98            arch: ArchSavedNormalState::default(),
99        });
100
101        match state {
102            Ok(s) => Ok(s),
103            Err(_) => {
104                // SAFETY: raw_vmo and raw_mapping were created by cpp_restricted_state_create_vmo_mapping.
105                unsafe { cpp_restricted_state_destroy_vmo_mapping(raw_vmo, raw_mapping) };
106                Err(Status::NO_MEMORY)
107            }
108        }
109    }
110
111    /// Return whether the thread is currently in restricted mode.
112    pub fn in_restricted(&self) -> bool {
113        self.in_restricted
114    }
115
116    /// Set whether the thread is currently in restricted mode.
117    pub fn set_in_restricted(&mut self, val: bool) {
118        self.in_restricted = val;
119    }
120
121    /// Return the normal mode vector table pointer.
122    pub fn vector_ptr(&self) -> usize {
123        self.vector_ptr
124    }
125
126    /// Set the normal mode vector table pointer.
127    pub fn set_vector_ptr(&mut self, val: usize) {
128        self.vector_ptr = val;
129    }
130
131    /// Return the normal mode context value.
132    pub fn context(&self) -> usize {
133        self.context
134    }
135
136    /// Set the normal mode context value.
137    pub fn set_context(&mut self, val: usize) {
138        self.context = val;
139    }
140
141    /// Return the user exception report pointer address.
142    pub fn exception_report_ptr(&self) -> *mut zx_exception_report_t {
143        self.exception_report_ptr
144    }
145
146    /// Return a shared reference to the saved normal mode architecture state.
147    pub fn arch_normal_state(&self) -> &ArchSavedNormalState {
148        &self.arch
149    }
150
151    /// Return a mutable reference to the saved normal mode architecture state.
152    pub fn arch_normal_state_mut(&mut self) -> &mut ArchSavedNormalState {
153        &mut self.arch
154    }
155
156    /// Return a shared reference to the mapped `zx_restricted_state_t` buffer.
157    pub fn state(&self) -> &zx_restricted_state_t {
158        // SAFETY: By RestrictedState invariant, state_mapping_ptr points to a valid zx_restricted_state_t.
159        unsafe { self.state_mapping_ptr.as_ref() }
160    }
161
162    /// Return a mutable reference to the mapped `zx_restricted_state_t` buffer.
163    pub fn state_mut(&mut self) -> &mut zx_restricted_state_t {
164        // SAFETY: By RestrictedState invariant, state_mapping_ptr points to a valid zx_restricted_state_t.
165        unsafe { self.state_mapping_ptr.as_mut() }
166    }
167
168    /// Return a raw pointer to the mapped `zx_restricted_state_t` buffer.
169    pub fn state_ptr(&self) -> *mut zx_restricted_state_t {
170        self.state_mapping_ptr.as_ptr()
171    }
172
173    /// Return a raw pointer to the underlying VMO.
174    pub fn vmo(&self) -> *mut c_void {
175        self.vmo.as_ptr()
176    }
177}
178
179impl Drop for RestrictedState {
180    fn drop(&mut self) {
181        // Destroy the kernel mapping and unpin the VMO pages.
182        // SAFETY: self.vmo and self.mapping contain valid pointers returned during creation.
183        unsafe {
184            cpp_restricted_state_destroy_vmo_mapping(self.vmo.as_ptr(), self.mapping.as_ptr());
185        }
186    }
187}
188
189/// Create a new `RestrictedState` and write its raw pointer into `out_ptr`.
190///
191/// # Safety
192///
193/// Caller must ensure `out_ptr` is a valid pointer for writing `*mut RestrictedState`.
194#[unsafe(no_mangle)]
195pub unsafe extern "C" fn rust_restricted_state_create(
196    exception_report_ptr: *mut zx_exception_report_t,
197    out_ptr: *mut *mut RestrictedState,
198) -> zx_status_t {
199    match RestrictedState::create(exception_report_ptr) {
200        Ok(box_state) => {
201            // SAFETY: out_ptr is guaranteed by caller to be valid for writing.
202            unsafe {
203                *out_ptr = Box::into_raw(box_state);
204            }
205            Status::OK.into_raw()
206        }
207        Err(status) => status.into_raw(),
208    }
209}
210
211/// Destroy a `RestrictedState` created by `rust_restricted_state_create`.
212///
213/// # Safety
214///
215/// Caller must pass a pointer returned by `rust_restricted_state_create` exactly once.
216#[unsafe(no_mangle)]
217pub unsafe extern "C" fn rust_restricted_state_destroy(ptr: *mut RestrictedState) {
218    if !ptr.is_null() {
219        // SAFETY: ptr was created by Box::into_raw in rust_restricted_state_create and passed once.
220        unsafe {
221            let _ = Box::from_raw(ptr);
222        }
223    }
224}