Skip to content

Commit

Permalink
WIP: TPM optimizations
Browse files Browse the repository at this point in the history
Change-Id: I16c56d367478c1394825b1be56a3622452af207a
  • Loading branch information
jesultra committed Nov 19, 2024
1 parent 016e792 commit fe3d468
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 98 deletions.
147 changes: 71 additions & 76 deletions sw/host/opentitanlib/src/tpm/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::io::i2c;
use crate::io::spi;
use crate::tpm::access::TpmAccess;
use crate::tpm::status::TpmStatus;
use crate::transport::TransportError;

/// Tpm registers, can be specified in command line arguments.
#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -213,20 +214,20 @@ fn wait_for_gsc_ready(gsc_ready_pin: &Option<GpioPinAndMonitoring>) -> Result<()
/// Implementation of the low level interface via standard SPI protocol.
pub struct SpiDriver {
spi: Rc<dyn spi::Target>,
gsc_ready_pin: Option<(Rc<dyn gpio::GpioPin>, Rc<dyn gpio::GpioMonitoring>)>,
use_gsc_ready: bool,
}

impl SpiDriver {
pub fn new(
spi: Rc<dyn spi::Target>,
gsc_ready_pin: Option<(Rc<dyn gpio::GpioPin>, Rc<dyn gpio::GpioMonitoring>)>,
use_gsc_ready: bool,
) -> Result<Self> {
if let Some((gsc_ready_pin, monitoring)) = &gsc_ready_pin {
/*if let Some((gsc_ready_pin, monitoring)) = &gsc_ready_pin {
// Set up monitoring of edges on the GSC ready pin. This will be more efficient than
// starting/stopping the monitoring on each TPM operation.
monitoring.monitoring_start(&[gsc_ready_pin.borrow()])?;
}
Ok(Self { spi, gsc_ready_pin })
}*/
Ok(Self { spi, use_gsc_ready })
}

/// Numerical TPM register address as used in SPI protocol.
Expand Down Expand Up @@ -311,76 +312,76 @@ const TIMEOUT: Duration = Duration::from_millis(500);

impl Driver for SpiDriver {
fn read_register(&self, register: Register, data: &mut [u8]) -> Result<()> {
if !self.spi.supports_bidirectional_transfer()? {
// SPI transport does not support bidirectional transfer. Assume that the TPM will
// send 0x01 on the byte immediately following the fourth and final request byte.
let req = self.compose_header(register, data.len(), true);
let mut buffer = vec![0u8; data.len() + 1];
if !self.spi.supports_tpm_poll()? {
return self.do_read_register(register, data);
}
let req = self.compose_header(register, data.len(), true /* is_read */);
let res = if self.use_gsc_ready {
self.spi.run_transaction(&mut [
spi::Transfer::Write(&req),
spi::Transfer::Read(&mut buffer),
])?;
ensure!(buffer[0] & 1 != 0, "TPM did not respond as expected",);
data.clone_from_slice(&buffer[1..]);
return Ok(());
}
let result = self.do_read_register(register, data);
if result.is_ok() {
wait_for_gsc_ready(&self.gsc_ready_pin)?;
spi::Transfer::GscReady,
spi::Transfer::TpmPoll,
spi::Transfer::Read(data),
])
} else {
self.spi.run_transaction(&mut [
spi::Transfer::Write(&req),
spi::Transfer::TpmPoll,
spi::Transfer::Read(data),
])
};
if res.is_err() {
bail!(TpmError::Timeout);
}
result
Ok(())
}

fn write_register(&self, register: Register, data: &[u8]) -> Result<()> {
if !self.spi.supports_bidirectional_transfer()? {
/*
* SPI transport does not support bidirectional transfer. Assume that the TPM will
* send 0x01 on the byte immediately following the fourth and final request byte.
*/
let req = self.compose_header(register, data.len(), true);
let mut buffer = [0u8; 1];
if !self.spi.supports_tpm_poll()? {
return self.do_write_register(register, data);
}
let req = self.compose_header(register, data.len(), false /* is_read */);
match if self.use_gsc_ready {
self.spi.run_transaction(&mut [
spi::Transfer::Write(&req),
spi::Transfer::Read(&mut buffer),
spi::Transfer::TpmPoll,
spi::Transfer::Write(data),
])?;
ensure!(buffer[0] & 1 != 0, "TPM did not respond as expected",);
return Ok(());
}
let result = self.do_write_register(register, data);
if result.is_ok() {
wait_for_gsc_ready(&self.gsc_ready_pin)?;
}
result
}
}

impl Drop for SpiDriver {
fn drop(&mut self) {
if let Some((gsc_ready_pin, monitoring)) = &self.gsc_ready_pin {
// Stop monitoring of the gsc_ready pin, by reading one final time.
let _ = monitoring.monitoring_read(&[gsc_ready_pin.borrow()], false);
spi::Transfer::GscReady,
])
} else {
self.spi.run_transaction(&mut [
spi::Transfer::Write(&req),
spi::Transfer::TpmPoll,
spi::Transfer::Write(data),
])
} {
Ok(()) => (),
Err(error) => {
if let Some(fisk) = error.downcast_ref::<TransportError>() {
if let TransportError::CommunicationError(_) = fisk {
// Check that the actual error is "timeout"
bail!(TpmError::Timeout);
}
}
bail!(error);
}
}
Ok(())
}
}

/// Implementation of the low level interface via Google I2C protocol.
pub struct I2cDriver {
i2c: Rc<dyn i2c::Bus>,
gsc_ready_pin: Option<(Rc<dyn gpio::GpioPin>, Rc<dyn gpio::GpioMonitoring>)>,
use_gsc_ready: bool,
}

impl I2cDriver {
pub fn new(
i2c: Rc<dyn i2c::Bus>,
gsc_ready_pin: Option<(Rc<dyn gpio::GpioPin>, Rc<dyn gpio::GpioMonitoring>)>,
use_gsc_ready: bool,
) -> Result<Self> {
if let Some((gsc_ready_pin, monitoring)) = &gsc_ready_pin {
// Set up monitoring of edges on the GSC ready pin. This will be more efficient than
// starting/stopping the monitoring on each TPM operation.
monitoring.monitoring_start(&[gsc_ready_pin.borrow()])?;
}
Ok(Self { i2c, gsc_ready_pin })
Ok(Self { i2c, use_gsc_ready })
}

/// Numerical TPM register address as used in Google I2C protocol.
Expand All @@ -395,7 +396,7 @@ impl I2cDriver {
}

fn try_read_register(&self, register: Register, data: &mut [u8]) -> Result<()> {
if self.gsc_ready_pin.is_none() {
if !self.use_gsc_ready {
// Do two I2C transfers in one call, for lowest latency.
self.i2c.run_transaction(
None, /* default addr */
Expand All @@ -405,16 +406,13 @@ impl I2cDriver {
],
)
} else {
// Since we need to check for the GSC ready signal in between, we have to do one I2C
// transfer at a time, and tolerate the latency of multiple roundtrip.
self.i2c.run_transaction(
None, /* default addr */
&mut [i2c::Transfer::Write(&[Self::addr(register).unwrap()])],
)?;
wait_for_gsc_ready(&self.gsc_ready_pin)?;
self.i2c.run_transaction(
None, /* default addr */
&mut [i2c::Transfer::Read(data)],
&mut [
i2c::Transfer::Write(&[Self::addr(register).unwrap()]),
i2c::Transfer::GscReady,
i2c::Transfer::Read(data),
],
)
}
}
Expand Down Expand Up @@ -456,20 +454,17 @@ impl Driver for I2cDriver {
fn write_register(&self, register: Register, data: &[u8]) -> Result<()> {
let mut buffer = vec![Self::addr(register).unwrap()];
buffer.extend_from_slice(data);
self.i2c.run_transaction(
None, /* default addr */
&mut [i2c::Transfer::Write(&buffer)],
)?;
wait_for_gsc_ready(&self.gsc_ready_pin)?;
Ok(())
}
}

impl Drop for I2cDriver {
fn drop(&mut self) {
if let Some((gsc_ready_pin, monitoring)) = &self.gsc_ready_pin {
// Stop monitoring of the gsc_ready pin, by reading one final time.
let _ = monitoring.monitoring_read(&[gsc_ready_pin.borrow()], false);
if !self.use_gsc_ready {
self.i2c.run_transaction(
None, /* default addr */
&mut [i2c::Transfer::Write(&buffer)],
)?;
} else {
self.i2c.run_transaction(
None, /* default addr */
&mut [i2c::Transfer::Write(&buffer), i2c::Transfer::GscReady],
)?;
}
Ok(())
}
}
12 changes: 6 additions & 6 deletions sw/host/opentitantool/src/command/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,13 @@ impl CommandDispatch for I2cTpm {
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
let context = context.downcast_ref::<I2cCommand>().unwrap();
let ready_pin = match &self.gsc_ready {
Some(pin) => Some((transport.gpio_pin(pin)?, transport.gpio_monitoring()?)),
None => None,
};
let i2c = context.params.create(transport, "TPM")?;
if let Some(pin) = &self.gsc_ready {
i2c.set_pins(None, None, Some(&transport.gpio_pin(pin)?))?;
}
let tpm_driver: Box<dyn tpm::Driver> = Box::new(tpm::I2cDriver::new(
context.params.create(transport, "TPM")?,
ready_pin,
i2c,
self.gsc_ready.is_some(),
)?);
self.command.run(&tpm_driver, transport)
}
Expand Down
12 changes: 6 additions & 6 deletions sw/host/opentitantool/src/command/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,13 @@ impl CommandDispatch for SpiTpm {
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
let context = context.downcast_ref::<SpiCommand>().unwrap();
let ready_pin = match &self.gsc_ready {
Some(pin) => Some((transport.gpio_pin(pin)?, transport.gpio_monitoring()?)),
None => None,
};
let spi = context.params.create(transport, "TPM")?;
if let Some(pin) = &self.gsc_ready {
spi.set_pins(None, None, None, None, Some(&transport.gpio_pin(pin)?))?;
}
let tpm_driver: Box<dyn tpm::Driver> = Box::new(tpm::SpiDriver::new(
context.params.create(transport, "TPM")?,
ready_pin,
spi,
self.gsc_ready.is_some(),
)?);
self.command.run(&tpm_driver, transport)
}
Expand Down
18 changes: 8 additions & 10 deletions sw/host/tpm2_test_server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,17 @@ pub fn main() -> anyhow::Result<()> {
let bus: Box<dyn Driver> = match options.bus {
TpmBus::Spi { params, gsc_ready } => {
let spi = params.create(&transport, "TPM")?;
let ready_pin = match &gsc_ready {
Some(pin) => Some((transport.gpio_pin(pin)?, transport.gpio_monitoring()?)),
None => None,
};
Box::new(SpiDriver::new(spi, ready_pin)?)
if let Some(pin) = &gsc_ready {
spi.set_pins(None, None, None, None, Some(&transport.gpio_pin(pin)?))?;
}
Box::new(SpiDriver::new(spi, gsc_ready.is_some())?)
}
TpmBus::I2C { params, gsc_ready } => {
let i2c = params.create(&transport, "TPM")?;
let ready_pin = match &gsc_ready {
Some(pin) => Some((transport.gpio_pin(pin)?, transport.gpio_monitoring()?)),
None => None,
};
Box::new(I2cDriver::new(i2c, ready_pin)?)
if let Some(pin) = &gsc_ready {
i2c.set_pins(None, None, Some(&transport.gpio_pin(pin)?))?;
}
Box::new(I2cDriver::new(i2c, gsc_ready.is_some())?)
}
};
bus.init()?;
Expand Down

0 comments on commit fe3d468

Please sign in to comment.