Skip to main content

starnix_core/task/
thread_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 crate::task::CurrentTask;
6use extended_pstate::{ExtendedPstatePointer, ExtendedPstateState};
7use starnix_registers::{HeapRegs, RegisterState, RegisterStorage, RegisterStorageEnum};
8use starnix_syscalls::SyscallResult;
9use starnix_types::arch::ArchWidth;
10use starnix_uapi::errors::{Errno, ErrnoCode};
11use starnix_uapi::user_address::ArchSpecific;
12
13#[derive(Clone)]
14pub enum ArchExtendedPstateStorage {
15    // Storage for 64 bit restricted mode.
16    State64(Box<ExtendedPstateState>),
17    #[cfg(target_arch = "aarch64")]
18    // Storage for 32 bit arm restricted mode.
19    State32(Box<extended_pstate::ExtendedAarch32PstateState>),
20}
21
22impl ArchExtendedPstateStorage {
23    /// Returns a type-erased pointer to the underlying storage currently in use.
24    pub fn as_ptr(&mut self) -> ExtendedPstatePointer {
25        match self {
26            ArchExtendedPstateStorage::State64(state) => {
27                ExtendedPstatePointer { extended_pstate: state.as_mut() as *mut _ }
28            }
29            #[cfg(target_arch = "aarch64")]
30            ArchExtendedPstateStorage::State32(state) => {
31                ExtendedPstatePointer { extended_aarch32_pstate: state.as_mut() as *mut _ }
32            }
33        }
34    }
35
36    pub fn reset(&mut self) {
37        match self {
38            ArchExtendedPstateStorage::State64(state) => state.reset(),
39            #[cfg(target_arch = "aarch64")]
40            ArchExtendedPstateStorage::State32(state) => state.reset(),
41        }
42    }
43
44    pub fn reset_with_arch(&mut self, arch_width: ArchWidth) {
45        match (self, arch_width) {
46            #[cfg(target_arch = "aarch64")]
47            (ArchExtendedPstateStorage::State32(state), ArchWidth::Arch32) => state.reset(),
48            #[cfg(target_arch = "aarch64")]
49            (this @ ArchExtendedPstateStorage::State64(_), ArchWidth::Arch32)
50            | (this @ ArchExtendedPstateStorage::State32(_), ArchWidth::Arch64) => {
51                *this = Self::with_arch(arch_width);
52            }
53            (ArchExtendedPstateStorage::State64(state), _) => state.reset(),
54        }
55    }
56
57    #[cfg(target_arch = "aarch64")]
58    pub fn has_sve(&self) -> bool {
59        match self {
60            ArchExtendedPstateStorage::State64(state) => state.has_arm64_sve(),
61            ArchExtendedPstateStorage::State32(_) => false,
62        }
63    }
64
65    /// Whether the SVE vector length is inherited across `execve` (`PR_SVE_VL_INHERIT`).
66    #[cfg(target_arch = "aarch64")]
67    pub fn sve_vl_inherit(&self) -> bool {
68        match self {
69            ArchExtendedPstateStorage::State64(state) => state.get_arm64_sve_vl_inherit(),
70            ArchExtendedPstateStorage::State32(_) => false,
71        }
72    }
73
74    #[cfg(target_arch = "aarch64")]
75    pub fn set_sve_vl_inherit(&mut self, inherit: bool) {
76        if let ArchExtendedPstateStorage::State64(state) = self {
77            state.set_arm64_sve_vl_inherit(inherit);
78        }
79    }
80
81    fn with_arch(arch_width: ArchWidth) -> Self {
82        #[cfg(target_arch = "aarch64")]
83        if arch_width == ArchWidth::Arch32 {
84            return ArchExtendedPstateStorage::State32(Box::new(
85                extended_pstate::ExtendedAarch32PstateState::default(),
86            ));
87        }
88        let _ = arch_width;
89        ArchExtendedPstateStorage::State64(Box::new(ExtendedPstateState::default()))
90    }
91}
92
93/// The thread related information of a `CurrentTask`. The information should never be used outside
94/// of the thread owning the `CurrentTask`.
95pub struct ThreadState<T: RegisterStorage> {
96    /// A copy of the registers associated with the Zircon thread. Up-to-date values can be read
97    /// from `self.handle.read_state_general_regs()`. To write these values back to the thread, call
98    /// `self.handle.write_state_general_regs(self.thread_state.registers.into())`.
99    pub registers: RegisterState<T>,
100
101    /// Copy of the current extended processor state including floating point and vector registers.
102    pub extended_pstate: ArchExtendedPstateStorage,
103
104    /// The errno code (if any) that indicated this task should restart a syscall.
105    pub restart_code: Option<ErrnoCode>,
106
107    /// A custom function to resume a syscall that has been interrupted by SIGSTOP.
108    /// To use, call set_syscall_restart_func and return ERESTART_RESTARTBLOCK. sys_restart_syscall
109    /// will eventually call it.
110    pub syscall_restart_func: Option<Box<SyscallRestartFunc>>,
111}
112
113impl<T: RegisterStorage + Default> Default for ThreadState<T> {
114    // TODO(https://fxbug.dev/407084069): Implementing default doesn't make much
115    // sense - we should only initialize thread state when we know the target
116    // architecture and we should initialize for that target specifically.
117    fn default() -> Self {
118        let registers = RegisterState::<T>::default();
119        let extended_pstate = ArchExtendedPstateStorage::with_arch(ArchWidth::Arch64);
120
121        Self { registers, extended_pstate, restart_code: None, syscall_restart_func: None }
122    }
123}
124
125impl<T: RegisterStorage> ThreadState<T> {
126    pub fn arch_width(&self) -> ArchWidth {
127        #[cfg(target_arch = "aarch64")]
128        {
129            return if self.registers.is_arch32() { ArchWidth::Arch32 } else { ArchWidth::Arch64 };
130        }
131        #[cfg(not(target_arch = "aarch64"))]
132        ArchWidth::Arch64
133    }
134
135    /// Returns a new `ThreadState` with the same `registers` as this one.
136    pub fn snapshot<R: RegisterStorage>(&self) -> ThreadState<R>
137    where
138        RegisterState<R>: From<RegisterState<T>>,
139    {
140        ThreadState::<R> {
141            registers: self.registers.clone().into(),
142            extended_pstate: self.extended_pstate.clone(),
143            restart_code: self.restart_code,
144            syscall_restart_func: None,
145        }
146    }
147
148    pub fn extended_snapshot<R: RegisterStorage>(&self) -> ThreadState<R>
149    where
150        RegisterState<R>: From<RegisterState<T>>,
151    {
152        ThreadState::<R> {
153            registers: self.registers.clone().into(),
154            extended_pstate: self.extended_pstate.clone(),
155            restart_code: self.restart_code,
156            syscall_restart_func: None,
157        }
158    }
159
160    pub fn replace_registers<O: RegisterStorage>(&mut self, other: &ThreadState<O>) {
161        let self_arch = self.arch_width();
162        let other_arch = other.arch_width();
163        self.registers.copy_from(&other.registers);
164        // If we're switching between 32 and 64 bit mode, re-initialize the extended processor state.
165        self.extended_pstate = if self_arch == other_arch {
166            other.extended_pstate.clone()
167        } else {
168            ArchExtendedPstateStorage::with_arch(other_arch)
169        };
170    }
171
172    pub fn get_user_register(&mut self, offset: usize) -> Result<usize, Errno> {
173        let mut result: usize = 0;
174        self.registers.apply_user_register(offset, &mut |register| result = *register as usize)?;
175        Ok(result)
176    }
177
178    pub fn set_user_register(&mut self, offset: usize, value: usize) -> Result<(), Errno> {
179        let self_arch = self.arch_width();
180        let result =
181            self.registers.apply_user_register(offset, &mut |register| *register = value as u64);
182        // If setting the CPSR register to switch between 32 and 64 bit mode, re-initialize the extended processor state.
183        if self_arch != self.arch_width() {
184            self.extended_pstate = ArchExtendedPstateStorage::with_arch(self.arch_width());
185        }
186        result
187    }
188}
189
190impl From<ThreadState<HeapRegs>> for ThreadState<RegisterStorageEnum> {
191    fn from(value: ThreadState<HeapRegs>) -> Self {
192        ThreadState {
193            registers: value.registers.into(),
194            extended_pstate: value.extended_pstate,
195            restart_code: value.restart_code,
196            syscall_restart_func: value.syscall_restart_func,
197        }
198    }
199}
200
201impl<T: RegisterStorage> ArchSpecific for ThreadState<T> {
202    fn is_arch32(&self) -> bool {
203        #[cfg(target_arch = "aarch64")]
204        return self.registers.is_arch32();
205        #[cfg(not(target_arch = "aarch64"))]
206        false
207    }
208}
209
210pub type SyscallRestartFunc =
211    dyn FnOnce(&mut CurrentTask) -> Result<SyscallResult, Errno> + Send + Sync;
212
213#[cfg(all(test, target_arch = "aarch64"))]
214mod tests {
215    use super::*;
216
217    #[::fuchsia::test]
218    fn test_arch_extended_pstate_storage_sve() {
219        let mut pstate = ExtendedPstateState::with_sve(true);
220        let qregs = [0x112233445566778899aabbccddeeff00u128; 32];
221        let pregs = [0x5a5au16; 16];
222        pstate.set_arm64_state(&qregs, 0x10, 0x20);
223        pstate.set_arm64_sve_state(&pregs, 0x00ff);
224
225        let mut storage = ArchExtendedPstateStorage::State64(Box::new(pstate));
226        assert!(storage.has_sve());
227
228        // `PR_SVE_VL_INHERIT` defaults to off and can be toggled without touching registers.
229        assert!(!storage.sve_vl_inherit());
230        storage.set_sve_vl_inherit(true);
231        assert!(storage.sve_vl_inherit());
232        let ArchExtendedPstateStorage::State64(state) = &storage else { unreachable!() };
233        assert_eq!(state.get_arm64_qregs(), &qregs);
234        assert_eq!(state.get_arm64_p_regs(), &pregs);
235        assert_eq!(state.get_arm64_ffr(), 0x00ff);
236
237        // `reset` (used on execve) zeroes all vector and SVE register state while preserving
238        // `has_sve` and the `PR_SVE_VL_INHERIT` setting, like Linux's `fpsimd_flush_thread`.
239        storage.reset();
240        assert!(storage.has_sve());
241        assert!(storage.sve_vl_inherit());
242        let ArchExtendedPstateStorage::State64(state) = &storage else { unreachable!() };
243        assert_eq!(state.get_arm64_qregs(), &[0u128; 32]);
244        assert_eq!(state.get_arm64_fpsr(), 0);
245        assert_eq!(state.get_arm64_fpcr(), 0);
246        assert_eq!(state.get_arm64_p_regs(), &[0u16; 16]);
247        assert_eq!(state.get_arm64_ffr(), 0);
248
249        storage.set_sve_vl_inherit(false);
250        storage.reset();
251        assert!(storage.has_sve());
252        assert!(!storage.sve_vl_inherit());
253
254        // 32-bit storage never reports SVE support or vector length inheritance.
255        let mut storage32 = ArchExtendedPstateStorage::State32(Box::default());
256        assert!(!storage32.has_sve());
257        storage32.set_sve_vl_inherit(true);
258        assert!(!storage32.sve_vl_inherit());
259    }
260
261    #[::fuchsia::test]
262    fn test_arch_extended_pstate_storage_reset_with_arch() {
263        let mut pstate = ExtendedPstateState::with_sve(true);
264        let qregs = [0x112233445566778899aabbccddeeff00u128; 32];
265        let pregs = [0x5a5au16; 16];
266        pstate.set_arm64_state(&qregs, 0x10, 0x20);
267        pstate.set_arm64_sve_state(&pregs, 0x00ff);
268
269        let mut storage = ArchExtendedPstateStorage::State64(Box::new(pstate));
270        storage.set_sve_vl_inherit(true);
271
272        // Arch64 -> Arch64 execve resets register state in-place while preserving `has_sve`
273        // and `PR_SVE_VL_INHERIT`.
274        storage.reset_with_arch(ArchWidth::Arch64);
275        assert!(storage.has_sve());
276        assert!(storage.sve_vl_inherit());
277        let ArchExtendedPstateStorage::State64(state) = &storage else { unreachable!() };
278        assert_eq!(state.get_arm64_qregs(), &[0u128; 32]);
279        assert_eq!(state.get_arm64_p_regs(), &[0u16; 16]);
280        assert_eq!(state.get_arm64_ffr(), 0);
281
282        // Arch64 -> Arch32 execve switches storage variant to `State32`.
283        storage.reset_with_arch(ArchWidth::Arch32);
284        assert!(matches!(storage, ArchExtendedPstateStorage::State32(_)));
285        assert!(!storage.has_sve());
286        assert!(!storage.sve_vl_inherit());
287
288        // Arch32 -> Arch32 execve resets `State32` in-place.
289        storage.reset_with_arch(ArchWidth::Arch32);
290        let ArchExtendedPstateStorage::State32(state32) = &storage else { unreachable!() };
291        assert_eq!(state32.get_arm32_qregs(), &[0u128; 16]);
292        assert_eq!(state32.get_arm32_fpsr(), 0);
293        assert_eq!(state32.get_arm32_fpcr(), 0);
294
295        // Arch32 -> Arch64 execve switches storage variant back to a fresh `State64`.
296        storage.reset_with_arch(ArchWidth::Arch64);
297        assert!(matches!(storage, ArchExtendedPstateStorage::State64(_)));
298        assert!(!storage.sve_vl_inherit());
299    }
300
301    /// Returns the address of the boxed 64-bit state, to verify that copies are deep copies.
302    fn state64_ptr(storage: &ArchExtendedPstateStorage) -> *const ExtendedPstateState {
303        let ArchExtendedPstateStorage::State64(state) = storage else { unreachable!() };
304        std::ptr::from_ref(state.as_ref())
305    }
306
307    fn assert_state64_regs(
308        storage: &ArchExtendedPstateStorage,
309        qregs: &[u128; 32],
310        fpsr: u32,
311        fpcr: u32,
312        pregs: &[u16; 16],
313        ffr: u16,
314        context: &str,
315    ) {
316        let ArchExtendedPstateStorage::State64(state) = storage else { unreachable!() };
317        assert_eq!(state.get_arm64_qregs(), qregs, "{context}");
318        assert_eq!(state.get_arm64_fpsr(), fpsr, "{context}");
319        assert_eq!(state.get_arm64_fpcr(), fpcr, "{context}");
320        assert_eq!(state.get_arm64_p_regs(), pregs, "{context}");
321        assert_eq!(state.get_arm64_ffr(), ffr, "{context}");
322    }
323
324    #[::fuchsia::test]
325    fn test_arch_extended_pstate_storage_clone_and_reset_preserve_sve_flags() {
326        let qregs = [0x00112233445566778899aabbccddeeffu128; 32];
327        let pregs = [0x0f0fu16; 16];
328        // Regardless of whether the host CPU supports SVE, `Clone` (fork/clone), `reset` and
329        // `reset_with_arch` (execve) must preserve `has_sve` and `sve_vl_inherit` exactly as set,
330        // for every combination of the two flags.
331        for has_sve in [false, true] {
332            for inherit in [false, true] {
333                let context = format!("has_sve={has_sve} inherit={inherit}");
334                let mut pstate = ExtendedPstateState::with_sve(has_sve);
335                pstate.set_arm64_state(&qregs, 0x10, 0x20);
336                pstate.set_arm64_sve_state(&pregs, 0x00ff);
337                let mut storage = ArchExtendedPstateStorage::State64(Box::new(pstate));
338                storage.set_sve_vl_inherit(inherit);
339                assert_eq!(storage.has_sve(), has_sve, "{context}");
340                assert_eq!(storage.sve_vl_inherit(), inherit, "{context}");
341
342                // `Clone` deep-copies the flags and every register into a separate allocation.
343                let mut cloned = storage.clone();
344                assert_eq!(cloned.has_sve(), has_sve, "{context}");
345                assert_eq!(cloned.sve_vl_inherit(), inherit, "{context}");
346                assert_ne!(state64_ptr(&cloned), state64_ptr(&storage), "{context}");
347                assert_state64_regs(&cloned, &qregs, 0x10, 0x20, &pregs, 0x00ff, &context);
348
349                // Mutating the clone leaves the original untouched, and vice versa.
350                cloned.set_sve_vl_inherit(!inherit);
351                cloned.reset();
352                assert_eq!(cloned.has_sve(), has_sve, "{context}");
353                assert_eq!(cloned.sve_vl_inherit(), !inherit, "{context}");
354                assert_state64_regs(&cloned, &[0u128; 32], 0, 0, &[0u16; 16], 0, &context);
355                assert_eq!(storage.has_sve(), has_sve, "{context}");
356                assert_eq!(storage.sve_vl_inherit(), inherit, "{context}");
357                assert_state64_regs(&storage, &qregs, 0x10, 0x20, &pregs, 0x00ff, &context);
358
359                // `reset` zeroes the registers in place and keeps both flags.
360                let ptr_before_reset = state64_ptr(&storage);
361                storage.reset();
362                assert_eq!(storage.has_sve(), has_sve, "{context}");
363                assert_eq!(storage.sve_vl_inherit(), inherit, "{context}");
364                assert_eq!(state64_ptr(&storage), ptr_before_reset, "{context}");
365                assert_state64_regs(&storage, &[0u128; 32], 0, 0, &[0u16; 16], 0, &context);
366
367                // So does `reset_with_arch(Arch64)` on 64-bit storage (the `finish_exec` path).
368                let ArchExtendedPstateStorage::State64(state) = &mut storage else {
369                    unreachable!()
370                };
371                state.set_arm64_state(&qregs, 0x30, 0x40);
372                state.set_arm64_sve_state(&pregs, 0x0001);
373                storage.reset_with_arch(ArchWidth::Arch64);
374                assert_eq!(storage.has_sve(), has_sve, "{context}");
375                assert_eq!(storage.sve_vl_inherit(), inherit, "{context}");
376                assert_eq!(state64_ptr(&storage), ptr_before_reset, "{context}");
377                assert_state64_regs(&storage, &[0u128; 32], 0, 0, &[0u16; 16], 0, &context);
378            }
379        }
380    }
381
382    #[::fuchsia::test]
383    fn test_thread_state_snapshot_preserves_sve_state() {
384        // `ThreadState::snapshot` is how `clone_task` copies the parent's thread state into the
385        // child on fork/clone: the child must inherit `has_sve`, `PR_SVE_VL_INHERIT`, and the live
386        // Q0-Q31/FPSR/FPCR/P0-P15/FFR registers, in its own storage.
387        let qregs = [0x0123456789abcdef0123456789abcdefu128; 32];
388        let pregs = [0x8001u16; 16];
389        for has_sve in [false, true] {
390            for inherit in [false, true] {
391                let context = format!("has_sve={has_sve} inherit={inherit}");
392                let mut parent = ThreadState::<HeapRegs>::default();
393                let mut pstate = ExtendedPstateState::with_sve(has_sve);
394                pstate.set_arm64_state(&qregs, 0x10, 0x20);
395                pstate.set_arm64_sve_state(&pregs, 0x0007);
396                parent.extended_pstate = ArchExtendedPstateStorage::State64(Box::new(pstate));
397                parent.extended_pstate.set_sve_vl_inherit(inherit);
398
399                for mut child in
400                    [parent.snapshot::<HeapRegs>(), parent.extended_snapshot::<HeapRegs>()]
401                {
402                    assert_eq!(child.arch_width(), ArchWidth::Arch64, "{context}");
403                    assert_eq!(child.extended_pstate.has_sve(), has_sve, "{context}");
404                    assert_eq!(child.extended_pstate.sve_vl_inherit(), inherit, "{context}");
405                    assert_ne!(
406                        state64_ptr(&child.extended_pstate),
407                        state64_ptr(&parent.extended_pstate),
408                        "{context}"
409                    );
410                    assert_state64_regs(
411                        &child.extended_pstate,
412                        &qregs,
413                        0x10,
414                        0x20,
415                        &pregs,
416                        0x0007,
417                        &context,
418                    );
419
420                    // The child's `PR_SVE_SET_VL` and `execve` do not affect the parent.
421                    child.extended_pstate.set_sve_vl_inherit(!inherit);
422                    child.extended_pstate.reset_with_arch(child.arch_width());
423                    assert_eq!(child.extended_pstate.has_sve(), has_sve, "{context}");
424                    assert_eq!(child.extended_pstate.sve_vl_inherit(), !inherit, "{context}");
425                    assert_state64_regs(
426                        &child.extended_pstate,
427                        &[0u128; 32],
428                        0,
429                        0,
430                        &[0u16; 16],
431                        0,
432                        &context,
433                    );
434                    assert_eq!(parent.extended_pstate.has_sve(), has_sve, "{context}");
435                    assert_eq!(parent.extended_pstate.sve_vl_inherit(), inherit, "{context}");
436                    assert_state64_regs(
437                        &parent.extended_pstate,
438                        &qregs,
439                        0x10,
440                        0x20,
441                        &pregs,
442                        0x0007,
443                        &context,
444                    );
445                }
446
447                // Nor does the parent's `execve` affect an existing child.
448                let child = parent.snapshot::<HeapRegs>();
449                parent.extended_pstate.reset_with_arch(parent.arch_width());
450                assert_state64_regs(
451                    &child.extended_pstate,
452                    &qregs,
453                    0x10,
454                    0x20,
455                    &pregs,
456                    0x0007,
457                    &context,
458                );
459                assert_eq!(child.extended_pstate.sve_vl_inherit(), inherit, "{context}");
460            }
461        }
462
463        // A 32-bit parent forks a 32-bit child, which never reports SVE support.
464        let mut parent32 = ThreadState::<HeapRegs>::default();
465        let mut regs32 = zx::sys::zx_restricted_state_t::default();
466        regs32.cpsr = zx::sys::ZX_REG_CPSR_ARCH_32_MASK as u32;
467        parent32.registers.load(regs32);
468        parent32.extended_pstate.reset_with_arch(parent32.arch_width());
469        assert!(matches!(parent32.extended_pstate, ArchExtendedPstateStorage::State32(_)));
470        let child32 = parent32.snapshot::<HeapRegs>();
471        assert_eq!(child32.arch_width(), ArchWidth::Arch32);
472        assert!(matches!(child32.extended_pstate, ArchExtendedPstateStorage::State32(_)));
473        assert!(!child32.extended_pstate.has_sve());
474        assert!(!child32.extended_pstate.sve_vl_inherit());
475    }
476
477    #[::fuchsia::test]
478    fn test_thread_state_finish_exec_cross_arch_and_sve_inheritance() {
479        // Mirrors the exact sequence in `CurrentTask::finish_exec`:
480        //   self.thread_state.registers.load(regs);
481        //   self.thread_state.extended_pstate.reset_with_arch(self.thread_state.arch_width());
482        fn simulate_finish_exec(
483            thread_state: &mut ThreadState<HeapRegs>,
484            regs: zx::sys::zx_restricted_state_t,
485        ) {
486            thread_state.registers.load(regs);
487            thread_state.extended_pstate.reset_with_arch(thread_state.arch_width());
488        }
489
490        let qregs = [0x112233445566778899aabbccddeeff00u128; 32];
491        let pregs = [0x5a5au16; 16];
492
493        let mut thread_state = ThreadState::<HeapRegs>::default();
494        assert_eq!(thread_state.arch_width(), ArchWidth::Arch64);
495        assert!(!thread_state.is_arch32());
496
497        let mut pstate = ExtendedPstateState::with_sve(true);
498        pstate.set_arm64_state(&qregs, 0x10, 0x20);
499        pstate.set_arm64_sve_state(&pregs, 0x00ff);
500        thread_state.extended_pstate = ArchExtendedPstateStorage::State64(Box::new(pstate));
501
502        // 1. `PR_SVE_SET_VL_ONEXEC` without `PR_SVE_VL_INHERIT`:
503        // Updating `sve_vl_inherit` before `execve` leaves live Q/FPSR/FPCR and SVE P/FFR state
504        // intact until `finish_exec` runs `reset_with_arch(ArchWidth::Arch64)`.
505        thread_state.extended_pstate.set_sve_vl_inherit(false);
506        let state64_box_ptr = match &thread_state.extended_pstate {
507            ArchExtendedPstateStorage::State64(state) => {
508                assert_eq!(state.get_arm64_qregs(), &qregs);
509                assert_eq!(state.get_arm64_p_regs(), &pregs);
510                assert_eq!(state.get_arm64_ffr(), 0x00ff);
511                std::ptr::from_ref(state.as_ref())
512            }
513            ArchExtendedPstateStorage::State32(_) => unreachable!(),
514        };
515
516        let mut regs64 = zx::sys::zx_restricted_state_t::default();
517        regs64.cpsr = 0;
518        simulate_finish_exec(&mut thread_state, regs64);
519        assert_eq!(thread_state.arch_width(), ArchWidth::Arch64);
520        assert!(thread_state.extended_pstate.has_sve());
521        assert!(!thread_state.extended_pstate.sve_vl_inherit());
522        // SAFETY: `thread_state.extended_pstate` is `State64`.
523        let raw_ptr64 =
524            unsafe { thread_state.extended_pstate.as_ptr().extended_pstate.cast_const() };
525        assert_eq!(raw_ptr64, state64_box_ptr);
526        let ArchExtendedPstateStorage::State64(state) = &mut thread_state.extended_pstate else {
527            unreachable!()
528        };
529        assert_eq!(state.get_arm64_qregs(), &[0u128; 32]);
530        assert_eq!(state.get_arm64_fpsr(), 0);
531        assert_eq!(state.get_arm64_fpcr(), 0);
532        assert_eq!(state.get_arm64_p_regs(), &[0u16; 16]);
533        assert_eq!(state.get_arm64_ffr(), 0);
534
535        // 2. `PR_SVE_VL_INHERIT` across `Arch64 -> Arch64` `execve`:
536        // Register state is zeroed on `finish_exec`, while `has_sve` and `sve_vl_inherit` persist.
537        state.set_arm64_state(&qregs, 0x33, 0x44);
538        state.set_arm64_sve_state(&pregs, 0x00ff);
539        thread_state.extended_pstate.set_sve_vl_inherit(true);
540        simulate_finish_exec(&mut thread_state, regs64);
541        assert_eq!(thread_state.arch_width(), ArchWidth::Arch64);
542        assert!(thread_state.extended_pstate.has_sve());
543        assert!(thread_state.extended_pstate.sve_vl_inherit());
544        let ArchExtendedPstateStorage::State64(state) = &mut thread_state.extended_pstate else {
545            unreachable!()
546        };
547        assert_eq!(state.get_arm64_qregs(), &[0u128; 32]);
548        assert_eq!(state.get_arm64_fpsr(), 0);
549        assert_eq!(state.get_arm64_fpcr(), 0);
550        assert_eq!(state.get_arm64_p_regs(), &[0u16; 16]);
551        assert_eq!(state.get_arm64_ffr(), 0);
552
553        // 3. `Arch64 -> Arch32` `execve` via `registers.load(regs32)` + `reset_with_arch`:
554        // Reinitializes `extended_pstate` to `State32`, clearing `has_sve` and `sve_vl_inherit`.
555        state.set_arm64_state(&qregs, 0x55, 0x66);
556        state.set_arm64_sve_state(&pregs, 0x00ff);
557        let mut regs32 = zx::sys::zx_restricted_state_t::default();
558        regs32.cpsr = zx::sys::ZX_REG_CPSR_ARCH_32_MASK as u32;
559        simulate_finish_exec(&mut thread_state, regs32);
560        assert_eq!(thread_state.arch_width(), ArchWidth::Arch32);
561        assert!(thread_state.is_arch32());
562        assert!(!thread_state.extended_pstate.has_sve());
563        assert!(!thread_state.extended_pstate.sve_vl_inherit());
564        let state32_box_ptr = match &thread_state.extended_pstate {
565            ArchExtendedPstateStorage::State32(state32) => {
566                assert_eq!(state32.get_arm32_qregs(), &[0u128; 16]);
567                assert_eq!(state32.get_arm32_fpsr(), 0);
568                assert_eq!(state32.get_arm32_fpcr(), 0);
569                std::ptr::from_ref(state32.as_ref())
570            }
571            ArchExtendedPstateStorage::State64(_) => unreachable!(),
572        };
573        // SAFETY: `thread_state.extended_pstate` is `State32`.
574        let raw_ptr32 =
575            unsafe { thread_state.extended_pstate.as_ptr().extended_aarch32_pstate.cast_const() };
576        assert_eq!(raw_ptr32, state32_box_ptr);
577
578        // 4. `Arch32 -> Arch64` `execve` via `registers.load(regs64)` + `reset_with_arch`:
579        // Reinitializes `extended_pstate` to a fresh `State64` with `sve_vl_inherit == false`.
580        simulate_finish_exec(&mut thread_state, regs64);
581        assert_eq!(thread_state.arch_width(), ArchWidth::Arch64);
582        assert!(!thread_state.is_arch32());
583        assert_eq!(
584            thread_state.extended_pstate.has_sve(),
585            ExtendedPstateState::default().has_arm64_sve()
586        );
587        assert!(!thread_state.extended_pstate.sve_vl_inherit());
588        let new_state64_box_ptr = match &thread_state.extended_pstate {
589            ArchExtendedPstateStorage::State64(state64) => {
590                assert_eq!(state64.get_arm64_qregs(), &[0u128; 32]);
591                assert_eq!(state64.get_arm64_fpsr(), 0);
592                assert_eq!(state64.get_arm64_fpcr(), 0);
593                assert_eq!(state64.get_arm64_p_regs(), &[0u16; 16]);
594                assert_eq!(state64.get_arm64_ffr(), 0);
595                std::ptr::from_ref(state64.as_ref())
596            }
597            ArchExtendedPstateStorage::State32(_) => unreachable!(),
598        };
599        // SAFETY: `thread_state.extended_pstate` is `State64`.
600        let new_raw_ptr64 =
601            unsafe { thread_state.extended_pstate.as_ptr().extended_pstate.cast_const() };
602        assert_eq!(new_raw_ptr64, new_state64_box_ptr);
603    }
604}