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
49pub fn claim_lc_mutex(jtag: &mut dyn Jtag, claim: bool) -> Result<()> {
50 if claim {
51 if jtag.read_lc_ctrl_reg(&LcCtrlReg::ClaimTransitionIf)?
53 == u8::from(MultiBitBool8::True) as u32
54 {
55 return Err(LcTransitionError::MutexAlreadyClaimed.into());
56 }
57
58 jtag.write_lc_ctrl_reg(
60 &LcCtrlReg::ClaimTransitionIf,
61 u8::from(MultiBitBool8::True) as u32,
62 )?;
63
64 if jtag.read_lc_ctrl_reg(&LcCtrlReg::ClaimTransitionIf)?
66 != u8::from(MultiBitBool8::True) as u32
67 {
68 return Err(LcTransitionError::FailedToClaimMutex.into());
69 }
70 } else {
71 jtag.write_lc_ctrl_reg(
73 &LcCtrlReg::ClaimTransitionIf,
74 u8::from(MultiBitBool8::False) as u32,
75 )?;
76 }
77 Ok(())
78}
79
80fn setup_lc_transition(
81 jtag: &mut dyn Jtag,
82 target_lc_state: DifLcCtrlState,
83 token: Option<[u32; 4]>,
84) -> Result<()> {
85 let status = jtag.read_lc_ctrl_reg(&LcCtrlReg::Status)?;
87 let status = LcCtrlStatus::from_bits(status).ok_or(LcTransitionError::InvalidState(status))?;
88 if status != LcCtrlStatus::INITIALIZED | LcCtrlStatus::READY {
89 return Err(LcTransitionError::LcCtrlNotReady(status).into());
90 }
91
92 claim_lc_mutex(jtag, true)?;
93
94 jtag.write_lc_ctrl_reg(
96 &LcCtrlReg::TransitionTarget,
97 target_lc_state.redundant_encoding(),
98 )?;
99
100 let target_lc_state_programmed = DifLcCtrlState::from_redundant_encoding(
102 jtag.read_lc_ctrl_reg(&LcCtrlReg::TransitionTarget)?,
103 )?;
104 if target_lc_state_programmed != target_lc_state {
105 return Err(
106 LcTransitionError::TargetProgrammingFailed(target_lc_state_programmed.into()).into(),
107 );
108 }
109
110 if let Some(token_words) = token {
112 let token_regs = [
113 &LcCtrlReg::TransitionToken0,
114 &LcCtrlReg::TransitionToken1,
115 &LcCtrlReg::TransitionToken2,
116 &LcCtrlReg::TransitionToken3,
117 ];
118
119 for (reg, value) in iter::zip(token_regs, token_words) {
120 jtag.write_lc_ctrl_reg(reg, value)?;
121 }
122 }
123
124 Ok(())
125}
126
127pub fn trigger_lc_transition(
177 transport: &TransportWrapper,
178 mut jtag: Box<dyn Jtag + '_>,
179 target_lc_state: DifLcCtrlState,
180 token: Option<[u32; 4]>,
181 use_external_clk: bool,
182 reset_tap_straps: Option<JtagTap>,
183) -> Result<()> {
184 setup_lc_transition(&mut *jtag, target_lc_state, token)?;
187
188 if use_external_clk {
190 jtag.write_lc_ctrl_reg(
191 &LcCtrlReg::TransitionCtrl,
192 LcCtrlTransitionCtrl::EXT_CLOCK_EN.bits(),
193 )?;
194 } else {
195 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, 0)?;
196 }
197
198 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
200
201 wait_for_status(
202 &mut *jtag,
203 Duration::from_secs(3),
204 LcCtrlStatus::TRANSITION_SUCCESSFUL,
205 )
206 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
207
208 let post_transition_lc_state = jtag.read_lc_ctrl_reg(&LcCtrlReg::LcState)?;
210 if post_transition_lc_state != DifLcCtrlState::PostTransition.redundant_encoding() {
211 return Err(LcTransitionError::BadPostTransitionState(post_transition_lc_state).into());
212 }
213
214 jtag.disconnect()?;
216 if let Some(tap) = reset_tap_straps {
217 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
218 match tap {
219 JtagTap::LcTap => transport.pin_strapping("PINMUX_TAP_LC")?.apply()?,
220 JtagTap::RiscvTap => transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?,
221 }
222 }
223 transport.reset(UartRx::Clear)?;
224
225 Ok(())
226}
227
228#[allow(clippy::too_many_arguments)]
240pub fn trigger_volatile_raw_unlock<'t>(
241 transport: &'t TransportWrapper,
242 mut jtag: Box<dyn Jtag + 't>,
243 target_lc_state: DifLcCtrlState,
244 hashed_token: Option<[u32; 4]>,
245 use_external_clk: bool,
246 post_transition_tap: JtagTap,
247 jtag_params: &JtagParams,
248 expect_raw_unlock_supported: bool,
249) -> Result<Box<dyn Jtag + 't>> {
250 setup_lc_transition(&mut *jtag, target_lc_state, hashed_token)?;
253
254 let mut ctrl = LcCtrlTransitionCtrl::VOLATILE_RAW_UNLOCK;
256 if use_external_clk {
257 ctrl |= LcCtrlTransitionCtrl::EXT_CLOCK_EN;
258 }
259 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, ctrl.bits())?;
260
261 let read = jtag.read_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl)?;
263 if read < 2u32 && expect_raw_unlock_supported {
264 return Err(LcTransitionError::VolatileRawUnlockNotSupported.into());
265 } else if read >= 2u32 && !expect_raw_unlock_supported {
266 return Err(LcTransitionError::VolatileRawUnlockSupported.into());
267 }
268
269 if post_transition_tap == JtagTap::RiscvTap {
271 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
272 transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?;
273 }
274
275 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
277
278 if post_transition_tap == JtagTap::RiscvTap {
282 jtag.disconnect()?;
283 jtag = transport.jtag(jtag_params)?.connect(JtagTap::RiscvTap)?;
284 }
285
286 if expect_raw_unlock_supported {
287 wait_for_status(
288 &mut *jtag,
289 Duration::from_secs(3),
290 LcCtrlStatus::TRANSITION_SUCCESSFUL,
291 )
292 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
293 } else {
294 let mut status = LcCtrlStatus::INITIALIZED | LcCtrlStatus::TOKEN_ERROR;
295 if use_external_clk {
296 status |= LcCtrlStatus::EXT_CLOCK_SWITCHED;
297 }
298 wait_for_status(&mut *jtag, Duration::from_secs(3), status)
299 .context("failed waiting for TOKEN_ERROR status.")?;
300 }
301 Ok(jtag)
302}
303
304pub fn wait_for_status(jtag: &mut dyn Jtag, timeout: Duration, status: LcCtrlStatus) -> Result<()> {
305 let jtag_tap = jtag.tap();
306
307 poll::poll_until(timeout, Duration::from_millis(1), || {
309 let polled_status = match jtag_tap {
310 JtagTap::LcTap => jtag.read_lc_ctrl_reg(&LcCtrlReg::Status).unwrap(),
311 JtagTap::RiscvTap => {
312 let mut status = [0u32];
313 jtag.read_memory32(
314 top_earlgrey::LC_CTRL_REGS_BASE_ADDR as u32 + LcCtrlReg::Status as u32,
315 &mut status,
316 )?;
317 status[0]
318 }
319 };
320
321 let polled_status =
322 LcCtrlStatus::from_bits(polled_status).context("status has invalid bits set")?;
323
324 if polled_status.intersects(LcCtrlStatus::ERRORS & !status) {
328 bail!("status {polled_status:#b} has error bits set");
329 }
330
331 Ok(polled_status.contains(status))
332 })
333}