Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[opentitantool] Advanced TPM primitives in transport #25259

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions sw/host/opentitanlib/src/app/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use crate::io::gpio::GpioPin;
use crate::io::i2c::{self, Bus, DeviceStatus, Mode};
use crate::transport::Transport;

use anyhow::Result;
use std::cell::Cell;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
Expand Down Expand Up @@ -45,8 +46,17 @@ pub struct LogicalI2cWrapper {
physical_wrapper: Rc<PhysicalI2cWrapper>,
/// Unique ID of this `LogicalI2cWrapper`.
uid: usize,
default_addr: Cell<Option<u8>>,
max_speed: Cell<Option<u32>>,
/// I2C port settings applying to this named logical I2C port.
inner: RefCell<Inner>,
}

/// I2C port settings applying to this named logical I2C port.
struct Inner {
default_addr: Option<u8>,
max_speed: Option<u32>,
serial_clock: Option<Rc<dyn GpioPin>>,
serial_data: Option<Rc<dyn GpioPin>>,
gsc_ready: Option<Rc<dyn GpioPin>>,
}

impl LogicalI2cWrapper {
Expand All @@ -59,8 +69,13 @@ impl LogicalI2cWrapper {
Ok(Self {
physical_wrapper,
uid: COUNTER.fetch_add(1, Ordering::Relaxed),
default_addr: Cell::new(conf.default_addr),
max_speed: Cell::new(conf.bits_per_sec),
inner: RefCell::new(Inner {
default_addr: conf.default_addr,
max_speed: conf.bits_per_sec,
serial_clock: None,
serial_data: None,
gsc_ready: None,
}),
})
}

Expand All @@ -70,16 +85,28 @@ impl LogicalI2cWrapper {
// settings.
return Ok(());
}
if let Some(addr) = self.default_addr.get() {
let inner = self.inner.borrow();
if let Some(addr) = inner.default_addr {
self.physical_wrapper
.underlying_target
.set_default_address(addr)?;
}
if let Some(speed) = self.max_speed.get() {
if let Some(speed) = inner.max_speed {
self.physical_wrapper
.underlying_target
.set_max_speed(speed)?;
}
match (
inner.serial_clock.as_ref(),
inner.serial_data.as_ref(),
inner.gsc_ready.as_ref(),
) {
(None, None, None) => (),
(scl, sda, rdy) => self
.physical_wrapper
.underlying_target
.set_pins(scl, sda, rdy)?,
}
self.physical_wrapper.last_used_by_uid.set(Some(self.uid));
Ok(())
}
Expand All @@ -94,12 +121,32 @@ impl Bus for LogicalI2cWrapper {
self.physical_wrapper.underlying_target.get_max_speed()
}
fn set_max_speed(&self, max_speed: u32) -> Result<()> {
self.max_speed.set(Some(max_speed));
self.inner.borrow_mut().max_speed = Some(max_speed);
Ok(())
}

fn set_pins(
&self,
serial_clock: Option<&Rc<dyn GpioPin>>,
serial_data: Option<&Rc<dyn GpioPin>>,
gsc_ready: Option<&Rc<dyn GpioPin>>,
) -> Result<()> {
log::error!("LogicalI2cWrapper::set_pins()");
let mut inner = self.inner.borrow_mut();
if serial_clock.is_some() {
inner.serial_clock = serial_clock.map(Rc::clone);
}
if serial_data.is_some() {
inner.serial_data = serial_data.map(Rc::clone);
}
if gsc_ready.is_some() {
inner.gsc_ready = gsc_ready.map(Rc::clone);
}
Ok(())
}

fn set_default_address(&self, addr: u8) -> Result<()> {
self.default_addr.set(Some(addr));
self.inner.borrow_mut().default_addr = Some(addr);
Ok(())
}

Expand Down
1 change: 1 addition & 0 deletions sw/host/opentitanlib/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ pub struct SpiConfiguration {
pub host_out_device_in: Option<String>,
pub host_in_device_out: Option<String>,
pub chip_select: Option<String>,
pub gsc_ready: Option<String>,
pub bits_per_word: Option<u32>,
pub bits_per_sec: Option<u32>,
}
Expand Down
17 changes: 14 additions & 3 deletions sw/host/opentitanlib/src/app/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct Inner {
host_out_device_in: Option<Rc<dyn GpioPin>>,
host_in_device_out: Option<Rc<dyn GpioPin>>,
chip_select: Option<Rc<dyn GpioPin>>,
gsc_ready: Option<Rc<dyn GpioPin>>,
}

impl LogicalSpiWrapper {
Expand All @@ -81,6 +82,7 @@ impl LogicalSpiWrapper {
host_out_device_in: Self::lookup_pin(transport, &conf.host_out_device_in)?,
host_in_device_out: Self::lookup_pin(transport, &conf.host_in_device_out)?,
chip_select: Self::lookup_pin(transport, &conf.chip_select)?,
gsc_ready: Self::lookup_pin(transport, &conf.gsc_ready)?,
}),
})
}
Expand Down Expand Up @@ -123,12 +125,13 @@ impl LogicalSpiWrapper {
inner.host_out_device_in.as_ref(),
inner.host_in_device_out.as_ref(),
inner.chip_select.as_ref(),
inner.gsc_ready.as_ref(),
) {
(None, None, None, None) => (),
(clock, hodi, hido, cs) => self
(None, None, None, None, None) => (),
(clock, hodi, hido, cs, rdy) => self
.physical_wrapper
.underlying_target
.set_pins(clock, hodi, hido, cs)?,
.set_pins(clock, hodi, hido, cs, rdy)?,
}
self.physical_wrapper.last_used_by_uid.set(Some(self.uid));
Ok(())
Expand Down Expand Up @@ -166,12 +169,17 @@ impl Target for LogicalSpiWrapper {
.supports_bidirectional_transfer()
}

fn supports_tpm_poll(&self) -> Result<bool> {
self.physical_wrapper.underlying_target.supports_tpm_poll()
}

fn set_pins(
&self,
serial_clock: Option<&Rc<dyn GpioPin>>,
host_out_device_in: Option<&Rc<dyn GpioPin>>,
host_in_device_out: Option<&Rc<dyn GpioPin>>,
chip_select: Option<&Rc<dyn GpioPin>>,
gsc_ready: Option<&Rc<dyn GpioPin>>,
) -> Result<()> {
let mut inner = self.inner.borrow_mut();
if serial_clock.is_some() {
Expand All @@ -186,6 +194,9 @@ impl Target for LogicalSpiWrapper {
if chip_select.is_some() {
inner.chip_select = chip_select.map(Rc::clone);
}
if gsc_ready.is_some() {
inner.gsc_ready = gsc_ready.map(Rc::clone);
}
Ok(())
}

Expand Down
18 changes: 18 additions & 0 deletions sw/host/opentitanlib/src/io/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 thiserror::Error;

use super::gpio;
use crate::app::TransportWrapper;
use crate::impl_serializable_error;
use crate::transport::TransportError;
Expand Down Expand Up @@ -63,6 +64,8 @@ pub enum I2cError {
MissingAddress,
#[error("I2C port not in device mode")]
NotInDeviceMode,
#[error("Given pin does not support requested I2C function")]
InvalidPin,
#[error("Generic error {0}")]
Generic(String),
}
Expand All @@ -72,6 +75,8 @@ impl_serializable_error!(I2cError);
pub enum Transfer<'rd, 'wr> {
Read(&'rd mut [u8]),
Write(&'wr [u8]),
// Wait for pulse on non-standard Google signal
GscReady,
}

/// Status of I2C read operations (data from device to host).
Expand Down Expand Up @@ -143,6 +148,19 @@ pub trait Bus {
/// 1_000_000.
fn set_max_speed(&self, max_speed: u32) -> Result<()>;

/// Sets which pins should be used for I2C communication. `None` value means use the same pin
/// as previously, or the implementation default if never before specified. This call is not
/// supported by most backend transports, and ones that do support it may still have
/// restrictions on which set of pins can be used in which roles.
fn set_pins(
&self,
_serial_clock: Option<&Rc<dyn gpio::GpioPin>>,
_serial_data: Option<&Rc<dyn gpio::GpioPin>>,
_gsc_ready: Option<&Rc<dyn gpio::GpioPin>>,
) -> Result<()> {
Err(I2cError::InvalidPin.into())
}

/// Sets the "default" device address, used in cases when not overriden by parameter to
/// `run_transaction()`.
fn set_default_address(&self, addr: u8) -> Result<()>;
Expand Down
16 changes: 15 additions & 1 deletion sw/host/opentitanlib/src/io/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@ impl SpiParams {
) -> Result<Rc<dyn Target>> {
let spi = transport.spi(self.bus.as_deref().unwrap_or(default_instance))?;
if let Some(ref cs) = self.chip_select {
spi.set_pins(None, None, None, Some(&transport.gpio_pin(cs.as_str())?))?;
spi.set_pins(
None,
None,
None,
Some(&transport.gpio_pin(cs.as_str())?),
None,
)?;
}
if let Some(speed) = self.speed {
spi.set_max_speed(speed)?;
Expand Down Expand Up @@ -156,6 +162,10 @@ pub enum Transfer<'rd, 'wr> {
Write(&'wr [u8]),
// Check supports_bidirectional_transfer before using this.
Both(&'wr [u8], &'rd mut [u8]),
// Repeatedly poll until device response with 0x01
TpmPoll,
// Wait for pulse on non-standard Google signal
GscReady,
}

/// A trait which represents a SPI Target.
Expand All @@ -178,6 +188,9 @@ pub trait Target {
/// Indicates whether `Transfer::Both()` is supported.
fn supports_bidirectional_transfer(&self) -> Result<bool>;

/// Indicates whether `Transfer::TpmPoll` is supported.
fn supports_tpm_poll(&self) -> Result<bool>;

/// Sets which pins should be used for SPI communication. `None` value means use the same pin
/// as previously, or the implementation default if never before specified. This call is not
/// supported by most backend transports, and ones that do support it may still have
Expand All @@ -188,6 +201,7 @@ pub trait Target {
_host_out_device_in: Option<&Rc<dyn gpio::GpioPin>>,
_host_in_device_out: Option<&Rc<dyn gpio::GpioPin>>,
_chip_select: Option<&Rc<dyn gpio::GpioPin>>,
_gsc_ready: Option<&Rc<dyn gpio::GpioPin>>,
) -> Result<()> {
Err(SpiError::InvalidPin.into())
}
Expand Down
30 changes: 30 additions & 0 deletions sw/host/opentitanlib/src/proxy/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,17 +334,23 @@ impl<'a> TransportCommandHandler<'a> {
has_support,
}))
}
SpiRequest::SupportsTpmPoll => {
let has_support = instance.supports_tpm_poll()?;
Ok(Response::Spi(SpiResponse::SupportsTpmPoll { has_support }))
}
SpiRequest::SetPins {
serial_clock,
host_out_device_in,
host_in_device_out,
chip_select,
gsc_ready,
} => {
instance.set_pins(
self.optional_pin(serial_clock)?.as_ref(),
self.optional_pin(host_out_device_in)?.as_ref(),
self.optional_pin(host_in_device_out)?.as_ref(),
self.optional_pin(chip_select)?.as_ref(),
self.optional_pin(gsc_ready)?.as_ref(),
)?;
Ok(Response::Spi(SpiResponse::SetPins))
}
Expand Down Expand Up @@ -378,6 +384,8 @@ impl<'a> TransportCommandHandler<'a> {
SpiTransferRequest::Both { data } => SpiTransferResponse::Both {
data: vec![0; data.len()],
},
SpiTransferRequest::TpmPoll => SpiTransferResponse::TpmPoll,
SpiTransferRequest::GscReady => SpiTransferResponse::GscReady,
})
.collect();
// Now carefully craft a proper parameter to the
Expand All @@ -400,6 +408,12 @@ impl<'a> TransportCommandHandler<'a> {
SpiTransferRequest::Both { data: wdata },
SpiTransferResponse::Both { data },
) => spi::Transfer::Both(wdata, data),
(SpiTransferRequest::TpmPoll, SpiTransferResponse::TpmPoll) => {
spi::Transfer::TpmPoll
}
(SpiTransferRequest::GscReady, SpiTransferResponse::GscReady) => {
spi::Transfer::GscReady
}
_ => {
// This can only happen if the logic in this method is
// flawed. (Never due to network input.)
Expand Down Expand Up @@ -452,6 +466,18 @@ impl<'a> TransportCommandHandler<'a> {
instance.set_max_speed(*value)?;
Ok(Response::I2c(I2cResponse::SetMaxSpeed))
}
I2cRequest::SetPins {
serial_clock,
serial_data,
gsc_ready,
} => {
instance.set_pins(
self.optional_pin(serial_clock)?.as_ref(),
self.optional_pin(serial_data)?.as_ref(),
self.optional_pin(gsc_ready)?.as_ref(),
)?;
Ok(Response::I2c(I2cResponse::SetPins))
}
I2cRequest::RunTransaction {
address,
transaction: reqs,
Expand All @@ -464,6 +490,7 @@ impl<'a> TransportCommandHandler<'a> {
data: vec![0; *len as usize],
},
I2cTransferRequest::Write { .. } => I2cTransferResponse::Write,
I2cTransferRequest::GscReady => I2cTransferResponse::GscReady,
})
.collect();
// Now carefully craft a proper parameter to the
Expand All @@ -482,6 +509,9 @@ impl<'a> TransportCommandHandler<'a> {
I2cTransferRequest::Write { data },
I2cTransferResponse::Write,
) => i2c::Transfer::Write(data),
(I2cTransferRequest::GscReady, I2cTransferResponse::GscReady) => {
i2c::Transfer::GscReady
}
_ => {
// This can only happen if the logic in this method is
// flawed. (Never due to network input.)
Expand Down
Loading
Loading