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 ot_hal::dif::lc_ctrl::{
13 DifLcCtrlState, LcCtrlReg, LcCtrlStatus, LcCtrlTransitionCmd, LcCtrlTransitionCtrl,
14};
15use ot_hal::top::earlgrey as top_earlgrey;
16use ot_hal::util::multibits::MultiBitBool8;
17
18use crate::app::{TransportWrapper, UartRx};
19use crate::impl_serializable_error;
20use crate::io::jtag::{Jtag, JtagParams, JtagTap};
21use crate::test_utils::poll;
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(
162 transport: &TransportWrapper,
163 mut jtag: Box<dyn Jtag + '_>,
164 target_lc_state: DifLcCtrlState,
165 token: Option<[u32; 4]>,
166 use_external_clk: bool,
167 reset_tap_straps: Option<JtagTap>,
168) -> Result<()> {
169 setup_lc_transition(&mut *jtag, target_lc_state, token)?;
172
173 if use_external_clk {
175 jtag.write_lc_ctrl_reg(
176 &LcCtrlReg::TransitionCtrl,
177 LcCtrlTransitionCtrl::EXT_CLOCK_EN.bits(),
178 )?;
179 } else {
180 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, 0)?;
181 }
182
183 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
185
186 wait_for_status(
187 &mut *jtag,
188 Duration::from_secs(3),
189 LcCtrlStatus::TRANSITION_SUCCESSFUL,
190 )
191 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
192
193 let post_transition_lc_state = jtag.read_lc_ctrl_reg(&LcCtrlReg::LcState)?;
195 if post_transition_lc_state != DifLcCtrlState::PostTransition.redundant_encoding() {
196 return Err(LcTransitionError::BadPostTransitionState(post_transition_lc_state).into());
197 }
198
199 jtag.disconnect()?;
201 if let Some(tap) = reset_tap_straps {
202 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
203 match tap {
204 JtagTap::LcTap => transport.pin_strapping("PINMUX_TAP_LC")?.apply()?,
205 JtagTap::RiscvTap => transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?,
206 }
207 }
208 transport.reset(UartRx::Clear)?;
209
210 Ok(())
211}
212
213#[allow(clippy::too_many_arguments)]
225pub fn trigger_volatile_raw_unlock<'t>(
226 transport: &'t TransportWrapper,
227 mut jtag: Box<dyn Jtag + 't>,
228 target_lc_state: DifLcCtrlState,
229 hashed_token: Option<[u32; 4]>,
230 use_external_clk: bool,
231 post_transition_tap: JtagTap,
232 jtag_params: &JtagParams,
233 expect_raw_unlock_supported: bool,
234) -> Result<Box<dyn Jtag + 't>> {
235 setup_lc_transition(&mut *jtag, target_lc_state, hashed_token)?;
238
239 let mut ctrl = LcCtrlTransitionCtrl::VOLATILE_RAW_UNLOCK;
241 if use_external_clk {
242 ctrl |= LcCtrlTransitionCtrl::EXT_CLOCK_EN;
243 }
244 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, ctrl.bits())?;
245
246 let read = jtag.read_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl)?;
248 if read < 2u32 && expect_raw_unlock_supported {
249 return Err(LcTransitionError::VolatileRawUnlockNotSupported.into());
250 } else if read >= 2u32 && !expect_raw_unlock_supported {
251 return Err(LcTransitionError::VolatileRawUnlockSupported.into());
252 }
253
254 if post_transition_tap == JtagTap::RiscvTap {
256 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
257 transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?;
258 }
259
260 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
262
263 if post_transition_tap == JtagTap::RiscvTap {
267 jtag.disconnect()?;
268 jtag = transport.jtag(jtag_params)?.connect(JtagTap::RiscvTap)?;
269 }
270
271 if expect_raw_unlock_supported {
272 wait_for_status(
273 &mut *jtag,
274 Duration::from_secs(3),
275 LcCtrlStatus::TRANSITION_SUCCESSFUL,
276 )
277 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
278 } else {
279 let mut status = LcCtrlStatus::INITIALIZED | LcCtrlStatus::TOKEN_ERROR;
280 if use_external_clk {
281 status |= LcCtrlStatus::EXT_CLOCK_SWITCHED;
282 }
283 wait_for_status(&mut *jtag, Duration::from_secs(3), status)
284 .context("failed waiting for TOKEN_ERROR status.")?;
285 }
286 Ok(jtag)
287}
288
289pub fn wait_for_status(jtag: &mut dyn Jtag, timeout: Duration, status: LcCtrlStatus) -> Result<()> {
290 let jtag_tap = jtag.tap();
291
292 poll::poll_until(timeout, Duration::from_millis(1), || {
294 let polled_status = match jtag_tap {
295 JtagTap::LcTap => jtag.read_lc_ctrl_reg(&LcCtrlReg::Status).unwrap(),
296 JtagTap::RiscvTap => {
297 let mut status = [0u32];
298 jtag.read_memory32(
299 top_earlgrey::LC_CTRL_REGS_BASE_ADDR as u32 + LcCtrlReg::Status as u32,
300 &mut status,
301 )?;
302 status[0]
303 }
304 };
305
306 let polled_status =
307 LcCtrlStatus::from_bits(polled_status).context("status has invalid bits set")?;
308
309 if polled_status.intersects(LcCtrlStatus::ERRORS & !status) {
313 bail!("status {polled_status:#b} has error bits set");
314 }
315
316 Ok(polled_status.contains(status))
317 })
318}