opentitanlib/test_utils/
lc_transition.rs1use std::iter;
6use std::time::Duration;
7
8use anyhow::{Context, Result, bail};
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use crate::app::TransportWrapper;
13use crate::chip::boolean::MultiBitBool8;
14use crate::dif::lc_ctrl::{
15 DifLcCtrlState, LcCtrlReg, LcCtrlStatus, LcCtrlTransitionCmd, LcCtrlTransitionCtrl,
16};
17use crate::impl_serializable_error;
18use crate::io::jtag::{Jtag, JtagParams, JtagTap};
19use crate::test_utils::poll;
20
21use top_earlgrey::top_earlgrey;
22
23#[derive(Error, Debug, Deserialize, Serialize)]
25pub enum LcTransitionError {
26 #[error("LC controller not ready to perform an LC transition (status: 0x{0:x}).")]
27 LcCtrlNotReady(LcCtrlStatus),
28 #[error("LC transition mutex was already claimed.")]
29 MutexAlreadyClaimed,
30 #[error("Failed to claim LC transition mutex.")]
31 FailedToClaimMutex,
32 #[error("Volatile raw unlock is not supported on this chip.")]
33 VolatileRawUnlockNotSupported,
34 #[error("Volatile raw unlock is unexpectedly supported on this chip.")]
35 VolatileRawUnlockSupported,
36 #[error("LC transition target programming failed (target state: 0x{0:x}).")]
37 TargetProgrammingFailed(u32),
38 #[error("LC transition failed (status: 0x{0:x}).")]
39 TransitionFailed(LcCtrlStatus),
40 #[error("Bad post transition LC state: 0x{0:x}.")]
41 BadPostTransitionState(u32),
42 #[error("Invalid LC state: {0:x}")]
43 InvalidState(u32),
44 #[error("Generic error {0}")]
45 Generic(String),
46}
47impl_serializable_error!(LcTransitionError);
48
49fn setup_lc_transition(
50 jtag: &mut dyn Jtag,
51 target_lc_state: DifLcCtrlState,
52 token: Option<[u32; 4]>,
53) -> Result<()> {
54 let status = jtag.read_lc_ctrl_reg(&LcCtrlReg::Status)?;
56 let status = LcCtrlStatus::from_bits(status).ok_or(LcTransitionError::InvalidState(status))?;
57 if status != LcCtrlStatus::INITIALIZED | LcCtrlStatus::READY {
58 return Err(LcTransitionError::LcCtrlNotReady(status).into());
59 }
60
61 if jtag.read_lc_ctrl_reg(&LcCtrlReg::ClaimTransitionIf)? == u8::from(MultiBitBool8::True) as u32
63 {
64 return Err(LcTransitionError::MutexAlreadyClaimed.into());
65 }
66
67 jtag.write_lc_ctrl_reg(
69 &LcCtrlReg::ClaimTransitionIf,
70 u8::from(MultiBitBool8::True) as u32,
71 )?;
72
73 if jtag.read_lc_ctrl_reg(&LcCtrlReg::ClaimTransitionIf)? != u8::from(MultiBitBool8::True) as u32
75 {
76 return Err(LcTransitionError::FailedToClaimMutex.into());
77 }
78
79 jtag.write_lc_ctrl_reg(
81 &LcCtrlReg::TransitionTarget,
82 target_lc_state.redundant_encoding(),
83 )?;
84
85 let target_lc_state_programmed = DifLcCtrlState::from_redundant_encoding(
87 jtag.read_lc_ctrl_reg(&LcCtrlReg::TransitionTarget)?,
88 )?;
89 if target_lc_state_programmed != target_lc_state {
90 return Err(
91 LcTransitionError::TargetProgrammingFailed(target_lc_state_programmed.into()).into(),
92 );
93 }
94
95 if let Some(token_words) = token {
97 let token_regs = [
98 &LcCtrlReg::TransitionToken0,
99 &LcCtrlReg::TransitionToken1,
100 &LcCtrlReg::TransitionToken2,
101 &LcCtrlReg::TransitionToken3,
102 ];
103
104 for (reg, value) in iter::zip(token_regs, token_words) {
105 jtag.write_lc_ctrl_reg(reg, value)?;
106 }
107 }
108
109 Ok(())
110}
111
112pub fn trigger_lc_transition(
163 transport: &TransportWrapper,
164 mut jtag: Box<dyn Jtag + '_>,
165 target_lc_state: DifLcCtrlState,
166 token: Option<[u32; 4]>,
167 use_external_clk: bool,
168 reset_delay: Duration,
169 reset_tap_straps: Option<JtagTap>,
170) -> Result<()> {
171 setup_lc_transition(&mut *jtag, target_lc_state, token)?;
174
175 if use_external_clk {
177 jtag.write_lc_ctrl_reg(
178 &LcCtrlReg::TransitionCtrl,
179 LcCtrlTransitionCtrl::EXT_CLOCK_EN.bits(),
180 )?;
181 } else {
182 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, 0)?;
183 }
184
185 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
187
188 wait_for_status(
189 &mut *jtag,
190 Duration::from_secs(3),
191 LcCtrlStatus::TRANSITION_SUCCESSFUL,
192 )
193 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
194
195 let post_transition_lc_state = jtag.read_lc_ctrl_reg(&LcCtrlReg::LcState)?;
197 if post_transition_lc_state != DifLcCtrlState::PostTransition.redundant_encoding() {
198 return Err(LcTransitionError::BadPostTransitionState(post_transition_lc_state).into());
199 }
200
201 jtag.disconnect()?;
203 if let Some(tap) = reset_tap_straps {
204 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
205 match tap {
206 JtagTap::LcTap => transport.pin_strapping("PINMUX_TAP_LC")?.apply()?,
207 JtagTap::RiscvTap => transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?,
208 }
209 }
210 transport.reset_target(reset_delay, true)?;
211
212 Ok(())
213}
214
215#[allow(clippy::too_many_arguments)]
227pub fn trigger_volatile_raw_unlock<'t>(
228 transport: &'t TransportWrapper,
229 mut jtag: Box<dyn Jtag + 't>,
230 target_lc_state: DifLcCtrlState,
231 hashed_token: Option<[u32; 4]>,
232 use_external_clk: bool,
233 post_transition_tap: JtagTap,
234 jtag_params: &JtagParams,
235 expect_raw_unlock_supported: bool,
236) -> Result<Box<dyn Jtag + 't>> {
237 setup_lc_transition(&mut *jtag, target_lc_state, hashed_token)?;
240
241 let mut ctrl = LcCtrlTransitionCtrl::VOLATILE_RAW_UNLOCK;
243 if use_external_clk {
244 ctrl |= LcCtrlTransitionCtrl::EXT_CLOCK_EN;
245 }
246 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, ctrl.bits())?;
247
248 let read = jtag.read_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl)?;
250 if read < 2u32 && expect_raw_unlock_supported {
251 return Err(LcTransitionError::VolatileRawUnlockNotSupported.into());
252 } else if read >= 2u32 && !expect_raw_unlock_supported {
253 return Err(LcTransitionError::VolatileRawUnlockSupported.into());
254 }
255
256 if post_transition_tap == JtagTap::RiscvTap {
258 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
259 transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?;
260 }
261
262 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
264
265 if post_transition_tap == JtagTap::RiscvTap {
269 jtag.disconnect()?;
270 jtag = transport.jtag(jtag_params)?.connect(JtagTap::RiscvTap)?;
271 }
272
273 if expect_raw_unlock_supported {
274 wait_for_status(
275 &mut *jtag,
276 Duration::from_secs(3),
277 LcCtrlStatus::TRANSITION_SUCCESSFUL,
278 )
279 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
280 } else {
281 let mut status = LcCtrlStatus::INITIALIZED | LcCtrlStatus::TOKEN_ERROR;
282 if use_external_clk {
283 status |= LcCtrlStatus::EXT_CLOCK_SWITCHED;
284 }
285 wait_for_status(&mut *jtag, Duration::from_secs(3), status)
286 .context("failed waiting for TOKEN_ERROR status.")?;
287 }
288 Ok(jtag)
289}
290
291pub fn wait_for_status(jtag: &mut dyn Jtag, timeout: Duration, status: LcCtrlStatus) -> Result<()> {
292 let jtag_tap = jtag.tap();
293
294 poll::poll_until(timeout, Duration::from_millis(50), || {
296 let polled_status = match jtag_tap {
297 JtagTap::LcTap => jtag.read_lc_ctrl_reg(&LcCtrlReg::Status).unwrap(),
298 JtagTap::RiscvTap => {
299 let mut status = [0u32];
300 jtag.read_memory32(
301 top_earlgrey::LC_CTRL_REGS_BASE_ADDR as u32 + LcCtrlReg::Status as u32,
302 &mut status,
303 )?;
304 status[0]
305 }
306 };
307
308 let polled_status =
309 LcCtrlStatus::from_bits(polled_status).context("status has invalid bits set")?;
310
311 if polled_status.intersects(LcCtrlStatus::ERRORS & !status) {
315 bail!("status {polled_status:#b} has error bits set");
316 }
317
318 Ok(polled_status.contains(status))
319 })
320}