extended_pstate/
lib.rs

1// Copyright 2023 The Fuchsia 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#[cfg(target_arch = "x86_64")]
6pub mod x86_64;
7
8#[cfg(target_arch = "x86_64")]
9pub use x86_64::XSAVE_AREA_SIZE as X64_XSAVE_AREA_SIZE;
10
11#[cfg(target_arch = "x86_64")]
12pub use x86_64::SUPPORTED_XSAVE_FEATURES as X64_SUPPORTED_XSAVE_FEATURES;
13
14#[cfg(target_arch = "aarch64")]
15mod aarch64;
16
17#[cfg(target_arch = "riscv64")]
18pub mod riscv64;
19
20#[derive(Clone, Copy, Default)]
21pub struct ExtendedPstateState {
22    #[cfg(target_arch = "x86_64")]
23    state: x86_64::State,
24
25    #[cfg(target_arch = "aarch64")]
26    state: aarch64::State,
27
28    #[cfg(target_arch = "riscv64")]
29    state: riscv64::State,
30}
31
32impl ExtendedPstateState {
33    /// Runs the provided function with the saved extended processor state.  The
34    /// provided function must be written carefully to avoid inadvertently using
35    /// any extended state itself such as vector or floating point registers.
36    ///
37    /// # Safety
38    ///
39    /// |f| must not use any extended processor state.
40    pub unsafe fn run_with_saved_state<F, R>(&mut self, f: F) -> R
41    where
42        F: FnOnce() -> R,
43    {
44        #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
45        unsafe {
46            self.restore();
47        }
48        let r = f();
49        self.save();
50        r
51    }
52
53    #[cfg(target_arch = "x86_64")]
54    pub fn with_strategy(strategy: x86_64::Strategy) -> Self {
55        Self { state: x86_64::State::with_strategy(strategy) }
56    }
57
58    /// This saves the current extended processor state to this state object.
59    #[inline(always)]
60    fn save(&mut self) {
61        self.state.save()
62    }
63
64    #[inline(always)]
65    /// This restores the extended processor state saved in this object into the processor's state
66    /// registers.
67    ///
68    /// Safety: This clobbers the current vector register, floating point register, and floating
69    /// point status and control register state including callee-saved registers. This should be
70    /// used in conjunction with save() to switch to an alternate extended processor state.
71    unsafe fn restore(&self) {
72        #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
73        unsafe {
74            self.state.restore()
75        }
76    }
77
78    pub fn reset(&mut self) {
79        self.state.reset()
80    }
81
82    #[cfg(target_arch = "aarch64")]
83    pub fn get_arm64_qregs(&self) -> &[u128; 32] {
84        &self.state.q
85    }
86
87    #[cfg(target_arch = "aarch64")]
88    pub fn get_arm64_fpsr(&self) -> u32 {
89        self.state.fpsr
90    }
91
92    #[cfg(target_arch = "aarch64")]
93    pub fn get_arm64_fpcr(&self) -> u32 {
94        self.state.fpcr
95    }
96
97    #[cfg(target_arch = "aarch64")]
98    pub fn set_arm64_state(&mut self, qregs: &[u128; 32], fpsr: u32, fpcr: u32) {
99        self.state.q = *qregs;
100        self.state.fpsr = fpsr;
101        self.state.fpcr = fpcr;
102    }
103
104    #[cfg(target_arch = "riscv64")]
105    pub fn get_riscv64_state(&self) -> &riscv64::State {
106        &self.state
107    }
108
109    #[cfg(target_arch = "riscv64")]
110    pub fn get_riscv64_state_mut(&mut self) -> &mut riscv64::State {
111        &mut self.state
112    }
113
114    #[cfg(target_arch = "x86_64")]
115    pub fn get_x64_xsave_area(&self) -> [u8; X64_XSAVE_AREA_SIZE] {
116        #[allow(
117            clippy::undocumented_unsafe_blocks,
118            reason = "Force documented unsafe blocks in Starnix"
119        )]
120        unsafe {
121            std::mem::transmute(self.state.buffer)
122        }
123    }
124
125    #[cfg(target_arch = "x86_64")]
126    pub fn set_x64_xsave_area(&mut self, xsave_area: [u8; X64_XSAVE_AREA_SIZE]) {
127        self.state.set_xsave_area(xsave_area);
128    }
129}
130
131#[unsafe(no_mangle)]
132unsafe extern "C" fn restore_extended_pstate(state_addr: usize) {
133    let state = state_addr as *mut ExtendedPstateState;
134    #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
135    unsafe {
136        (&*state).restore()
137    }
138}
139
140#[unsafe(no_mangle)]
141unsafe extern "C" fn save_extended_pstate(state_addr: usize) {
142    let state = state_addr as *mut ExtendedPstateState;
143    #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
144    unsafe {
145        (&mut *state).save()
146    }
147}
148
149#[cfg(test)]
150mod test {
151    use super::*;
152
153    #[::fuchsia::test]
154    fn extended_pstate_state_lifecycle() {
155        let mut state = ExtendedPstateState::default();
156        #[allow(
157            clippy::undocumented_unsafe_blocks,
158            reason = "Force documented unsafe blocks in Starnix"
159        )]
160        unsafe {
161            state.save();
162            state.restore();
163        }
164    }
165}