Skip to content

Commit

Permalink
[opentitantool] HyperDebug support for advanced TPM primitives
Browse files Browse the repository at this point in the history
Add support in the HyperDebug transport driver for `TpmPoll` and
`GscReady`.

Signed-off-by: Jes B. Klinke <[email protected]>

Change-Id: I154229794bcb88cc7845329c03621eec5c4da4a5
  • Loading branch information
jesultra committed Nov 19, 2024
1 parent f65590c commit 9fcfe3e
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 18 deletions.
82 changes: 74 additions & 8 deletions sw/host/opentitanlib/src/transport/hyperdebug/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::rc::Rc;
use std::time::Duration;
use zerocopy::{AsBytes, FromBytes, FromZeroes};

use crate::io::gpio::GpioPin;
use crate::io::i2c::{self, Bus, DeviceStatus, DeviceTransfer, I2cError, ReadStatus, Transfer};
use crate::transport::hyperdebug::{BulkInterface, Inner};
use crate::transport::{TransportError, TransportInterfaceType};
Expand Down Expand Up @@ -59,7 +60,7 @@ struct CmdTransferLong {
write_count: u8,
read_count: u8,
read_count1: u8,
reserved: u8,
flags: u8,
data: [u8; USB_MAX_SIZE - 6],
}

Expand Down Expand Up @@ -175,7 +176,13 @@ impl HyperdebugI2cBus {
}

/// Transmit data for a single I2C operation, using one or more USB packets.
fn transmit_then_receive(&self, addr: u8, wbuf: &[u8], rbuf: &mut [u8]) -> Result<()> {
fn transmit_then_receive(
&self,
addr: u8,
wbuf: &[u8],
rbuf: &mut [u8],
gsc_ready: bool,
) -> Result<()> {
ensure!(
rbuf.len() < self.max_read_size,
I2cError::InvalidDataLength(rbuf.len())
Expand All @@ -185,7 +192,9 @@ impl HyperdebugI2cBus {
I2cError::InvalidDataLength(wbuf.len())
);
let encapsulation_header_size = if self.cmsis_encapsulation { 1 } else { 0 };
let mut index = if rbuf.len() < 128 {
let mut index = if false
/*rbuf.len() < 128*/
{
// Short format header
let mut req = CmdTransferShort {
encapsulation_header: Self::CMSIS_DAP_CUSTOM_COMMAND_I2C,
Expand All @@ -197,6 +206,10 @@ impl HyperdebugI2cBus {
};
let databytes = cmp::min(USB_MAX_SIZE - 4 - encapsulation_header_size, wbuf.len());
req.data[..databytes].clone_from_slice(&wbuf[..databytes]);
log::error!(
"I2C wrote {:?}",
&req.as_bytes()[1 - encapsulation_header_size..1 + 4 + databytes]
);
self.usb_write_bulk(&req.as_bytes()[1 - encapsulation_header_size..1 + 4 + databytes])?;
databytes
} else {
Expand All @@ -206,20 +219,25 @@ impl HyperdebugI2cBus {
port: self.bus_idx | (((wbuf.len() & 0x0F00) >> 4) as u8),
addr,
write_count: (wbuf.len() & 0x00FF) as u8,
read_count: (rbuf.len() & 0x007F) as u8,
read_count: (rbuf.len() & 0x007F | 0x0080) as u8,
read_count1: (rbuf.len() >> 7) as u8,
reserved: 0,
flags: if gsc_ready { 0x80 } else { 0x00 },
data: [0; USB_MAX_SIZE - 6],
};
let databytes = cmp::min(USB_MAX_SIZE - 6 - encapsulation_header_size, wbuf.len());
req.data[..databytes].clone_from_slice(&wbuf[..databytes]);
log::error!(
"I2C wrote {:?}",
&req.as_bytes()[1 - encapsulation_header_size..1 + 6 + databytes]
);
self.usb_write_bulk(&req.as_bytes()[1 - encapsulation_header_size..1 + 6 + databytes])?;
databytes
};

// Transmit any more data without further header.
while index < wbuf.len() {
let databytes = cmp::min(USB_MAX_SIZE, wbuf.len() - index);
log::error!("I2C wrote {:?}", &wbuf[index..index + databytes]);
self.usb_write_bulk(&wbuf[index..index + databytes])?;
index += databytes;
}
Expand All @@ -230,6 +248,10 @@ impl HyperdebugI2cBus {
let read_count = self.usb_read_bulk(
&mut resp.as_bytes_mut()[1 - encapsulation_header_size + bytecount..][..64],
)?;
log::error!(
"I2C read {:?}",
&resp.as_bytes_mut()[1 - encapsulation_header_size + bytecount..][..read_count]
);
ensure!(
read_count > 0,
TransportError::CommunicationError("Truncated I2C response".to_string())
Expand Down Expand Up @@ -336,6 +358,25 @@ impl Bus for HyperdebugI2cBus {
.cmd_no_output(&format!("i2c set speed {} {}", &self.bus_idx, max_speed))
}

fn set_pins(
&self,
serial_clock: Option<&Rc<dyn GpioPin>>,
serial_data: Option<&Rc<dyn GpioPin>>,
gsc_ready: Option<&Rc<dyn GpioPin>>,
) -> Result<()> {
if serial_clock.is_some() || serial_data.is_some() {
bail!(I2cError::InvalidPin);
}
if let Some(pin) = gsc_ready {
self.inner.cmd_no_output(&format!(
"i2c set ready {} {}",
&self.bus_idx,
pin.get_internal_pin_name().ok_or(I2cError::InvalidPin)?
))?;
}
Ok(())
}

fn set_default_address(&self, addr: u8) -> Result<()> {
self.default_addr.set(Some(addr));
Ok(())
Expand All @@ -347,6 +388,22 @@ impl Bus for HyperdebugI2cBus {
.ok_or(I2cError::MissingAddress)?;
while !transaction.is_empty() {
match transaction {
[Transfer::Write(wbuf), Transfer::GscReady, Transfer::Read(rbuf), ..] => {
// Hyperdebug can do I2C write followed by I2C read as a single USB
// request/reply. Take advantage of that by detecting pairs of
// Transfer::Write followed by Transfer::Read.
ensure!(
wbuf.len() <= self.max_write_size,
I2cError::InvalidDataLength(wbuf.len())
);
ensure!(
rbuf.len() <= self.max_read_size,
I2cError::InvalidDataLength(rbuf.len())
);
self.transmit_then_receive(addr, wbuf, rbuf, true)?;
// Skip three steps ahead, as three items were processed.
transaction = &mut transaction[3..];
}
[Transfer::Write(wbuf), Transfer::Read(rbuf), ..] => {
// Hyperdebug can do I2C write followed by I2C read as a single USB
// request/reply. Take advantage of that by detecting pairs of
Expand All @@ -359,7 +416,16 @@ impl Bus for HyperdebugI2cBus {
rbuf.len() <= self.max_read_size,
I2cError::InvalidDataLength(rbuf.len())
);
self.transmit_then_receive(addr, wbuf, rbuf)?;
self.transmit_then_receive(addr, wbuf, rbuf, false)?;
// Skip two steps ahead, as two items were processed.
transaction = &mut transaction[2..];
}
[Transfer::Write(wbuf), Transfer::GscReady, ..] => {
ensure!(
wbuf.len() <= self.max_write_size,
I2cError::InvalidDataLength(wbuf.len())
);
self.transmit_then_receive(addr, wbuf, &mut [], true)?;
// Skip two steps ahead, as two items were processed.
transaction = &mut transaction[2..];
}
Expand All @@ -368,15 +434,15 @@ impl Bus for HyperdebugI2cBus {
wbuf.len() <= self.max_write_size,
I2cError::InvalidDataLength(wbuf.len())
);
self.transmit_then_receive(addr, wbuf, &mut [])?;
self.transmit_then_receive(addr, wbuf, &mut [], false)?;
transaction = &mut transaction[1..];
}
[Transfer::Read(rbuf), ..] => {
ensure!(
rbuf.len() <= self.max_read_size,
I2cError::InvalidDataLength(rbuf.len())
);
self.transmit_then_receive(addr, &[], rbuf)?;
self.transmit_then_receive(addr, &[], rbuf, false)?;
transaction = &mut transaction[1..];
}
[] => (),
Expand Down
18 changes: 14 additions & 4 deletions sw/host/opentitanlib/src/transport/hyperdebug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ impl<T: Flavor> Hyperdebug<T> {
const GOOGLE_CAP_GPIO_MONITORING: u16 = 0x0004;
const GOOGLE_CAP_GPIO_BITBANGING: u16 = 0x0008;
const GOOGLE_CAP_UART_QUEUE_CLEAR: u16 = 0x0010;
const GOOGLE_CAP_TPM_POLL: u16 = 0x0020;

/// Establish connection with a particular HyperDebug.
pub fn open(
Expand Down Expand Up @@ -442,6 +443,7 @@ pub struct CachedIo {

pub struct Conn {
console_port: RefCell<TTYPort>,
first_use: Cell<bool>,
}

// The way that the HyperDebug allows callers to request optimization for a sequence of operations
Expand All @@ -467,6 +469,7 @@ impl Inner {
flock_serial(&port, port_name)?;
let conn = Rc::new(Conn {
console_port: RefCell::new(port),
first_use: Cell::new(true),
});
// Return a (strong) reference to the newly opened connection, while keeping a weak
// reference to the same in this `Inner` object. The result is that if the caller keeps
Expand Down Expand Up @@ -560,10 +563,16 @@ impl Conn {
fn execute_command(&self, cmd: &str, mut callback: impl FnMut(&str)) -> Result<()> {
let port: &mut TTYPort = &mut self.console_port.borrow_mut();

// Send Ctrl-C, followed by the command, then newline. This will discard any previous
// partial input, before executing our command.
port.write(format!("\x03{}\n", cmd).as_bytes())
.context("writing to HyperDebug console")?;
if self.first_use.get() {
// Send Ctrl-C, followed by the command, then newline. This will discard any previous
// partial input, before executing our command.
port.write(format!("\x03{}\n", cmd).as_bytes())
.context("writing to HyperDebug console")?;
self.first_use.set(false);
} else {
port.write(format!("{}\n", cmd).as_bytes())
.context("writing to HyperDebug console")?;
}

// Now process response from HyperDebug. First we expect to see the echo of the command
// we just "typed". Then zero, one or more lines of useful output, which we want to pass
Expand Down Expand Up @@ -656,6 +665,7 @@ impl<T: Flavor> Transport for Hyperdebug<T> {
&self.spi_interface,
enable_cmd,
idx,
self.get_cmsis_google_capabilities()? & Self::GOOGLE_CAP_TPM_POLL != 0,
)?);
self.cached_io_interfaces
.spis
Expand Down
Loading

0 comments on commit 9fcfe3e

Please sign in to comment.