| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| use anyhow::{anyhow, Result}; | |
| use mio::net::TcpStream; | |
| use num_enum::TryFromPrimitive; | |
| use opentitanlib::tpm::Driver; | |
| use std::io::{Read, Write}; | |
| use std::net::Shutdown; | |
| pub(crate) const CMD_SIZE: usize = std::mem::size_of::<TcpTpmCommands>(); | |
| const SERVER_VERSION: u32 = 1; | |
| #[derive(TryFromPrimitive, Debug)] | |
| #[repr(u32)] | |
| pub(crate) enum TcpTpmCommands { | |
| SignalPowerOn = 1, | |
| SignalPowerOff = 2, | |
| SignalPPOn = 3, | |
| SignalPPOff = 4, | |
| SignalHashStart = 5, | |
| SignalHashData = 6, | |
| SignalHashEnd = 7, | |
| SendCommand = 8, | |
| SignalCancelOn = 9, | |
| SignalCancelOff = 10, | |
| SignalNvOn = 11, | |
| SignalNvOff = 12, | |
| SignalKeyCacheOn = 13, | |
| SignalKeyCacheOff = 14, | |
| RemoteHandshake = 15, | |
| //SetAlternativeResult = 16, // Not used since 1.38h | |
| SessionEnd = 20, | |
| Stop = 21, | |
| ActGetSignaled = 26, | |
| TestFailureMode = 30, | |
| } | |
| /// Platform hierarchy is enabled, and hardware platform functionality (such | |
| /// as SignalHashStart/Data/End) is available. | |
| const PLATFORM_AVAILABLE: u32 = 0x01; | |
| /// The device is TPM Resource Manager (TRM), rather than a raw TPM. | |
| /// This means context management commands are unavailable, and the handle values | |
| /// returned to the client are virtualized. | |
| const _USES_TBS: u32 = 0x02; | |
| /// The TRM is in raw mode (i.e. no actual resourse virtualization is performed). | |
| const _IN_RAW_MODE: u32 = 0x04; | |
| /// Physical presence signals (SignalPPOn/Off) are supported. | |
| const _SUPPORTS_PP: u32 = 0x08; | |
| /// Valid only with PlatformAvailable set. | |
| /// System and TPM power control signals (SignalPowerOn/Off) are not supported. | |
| const NO_POWER_CTL: u32 = 0x10; | |
| /// Valid only with tpmPlatformAvailable set. | |
| /// TPM locality cannot be changed. | |
| const NO_LOCALITY_CTL: u32 = 0x20; | |
| /// Valid only with tpmPlatformAvailable set. | |
| /// NV control signals (SignalNvOn/Off) are not supported. | |
| const NO_NV_CTL: u32 = 0x40; | |
| /// Serve the command port for the TPM, forwarding commands to the bus specified in `opts`. | |
| pub(crate) fn serve_command(stream: &mut TcpStream, tpm: &dyn Driver) -> Result<bool> { | |
| let mut data = [0u8; CMD_SIZE]; | |
| let len = stream.read(&mut data)?; | |
| if len == 0 { | |
| stream.shutdown(Shutdown::Both)?; | |
| return Ok(true); | |
| } | |
| let cmd_u32 = u32::from_be_bytes(data); | |
| let cmd = TcpTpmCommands::try_from_primitive(cmd_u32)?; | |
| if matches!(cmd, TcpTpmCommands::SessionEnd) { | |
| stream.shutdown(Shutdown::Both)?; | |
| Ok(true) | |
| } else { | |
| handle_cmd(cmd, stream, tpm).map(|_| false) | |
| } | |
| } | |
| /// Handle the requested command and send the reply on `stream`. If this it a TPM command, send it | |
| /// to `tpm`. | |
| fn handle_cmd(cmd: TcpTpmCommands, stream: &mut TcpStream, tpm: &dyn Driver) -> Result<()> { | |
| const CFG: u32 = PLATFORM_AVAILABLE | NO_POWER_CTL | NO_LOCALITY_CTL | NO_NV_CTL; | |
| log::info!("CMD {:?}", cmd); | |
| match cmd { | |
| TcpTpmCommands::RemoteHandshake => { | |
| let mut ver = [0u8; 4]; | |
| stream.read_exact(&mut ver)?; | |
| log::debug!("Client ver {}.", u32::from_be_bytes(ver)); | |
| stream.write_all(&SERVER_VERSION.to_be_bytes())?; | |
| // TODO Make TpmEndpointInfo a bitfield and send | |
| stream.write_all(&CFG.to_be_bytes())?; | |
| stream.write_all(&[0u8; 4])?; | |
| Ok(()) | |
| } | |
| TcpTpmCommands::SendCommand => handle_send(stream, tpm), | |
| _ => { | |
| let _ = stream.write(&[0u8; 4])?; | |
| Ok(()) | |
| } | |
| } | |
| } | |
| /// Forward a TPM command to the device and send the reponse back to `stream`. | |
| fn handle_send(stream: &mut TcpStream, tpm: &dyn Driver) -> Result<()> { | |
| let mut locality = [0u8; 1]; | |
| let mut sz = [0u8; 4]; | |
| stream.read_exact(&mut locality)?; | |
| stream.read_exact(&mut sz)?; | |
| let sz = u32::from_be_bytes(sz) as usize; | |
| let mut cmd: Vec<u8> = vec![0; sz]; | |
| let cmd_sz = stream.read(&mut cmd)?; | |
| if cmd_sz != sz { | |
| return Err(anyhow!("Bad command size.")); | |
| } | |
| log::debug!("TPM cmd {:02x?}", cmd); | |
| if let Ok(res) = tpm.execute_command(&cmd) { | |
| stream.write_all(&(res.len() as u32).to_be_bytes())?; | |
| stream.write_all(&res)?; | |
| stream.write_all(&[0u8; 4])?; | |
| } else { | |
| log::error!("Command fail."); | |
| stream.write_all(&[0u8; 4])?; | |
| stream.write_all(&[0u8; 4])?; | |
| } | |
| Ok(()) | |
| } |