Skip to main content

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)]
21#[repr(C)]
22pub struct ExtendedPstateState {
23    #[cfg(target_arch = "x86_64")]
24    state: x86_64::State,
25
26    #[cfg(target_arch = "aarch64")]
27    state: aarch64::State,
28
29    #[cfg(target_arch = "riscv64")]
30    state: riscv64::State,
31}
32
33#[cfg(target_arch = "aarch64")]
34/// A version of [`ExtendedPstateState`] that only stores the processor state
35/// accessible from AArch32 (e.g., registers Q0-Q15).
36#[derive(Clone, Copy, Default)]
37#[repr(C)]
38pub struct ExtendedAarch32PstateState {
39    state: aarch64::Aarch32State,
40}
41
42#[cfg(target_arch = "aarch64")]
43impl ExtendedAarch32PstateState {
44    pub fn reset(&mut self) {
45        self.state.reset()
46    }
47
48    pub fn get_arm32_qregs(&self) -> &[u128; 16] {
49        &self.state.q
50    }
51
52    pub fn get_arm32_fpsr(&self) -> u32 {
53        self.state.fpsr
54    }
55
56    pub fn get_arm32_fpcr(&self) -> u32 {
57        self.state.fpcr
58    }
59
60    pub fn set_arm32_state(&mut self, qregs: &[u128; 16], fpsr: u32, fpcr: u32) {
61        self.state.q = *qregs;
62        self.state.fpsr = fpsr;
63        self.state.fpcr = fpcr;
64    }
65}
66
67impl ExtendedPstateState {
68    #[cfg(target_arch = "x86_64")]
69    pub fn with_strategy(strategy: x86_64::Strategy) -> Self {
70        Self { state: x86_64::State::with_strategy(strategy) }
71    }
72
73    pub fn reset(&mut self) {
74        self.state.reset()
75    }
76
77    #[cfg(target_arch = "aarch64")]
78    pub fn get_arm64_qregs(&self) -> &[u128; 32] {
79        &self.state.q
80    }
81
82    #[cfg(target_arch = "aarch64")]
83    pub fn get_arm64_fpsr(&self) -> u32 {
84        self.state.fpsr
85    }
86
87    #[cfg(target_arch = "aarch64")]
88    pub fn get_arm64_fpcr(&self) -> u32 {
89        self.state.fpcr
90    }
91
92    #[cfg(target_arch = "aarch64")]
93    pub fn set_arm64_state(&mut self, qregs: &[u128; 32], fpsr: u32, fpcr: u32) {
94        self.state.q = *qregs;
95        self.state.fpsr = fpsr;
96        self.state.fpcr = fpcr;
97    }
98
99    #[cfg(target_arch = "aarch64")]
100    pub fn with_sve(has_sve: bool) -> Self {
101        Self { state: aarch64::State { has_sve: u8::from(has_sve), ..Default::default() } }
102    }
103
104    #[cfg(target_arch = "aarch64")]
105    pub fn has_arm64_sve(&self) -> bool {
106        self.state.has_sve != 0
107    }
108
109    #[cfg(target_arch = "aarch64")]
110    pub fn get_arm64_p_regs(&self) -> &[u16; 16] {
111        &self.state.p
112    }
113
114    #[cfg(target_arch = "aarch64")]
115    pub fn get_arm64_ffr(&self) -> u16 {
116        self.state.ffr
117    }
118
119    #[cfg(target_arch = "aarch64")]
120    pub fn set_arm64_sve_state(&mut self, p_regs: &[u16; 16], ffr: u16) {
121        self.state.p = *p_regs;
122        // Per the Arm ARM for WRFFR, FFR must be a monotonically decreasing sequence of 1s
123        // starting from bit 0 (2^k - 1 for 0 <= k <= 16), and the result of writing any other
124        // value is not defined by the architecture. Linux copies a user-supplied FFR (e.g. from
125        // a signal frame) verbatim into the task's SVE state and leaves it to the hardware; Starnix
126        // instead canonicalizes the value by masking it to the contiguous prefix of 1s starting
127        // from bit 0, so that `wrffr` in `restore_extended_pstate` always has well-defined
128        // behavior. Architecturally valid values are stored unchanged.
129        self.state.ffr = ((1u32 << ffr.trailing_ones()) - 1) as u16;
130    }
131
132    #[cfg(target_arch = "aarch64")]
133    pub fn reset_arm64_sve_state(&mut self) {
134        self.state.p = [0; 16];
135        self.state.ffr = 0;
136    }
137
138    /// Whether the SVE vector length is inherited across `execve` (`PR_SVE_VL_INHERIT`).
139    #[cfg(target_arch = "aarch64")]
140    pub fn get_arm64_sve_vl_inherit(&self) -> bool {
141        self.state.sve_vl_inherit != 0
142    }
143
144    #[cfg(target_arch = "aarch64")]
145    pub fn set_arm64_sve_vl_inherit(&mut self, inherit: bool) {
146        self.state.sve_vl_inherit = u8::from(inherit);
147    }
148
149    #[cfg(target_arch = "riscv64")]
150    pub fn get_riscv64_state(&self) -> &riscv64::State {
151        &self.state
152    }
153
154    #[cfg(target_arch = "riscv64")]
155    pub fn get_riscv64_state_mut(&mut self) -> &mut riscv64::State {
156        &mut self.state
157    }
158
159    #[cfg(target_arch = "x86_64")]
160    pub fn get_x64_xsave_area(&self) -> [u8; X64_XSAVE_AREA_SIZE] {
161        #[allow(
162            clippy::undocumented_unsafe_blocks,
163            reason = "Force documented unsafe blocks in Starnix"
164        )]
165        unsafe {
166            std::mem::transmute(self.state.buffer)
167        }
168    }
169
170    #[cfg(target_arch = "x86_64")]
171    pub fn set_x64_xsave_area(&mut self, xsave_area: [u8; X64_XSAVE_AREA_SIZE]) {
172        self.state.set_xsave_area(xsave_area);
173    }
174}
175
176/// Stores a pointer to the currently active extended pstate storage.
177/// The caller to the C entry points is responsible for ensuring that the active union
178/// member corresponds to the entry points being called.
179#[repr(C)]
180pub union ExtendedPstatePointer {
181    pub extended_pstate: *mut ExtendedPstateState,
182    #[cfg(target_arch = "aarch64")]
183    pub extended_aarch32_pstate: *mut ExtendedAarch32PstateState,
184}
185
186// Provided by assembly targets.
187unsafe extern "C" {
188    pub fn save_extended_pstate(state_addr: usize);
189    pub fn restore_extended_pstate(state_addr: usize);
190
191    #[cfg(target_arch = "aarch64")]
192    pub fn save_extended_aarch32_pstate(state_addr: usize);
193    #[cfg(target_arch = "aarch64")]
194    pub fn restore_extended_aarch32_pstate(state_addr: usize);
195}