diff --git a/Cargo.lock b/Cargo.lock index 51f8ac15..b6476139 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1629,6 +1629,7 @@ dependencies = [ "spdm-emu", "spdmlib", "spin 0.9.8", + "tdisp", "tokio", ] @@ -1646,6 +1647,7 @@ dependencies = [ "spdm-emu", "spdmlib", "spin 0.9.8", + "tdisp", "tokio", "zeroize", ] diff --git a/tdisp/src/pci_tdisp_responder/pci_tdisp_rsp_device_interface_report.rs b/tdisp/src/pci_tdisp_responder/pci_tdisp_rsp_device_interface_report.rs index 37c1cfb1..7edd550c 100644 --- a/tdisp/src/pci_tdisp_responder/pci_tdisp_rsp_device_interface_report.rs +++ b/tdisp/src/pci_tdisp_responder/pci_tdisp_rsp_device_interface_report.rs @@ -30,7 +30,7 @@ static PCI_TDISP_DEVICE_INTERFACE_REPORT_INSTANCE: OnceCell SpdmResult { - if vendor_defined_req_payload_struct.req_length < 3 { + if vendor_defined_req_payload_struct.req_length < 3 + || vendor_defined_req_payload_struct.vendor_defined_req_payload[0] != TDISP_PROTOCOL_ID + { return Err(SPDM_STATUS_INVALID_MSG_FIELD); } @@ -69,9 +71,6 @@ pub fn pci_tdisp_rsp_dispatcher( TdispRequestResponseCode::UNBIND_P2P_STREAM_REQUEST => { pci_tdisp_rsp_unbind_p2p_stream(vendor_context, vendor_defined_req_payload_struct) } - TdispRequestResponseCode::VDM_REQUEST => { - pci_tdisp_rsp_vdm_response(vendor_context, vendor_defined_req_payload_struct) - } _ => { let mut vendor_defined_rsp_payload_struct = VendorDefinedRspPayloadStruct { rsp_length: 0, @@ -90,6 +89,6 @@ pub fn pci_tdisp_rsp_dispatcher( } } } else { - Err(SPDM_STATUS_INVALID_MSG_FIELD) + pci_tdisp_rsp_vdm_response(vendor_context, vendor_defined_req_payload_struct) } } diff --git a/test/spdm-requester-emu/Cargo.toml b/test/spdm-requester-emu/Cargo.toml index 9da878ad..c3677ced 100644 --- a/test/spdm-requester-emu/Cargo.toml +++ b/test/spdm-requester-emu/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" spdm-emu = { path = "../spdm-emu", default-features = false } spdmlib = { path = "../../spdmlib", default-features = false } idekm = { path = "../../idekm", default-features = false } +tdisp = { path = "../../tdisp", default-features = false } codec = { path = "../../codec" } mctp_transport = { path = "../../mctp_transport" } pcidoe_transport = { path = "../../pcidoe_transport" } diff --git a/test/spdm-requester-emu/src/main.rs b/test/spdm-requester-emu/src/main.rs index 040f9ec2..b29a4745 100644 --- a/test/spdm-requester-emu/src/main.rs +++ b/test/spdm-requester-emu/src/main.rs @@ -4,6 +4,7 @@ #![forbid(unsafe_code)] +use codec::Codec; use common::SpdmDeviceIo; use common::SpdmTransportEncap; @@ -39,6 +40,26 @@ use pcidoe_transport::PciDoeTransportEncap; use spdm_emu::socket_io_transport::SocketIoTransport; use spdm_emu::spdm_emu::*; use std::net::TcpStream; +use tdisp::pci_tdisp::FunctionId; +use tdisp::pci_tdisp::InterfaceId; +use tdisp::pci_tdisp::InterfaceInfo; +use tdisp::pci_tdisp::LockInterfaceFlag; +use tdisp::pci_tdisp::TdiState; +use tdisp::pci_tdisp::TdispMmioRange; +use tdisp::pci_tdisp::MAX_DEVICE_REPORT_BUFFER; +use tdisp::pci_tdisp::START_INTERFACE_NONCE_LEN; +use tdisp::pci_tdisp::TDISP_PROTOCOL_ID; +use tdisp::pci_tdisp_requester::pci_tdisp_req_bind_p2p_stream_request; +use tdisp::pci_tdisp_requester::pci_tdisp_req_get_device_interface_report; +use tdisp::pci_tdisp_requester::pci_tdisp_req_get_device_interface_state; +use tdisp::pci_tdisp_requester::pci_tdisp_req_get_tdisp_capabilities; +use tdisp::pci_tdisp_requester::pci_tdisp_req_get_tdisp_version; +use tdisp::pci_tdisp_requester::pci_tdisp_req_lock_interface_request; +use tdisp::pci_tdisp_requester::pci_tdisp_req_set_mmio_attribute_request; +use tdisp::pci_tdisp_requester::pci_tdisp_req_start_interface_request; +use tdisp::pci_tdisp_requester::pci_tdisp_req_stop_interface_request; +use tdisp::pci_tdisp_requester::pci_tdisp_req_unbind_p2p_stream_request; +use tdisp::pci_tdisp_requester::pci_tdisp_req_vdm_request; use tokio::runtime::Runtime; @@ -402,7 +423,7 @@ async fn test_spdm( } } -async fn test_idekm( +async fn test_idekm_tdisp( socket_io_transport: Arc>, transport_encap: Arc>, ) { @@ -1080,6 +1101,238 @@ async fn test_idekm( .unwrap(); println!("Successful KEY_SET_STOP at Key Set 0 | TX | CPL!"); + // tdisp test + let interface_id = InterfaceId { + function_id: FunctionId { + requester_id: 0x1234, + requester_segment: 0, + requester_segment_valid: false, + }, + }; + + let mut negotiated_version = None; + + pci_tdisp_req_get_tdisp_version( + &mut context, + session_id, + interface_id, + &mut negotiated_version, + ) + .await + .unwrap(); + println!("Successful Get Tdisp Version!"); + + assert!(negotiated_version.is_some()); + assert_eq!( + negotiated_version.unwrap(), + tdisp::pci_tdisp::TdispVersion { + major_version: 1, + minor_version: 0 + } + ); + let negotiated_version = negotiated_version.unwrap(); + + let tsm_caps = 0; + let mut dsm_caps = 0u32; + let mut lock_interface_flags_supported = LockInterfaceFlag::empty(); + let mut dev_addr_width = 0u8; + let mut num_req_this = 0u8; + let mut num_req_all = 0u8; + let mut req_msgs_supported = [0u8; 16]; + pci_tdisp_req_get_tdisp_capabilities( + &mut context, + session_id, + tsm_caps, + negotiated_version, + interface_id, + &mut dsm_caps, + &mut lock_interface_flags_supported, + &mut dev_addr_width, + &mut num_req_this, + &mut num_req_all, + &mut req_msgs_supported, + ) + .await + .unwrap(); + println!("Successful Get Tdisp Capabilities!"); + + let mut tdi_state = TdiState::ERROR; + pci_tdisp_req_get_device_interface_state( + &mut context, + session_id, + negotiated_version, + interface_id, + &mut tdi_state, + ) + .await + .unwrap(); + assert_eq!(tdi_state, TdiState::CONFIG_UNLOCKED); + println!("Successful Get Tdisp State: {:X?}!", tdi_state); + + let flags = LockInterfaceFlag::NO_FW_UPDATE; + let default_stream_id = 0; + let mmio_reporting_offset = 0xFFFFFF00; + let bind_p2p_address_mask = 0; + let mut start_interface_nonce = [0u8; START_INTERFACE_NONCE_LEN]; + let mut tdisp_error_code = None; + pci_tdisp_req_lock_interface_request( + &mut context, + session_id, + negotiated_version, + interface_id, + flags, + default_stream_id, + mmio_reporting_offset, + bind_p2p_address_mask, + &mut start_interface_nonce, + &mut tdisp_error_code, + ) + .await + .unwrap(); + assert!(tdisp_error_code.is_none()); + println!( + "Successful Lock Interface, start_interface_nonce: {:X?}!", + start_interface_nonce + ); + + pci_tdisp_req_get_device_interface_state( + &mut context, + session_id, + negotiated_version, + interface_id, + &mut tdi_state, + ) + .await + .unwrap(); + assert_eq!(tdi_state, TdiState::CONFIG_LOCKED); + println!("Successful Get Tdisp State: {:X?}!", tdi_state); + + let mut report = [0u8; MAX_DEVICE_REPORT_BUFFER]; + let mut report_size = 0usize; + pci_tdisp_req_get_device_interface_report( + &mut context, + session_id, + negotiated_version, + interface_id, + &mut report, + &mut report_size, + &mut tdisp_error_code, + ) + .await + .unwrap(); + assert!(tdisp_error_code.is_none()); + let tdi_report = TdiReportStructure::read_bytes(&report).unwrap(); + println!( + "Successful Get Interface Report, tdi_report: {:X?}!", + tdi_report + ); + + pci_tdisp_req_start_interface_request( + &mut context, + session_id, + negotiated_version, + interface_id, + &start_interface_nonce, + &mut tdisp_error_code, + ) + .await + .unwrap(); + assert!(tdisp_error_code.is_none()); + println!("Successful Start Interface!"); + + let p2p_stream_id = 1u8; + pci_tdisp_req_bind_p2p_stream_request( + &mut context, + session_id, + negotiated_version, + interface_id, + p2p_stream_id, + &mut tdisp_error_code, + ) + .await + .unwrap(); + assert!(tdisp_error_code.is_none()); + println!("Successful Bind P2P stream!"); + + pci_tdisp_req_set_mmio_attribute_request( + &mut context, + session_id, + negotiated_version, + interface_id, + tdi_report.mmio_range[0], + &mut tdisp_error_code, + ) + .await + .unwrap(); + assert!(tdisp_error_code.is_none()); + println!("Successful Set MMIO attribute!"); + + pci_tdisp_req_unbind_p2p_stream_request( + &mut context, + session_id, + negotiated_version, + interface_id, + p2p_stream_id, + &mut tdisp_error_code, + ) + .await + .unwrap(); + assert!(tdisp_error_code.is_none()); + println!("Successful UnBind P2P stream!"); + + pci_tdisp_req_get_device_interface_state( + &mut context, + session_id, + negotiated_version, + interface_id, + &mut tdi_state, + ) + .await + .unwrap(); + assert_eq!(tdi_state, TdiState::RUN); + println!("Successful Get Tdisp State: {:X?}!", tdi_state); + + pci_tdisp_req_stop_interface_request( + &mut context, + session_id, + negotiated_version, + interface_id, + ) + .await + .unwrap(); + println!("Successful Stop Interface!"); + + pci_tdisp_req_get_device_interface_state( + &mut context, + session_id, + negotiated_version, + interface_id, + &mut tdi_state, + ) + .await + .unwrap(); + assert_eq!(tdi_state, TdiState::CONFIG_UNLOCKED); + println!("Successful Get Tdisp State: {:X?}!", tdi_state); + + let mut vendor_defined_req_payload_struct = VendorDefinedReqPayloadStruct { + req_length: 18, + vendor_defined_req_payload: [0u8; MAX_SPDM_VENDOR_DEFINED_PAYLOAD_SIZE], + }; + vendor_defined_req_payload_struct.vendor_defined_req_payload[0] = TDISP_PROTOCOL_ID; + vendor_defined_req_payload_struct.vendor_defined_req_payload[1..18] + .copy_from_slice(b"tdisp vdm request"); + if let Ok(vendor_defined_rsp_payload_struct) = + pci_tdisp_req_vdm_request(&mut context, session_id, vendor_defined_req_payload_struct).await + { + println!( + "Successful Get VDM response: {:X?}!", + &vendor_defined_rsp_payload_struct.vendor_defined_rsp_payload + [..vendor_defined_rsp_payload_struct.rsp_length as usize] + ); + } else { + panic!("failed to get vdm response!"); + } + // end spdm session context.end_session(session_id).await.unwrap(); } @@ -1153,7 +1406,7 @@ fn emu_main() { transport_encap.clone(), )); - rt.block_on(test_idekm( + rt.block_on(test_idekm_tdisp( socket_io_transport.clone(), transport_encap.clone(), )); @@ -1171,3 +1424,98 @@ fn main() { .join() .unwrap(); } + +pub const MMIO_RANGE_COUNT: usize = 4; +pub const DEVICE_SPECIFIC_INFO: &[u8; 9] = b"tdisp emu"; +pub const DEVICE_SPECIFIC_INFO_LEN: usize = DEVICE_SPECIFIC_INFO.len(); + +#[derive(Debug, Copy, Clone)] +pub struct TdiReportStructure { + pub interface_info: InterfaceInfo, + pub msi_x_message_control: u16, + pub lnr_control: u16, + pub tph_control: u32, + pub mmio_range_count: u32, + pub mmio_range: [TdispMmioRange; MMIO_RANGE_COUNT], + pub device_specific_info_len: u32, + pub device_specific_info: [u8; DEVICE_SPECIFIC_INFO_LEN], +} + +impl Default for TdiReportStructure { + fn default() -> Self { + Self { + interface_info: InterfaceInfo::default(), + msi_x_message_control: 0u16, + lnr_control: 0u16, + tph_control: 0u32, + mmio_range_count: 0u32, + mmio_range: [TdispMmioRange::default(); MMIO_RANGE_COUNT], + device_specific_info_len: 0u32, + device_specific_info: [0u8; DEVICE_SPECIFIC_INFO_LEN], + } + } +} + +impl Codec for TdiReportStructure { + fn encode(&self, bytes: &mut codec::Writer) -> Result { + let mut cnt = 0; + + cnt += self.interface_info.encode(bytes)?; + cnt += 0u16.encode(bytes)?; + cnt += self.msi_x_message_control.encode(bytes)?; + cnt += self.lnr_control.encode(bytes)?; + cnt += self.tph_control.encode(bytes)?; + cnt += self.mmio_range_count.encode(bytes)?; + for mr in self.mmio_range.iter().take(self.mmio_range_count as usize) { + cnt += mr.encode(bytes)?; + } + cnt += self.device_specific_info_len.encode(bytes)?; + for dsi in self + .device_specific_info + .iter() + .take(self.device_specific_info_len as usize) + { + cnt += dsi.encode(bytes)?; + } + + Ok(cnt) + } + + fn read(r: &mut codec::Reader) -> Option { + let interface_info = InterfaceInfo::read(r)?; + u16::read(r)?; + let msi_x_message_control = u16::read(r)?; + let lnr_control = u16::read(r)?; + let tph_control = u32::read(r)?; + let mmio_range_count = u32::read(r)?; + if mmio_range_count as usize > MMIO_RANGE_COUNT { + return None; + } + let mut mmio_range = [TdispMmioRange::default(); MMIO_RANGE_COUNT]; + for mr in mmio_range.iter_mut().take(mmio_range_count as usize) { + *mr = TdispMmioRange::read(r)?; + } + let device_specific_info_len = u32::read(r)?; + if device_specific_info_len as usize > DEVICE_SPECIFIC_INFO_LEN { + return None; + } + let mut device_specific_info = [0u8; DEVICE_SPECIFIC_INFO_LEN]; + for dsi in device_specific_info + .iter_mut() + .take(device_specific_info_len as usize) + { + *dsi = u8::read(r)?; + } + + Some(Self { + interface_info, + msi_x_message_control, + lnr_control, + tph_control, + mmio_range_count, + mmio_range, + device_specific_info_len, + device_specific_info, + }) + } +} diff --git a/test/spdm-responder-emu/Cargo.toml b/test/spdm-responder-emu/Cargo.toml index 7aeb44dd..5fe6ac48 100644 --- a/test/spdm-responder-emu/Cargo.toml +++ b/test/spdm-responder-emu/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" spdm-emu = { path = "../spdm-emu", default-features = false } spdmlib = { path = "../../spdmlib", default-features = false } idekm = { path = "../../idekm", default-features = false } +tdisp = { path = "../../tdisp", default-features = false } codec = { path = "../../codec" } mctp_transport = { path = "../../mctp_transport" } pcidoe_transport = { path = "../../pcidoe_transport" } diff --git a/test/spdm-responder-emu/src/main.rs b/test/spdm-responder-emu/src/main.rs index 8b8e7d2c..1d617092 100644 --- a/test/spdm-responder-emu/src/main.rs +++ b/test/spdm-responder-emu/src/main.rs @@ -2,17 +2,28 @@ // // SPDX-License-Identifier: Apache-2.0 or MIT -#![forbid(unsafe_code)] - mod spdm_device_example; +use idekm::pci_ide_km_responder::pci_ide_km_rsp_dispatcher; +use idekm::pci_idekm::IDE_PROTOCOL_ID; use spdm_device_example::init_device_instance; -use idekm::pci_ide_km_responder::PCI_IDE_KM_INSTANCE; +mod spdm_device_tdisp_example; +use spdm_device_tdisp_example::init_device_tdisp_instance; + use log::LevelFilter; use simple_logger::SimpleLogger; use spdm_emu::watchdog_impl_sample::init_watchdog; use spdmlib::common::{SecuredMessageVersion, SpdmOpaqueSupport}; use spdmlib::config::{MAX_ROOT_CERT_SUPPORT, RECEIVER_BUFFER_SIZE}; +use spdmlib::error::{SpdmResult, SPDM_STATUS_INVALID_MSG_FIELD}; +use spdmlib::message::{ + VendorDefinedReqPayloadStruct, VendorDefinedRspPayloadStruct, VendorDefinedStruct, +}; +use tdisp::pci_tdisp::{ + FunctionId, InterfaceId, LockInterfaceFlag, TdiState, START_INTERFACE_NONCE_LEN, + TDISP_PROTOCOL_ID, +}; +use tdisp::pci_tdisp_responder::pci_tdisp_rsp_dispatcher; use std::net::{TcpListener, TcpStream}; use std::u32; @@ -36,6 +47,8 @@ extern crate alloc; use alloc::sync::Arc; use core::ops::DerefMut; +use crate::spdm_device_tdisp_example::DeviceContext; + async fn process_socket_message( stream: Arc>, transport_encap: Arc>, @@ -263,7 +276,36 @@ async fn handle_message( }; spdmlib::secret::asym_sign::register(SECRET_ASYM_IMPL_INSTANCE.clone()); - spdmlib::message::vendor::register_vendor_defined_struct(PCI_IDE_KM_INSTANCE); + let tdisp_rsp_context = DeviceContext { + bus: 0x2a, + device: 0x00, + function: 0x00, + negotiated_version: None, + interface_id: InterfaceId { + function_id: FunctionId { + requester_id: 0x1234, + requester_segment: 0, + requester_segment_valid: false, + }, + }, + dsm_caps: 0, + dev_addr_width: 52, + num_req_this: 1, + num_req_all: 1, + flags: LockInterfaceFlag::empty(), + tdi_state: TdiState::CONFIG_UNLOCKED, + default_stream_id: 0, + mmio_reporting_offset: 0, + bind_p2p_address_mask: 0, + start_interface_nonce: [0u8; START_INTERFACE_NONCE_LEN], + p2p_stream_id: 0, + }; + + let device_context_handle = &tdisp_rsp_context as *const DeviceContext as usize; + spdmlib::message::vendor::register_vendor_defined_struct(VendorDefinedStruct { + vendor_defined_request_handler: pci_idekm_tdisp_rsp_dispatcher, + vendor_context: device_context_handle, + }); init_watchdog(); let mut context = responder::ResponderContext::new( socket_io_transport, @@ -421,9 +463,29 @@ pub async fn send_pci_discovery( true } +fn pci_idekm_tdisp_rsp_dispatcher( + vendor_context: usize, + vendor_defined_req_payload_struct: &VendorDefinedReqPayloadStruct, +) -> SpdmResult { + if vendor_defined_req_payload_struct.req_length < 1 { + return Err(SPDM_STATUS_INVALID_MSG_FIELD); + } + + match vendor_defined_req_payload_struct.vendor_defined_req_payload[0] { + IDE_PROTOCOL_ID => { + pci_ide_km_rsp_dispatcher(vendor_context, vendor_defined_req_payload_struct) + } + TDISP_PROTOCOL_ID => { + pci_tdisp_rsp_dispatcher(vendor_context, vendor_defined_req_payload_struct) + } + _ => Err(SPDM_STATUS_INVALID_MSG_FIELD), + } +} + fn main() { use std::thread; init_device_instance(); + init_device_tdisp_instance(); thread::Builder::new() .stack_size(EMU_STACK_SIZE) diff --git a/test/spdm-responder-emu/src/spdm_device_tdisp_example.rs b/test/spdm-responder-emu/src/spdm_device_tdisp_example.rs new file mode 100644 index 00000000..175b243f --- /dev/null +++ b/test/spdm-responder-emu/src/spdm_device_tdisp_example.rs @@ -0,0 +1,591 @@ +// Copyright (c) 2023 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 or MIT + +use codec::{Codec, Writer}; +use spdmlib::{ + error::{SpdmResult, SPDM_STATUS_INVALID_STATE_PEER}, + message::{ + VendorDefinedReqPayloadStruct, VendorDefinedRspPayloadStruct, + MAX_SPDM_VENDOR_DEFINED_PAYLOAD_SIZE, + }, +}; +use tdisp::{ + pci_tdisp::{ + InterfaceId, InterfaceInfo, LockInterfaceFlag, MMIORangeAttribute, TdiState, + TdispErrorCode, TdispMmioRange, TdispVersion, MAX_DEVICE_REPORT_BUFFER, + START_INTERFACE_NONCE_LEN, TDISP_PROTOCOL_ID, + }, + pci_tdisp_responder::{ + pci_tdisp_rsp_bind_p2p_stream_request::{self, PciTdispDeviceBindP2pStream}, + pci_tdisp_rsp_device_interface_report::{self, PciTdispDeviceInterfaceReport}, + pci_tdisp_rsp_device_interface_state::{self, PciTdispDeviceInterfaceState}, + pci_tdisp_rsp_lock_interface_request::{self, PciTdispDeviceLockInterface}, + pci_tdisp_rsp_set_mmio_attribute_request::{self, PciTdispDeviceSetMmioAttribute}, + pci_tdisp_rsp_start_interface_request::{self, PciTdispDeviceStartInterface}, + pci_tdisp_rsp_stop_interface_request::{self, PciTdispDeviceStopInterface}, + pci_tdisp_rsp_tdisp_capabilities::{self, PciTdispDeviceCapabilities}, + pci_tdisp_rsp_tdisp_error::{self, PciTdispDeviceError}, + pci_tdisp_rsp_tdisp_version::{self, PciTdispDeviceVersion}, + pci_tdisp_rsp_unbind_p2p_stream_request::{self, PciTdispDeviceUnBindP2pStream}, + pci_tdisp_rsp_vdm_response::{self, PciTdispDeviceVdmResponse}, + MAX_TDISP_VERSION_COUNT, + }, +}; + +pub const MMIO_RANGE_COUNT: usize = 4; +pub const DEVICE_SPECIFIC_INFO: &[u8; 9] = b"tdisp emu"; +pub const DEVICE_SPECIFIC_INFO_LEN: usize = DEVICE_SPECIFIC_INFO.len(); + +#[derive(Debug, Copy, Clone)] +pub struct TdiReportStructure { + pub interface_info: InterfaceInfo, + pub msi_x_message_control: u16, + pub lnr_control: u16, + pub tph_control: u32, + pub mmio_range_count: u32, + pub mmio_range: [TdispMmioRange; MMIO_RANGE_COUNT], + pub device_specific_info_len: u32, + pub device_specific_info: [u8; DEVICE_SPECIFIC_INFO_LEN], +} + +impl Default for TdiReportStructure { + fn default() -> Self { + Self { + interface_info: InterfaceInfo::default(), + msi_x_message_control: 0u16, + lnr_control: 0u16, + tph_control: 0u32, + mmio_range_count: 0u32, + mmio_range: [TdispMmioRange::default(); MMIO_RANGE_COUNT], + device_specific_info_len: 0u32, + device_specific_info: [0u8; DEVICE_SPECIFIC_INFO_LEN], + } + } +} + +impl Codec for TdiReportStructure { + fn encode(&self, bytes: &mut codec::Writer) -> Result { + let mut cnt = 0; + + cnt += self.interface_info.encode(bytes)?; + cnt += 0u16.encode(bytes)?; + cnt += self.msi_x_message_control.encode(bytes)?; + cnt += self.lnr_control.encode(bytes)?; + cnt += self.tph_control.encode(bytes)?; + cnt += self.mmio_range_count.encode(bytes)?; + for mr in self.mmio_range.iter().take(self.mmio_range_count as usize) { + cnt += mr.encode(bytes)?; + } + cnt += self.device_specific_info_len.encode(bytes)?; + for dsi in self + .device_specific_info + .iter() + .take(self.device_specific_info_len as usize) + { + cnt += dsi.encode(bytes)?; + } + + Ok(cnt) + } + + fn read(r: &mut codec::Reader) -> Option { + let interface_info = InterfaceInfo::read(r)?; + u16::read(r)?; + let msi_x_message_control = u16::read(r)?; + let lnr_control = u16::read(r)?; + let tph_control = u32::read(r)?; + let mmio_range_count = u32::read(r)?; + if mmio_range_count as usize > MMIO_RANGE_COUNT { + return None; + } + let mut mmio_range = [TdispMmioRange::default(); MMIO_RANGE_COUNT]; + for mr in mmio_range.iter_mut().take(mmio_range_count as usize) { + *mr = TdispMmioRange::read(r)?; + } + let device_specific_info_len = u32::read(r)?; + if device_specific_info_len as usize > DEVICE_SPECIFIC_INFO_LEN { + return None; + } + let mut device_specific_info = [0u8; DEVICE_SPECIFIC_INFO_LEN]; + for dsi in device_specific_info + .iter_mut() + .take(device_specific_info_len as usize) + { + *dsi = u8::read(r)?; + } + + Some(Self { + interface_info, + msi_x_message_control, + lnr_control, + tph_control, + mmio_range_count, + mmio_range, + device_specific_info_len, + device_specific_info, + }) + } +} + +#[derive(Debug, Clone)] +pub struct DeviceContext { + pub bus: u8, + pub device: u8, + pub function: u8, + pub negotiated_version: Option, + pub interface_id: InterfaceId, + pub dsm_caps: u32, + pub dev_addr_width: u8, + pub num_req_this: u8, + pub num_req_all: u8, + pub flags: LockInterfaceFlag, + pub tdi_state: TdiState, + pub default_stream_id: u8, + pub mmio_reporting_offset: u64, + pub bind_p2p_address_mask: u64, + pub start_interface_nonce: [u8; START_INTERFACE_NONCE_LEN], + pub p2p_stream_id: u8, +} + +#[allow(clippy::too_many_arguments)] +fn pci_tdisp_device_capabilities( + // IN + vendor_context: usize, + _tsm_caps: u32, + // OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + dsm_caps: &mut u32, + req_msgs_supported: &mut [u8; 16], + lock_interface_flags_supported: &mut LockInterfaceFlag, + dev_addr_width: &mut u8, + num_req_this: &mut u8, + num_req_all: &mut u8, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &*device_context as &DeviceContext }; + + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + *tdisp_error_code = Some(TdispErrorCode::UNSPECIFIED); + return Ok(()); + }; + + *interface_id = device_context.interface_id; + *dsm_caps = device_context.dsm_caps; + req_msgs_supported[0] = 0x7f; + *lock_interface_flags_supported = device_context.flags; + *dev_addr_width = device_context.dev_addr_width; + *num_req_this = device_context.num_req_this; + *num_req_all = device_context.num_req_all; + + *tdisp_error_code = None; + Ok(()) +} + +fn pci_tdisp_device_error( + // IN + vendor_context: usize, + // OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + return Err(SPDM_STATUS_INVALID_STATE_PEER); + }; + + *interface_id = device_context.interface_id; + + device_context.tdi_state = TdiState::ERROR; + + Ok(()) +} + +fn pci_tdisp_device_interface_report( + // IN + vendor_context: usize, + // OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + tdi_report: &mut [u8; MAX_DEVICE_REPORT_BUFFER], + tdi_report_size: &mut usize, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + if device_context.tdi_state != TdiState::CONFIG_LOCKED + && device_context.tdi_state != TdiState::RUN + { + *tdisp_error_code = Some(TdispErrorCode::INVALID_INTERFACE_STATE); + } else { + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + return Err(SPDM_STATUS_INVALID_STATE_PEER); + }; + + *interface_id = device_context.interface_id; + let report = TdiReportStructure { + interface_info: InterfaceInfo::DEVICE_FIRMWARE_UPDATES_NOT_PERMITTED, + msi_x_message_control: 0u16, + lnr_control: 0u16, + tph_control: 0u32, + mmio_range_count: 1, + mmio_range: [TdispMmioRange { + first_page_with_offset_added: 0x12340000 + device_context.mmio_reporting_offset, + number_of_pages: 32, + range_attributes: MMIORangeAttribute::empty(), + }; MMIO_RANGE_COUNT], + device_specific_info_len: 6, + device_specific_info: [6u8; DEVICE_SPECIFIC_INFO_LEN], + }; + let mut writer = Writer::init(tdi_report); + if let Ok(size) = report.encode(&mut writer) { + *tdi_report_size = size; + *tdisp_error_code = None; + } else { + *tdi_report_size = 0; + *tdisp_error_code = Some(TdispErrorCode::INVALID_DEVICE_CONFIGURATION); + } + } + + Ok(()) +} + +fn pci_tdisp_device_interface_state( + // IN + vendor_context: usize, + // OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + tdi_state: &mut TdiState, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + *tdisp_error_code = Some(TdispErrorCode::UNSPECIFIED); + return Ok(()); + }; + + *interface_id = device_context.interface_id; + *tdi_state = device_context.tdi_state; + + *tdisp_error_code = None; + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn pci_tdisp_device_lock_interface( + // IN + vendor_context: usize, + flags: &LockInterfaceFlag, + default_stream_id: u8, + mmio_reporting_offset: u64, + bind_p2p_address_mask: u64, + // OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + start_interface_nonce: &mut [u8; START_INTERFACE_NONCE_LEN], + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + if device_context.tdi_state != TdiState::CONFIG_UNLOCKED { + *tdisp_error_code = Some(TdispErrorCode::INVALID_INTERFACE_STATE); + } else { + *tdisp_error_code = None; + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + *tdisp_error_code = Some(TdispErrorCode::UNSPECIFIED); + return Ok(()); + }; + *interface_id = device_context.interface_id; + + device_context.flags = *flags; + device_context.default_stream_id = default_stream_id; + device_context.mmio_reporting_offset = mmio_reporting_offset; + device_context.bind_p2p_address_mask = bind_p2p_address_mask; + + if spdmlib::crypto::rand::get_random(start_interface_nonce).is_err() { + *tdisp_error_code = Some(TdispErrorCode::INSUFFICIENT_ENTROPY); + return Ok(()); + } + + device_context + .start_interface_nonce + .copy_from_slice(start_interface_nonce); + + device_context.tdi_state = TdiState::CONFIG_LOCKED; + } + + Ok(()) +} + +fn pci_tdisp_device_start_interface( + //IN + vendor_context: usize, + start_interface_nonce: &[u8; START_INTERFACE_NONCE_LEN], + //OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + if device_context.tdi_state != TdiState::CONFIG_LOCKED { + *tdisp_error_code = Some(TdispErrorCode::INVALID_INTERFACE_STATE); + } else if start_interface_nonce != &device_context.start_interface_nonce { + *tdisp_error_code = Some(TdispErrorCode::INVALID_NONCE); + } else { + *tdisp_error_code = None; + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + return Err(SPDM_STATUS_INVALID_STATE_PEER); + }; + *interface_id = device_context.interface_id; + + device_context.tdi_state = TdiState::RUN; + } + + Ok(()) +} + +fn pci_tdisp_device_stop_interface( + // IN + vendor_context: usize, + // OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + if device_context.tdi_state != TdiState::RUN { + *tdisp_error_code = Some(TdispErrorCode::INVALID_INTERFACE_STATE); + return Ok(()); + } else { + *tdisp_error_code = None; + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + *tdisp_error_code = Some(TdispErrorCode::UNSPECIFIED); + return Ok(()); + }; + *interface_id = device_context.interface_id; + + device_context.tdi_state = TdiState::CONFIG_UNLOCKED; + } + + Ok(()) +} + +fn pci_tdisp_device_bind_p2p_stream( + //IN + vendor_context: usize, + p2p_stream_id: u8, + //OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + if device_context.tdi_state != TdiState::RUN { + *tdisp_error_code = Some(TdispErrorCode::INVALID_INTERFACE_STATE); + return Ok(()); + } else { + *tdisp_error_code = None; + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + *tdisp_error_code = Some(TdispErrorCode::UNSPECIFIED); + return Ok(()); + }; + *interface_id = device_context.interface_id; + + device_context.p2p_stream_id = p2p_stream_id; + } + + Ok(()) +} + +fn pci_tdisp_device_unbind_p2p_stream( + //IN + vendor_context: usize, + p2p_stream_id: u8, + //OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + if device_context.tdi_state != TdiState::RUN { + *tdisp_error_code = Some(TdispErrorCode::INVALID_INTERFACE_STATE); + return Ok(()); + } else if p2p_stream_id != device_context.p2p_stream_id { + *tdisp_error_code = Some(TdispErrorCode::INVALID_REQUEST); + return Ok(()); + } else { + *tdisp_error_code = None; + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + *tdisp_error_code = Some(TdispErrorCode::UNSPECIFIED); + return Ok(()); + }; + *interface_id = device_context.interface_id; + + device_context.p2p_stream_id = 0; + } + + Ok(()) +} + +fn pci_tdisp_device_set_mmio_attribute( + //IN + vendor_context: usize, + _mmio_range: &TdispMmioRange, + //OUT + negotiated_version: &mut TdispVersion, + interface_id: &mut InterfaceId, + tdisp_error_code: &mut Option, +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + if device_context.tdi_state != TdiState::RUN { + *tdisp_error_code = Some(TdispErrorCode::INVALID_INTERFACE_STATE); + return Ok(()); + } else { + *tdisp_error_code = None; + *negotiated_version = if let Some(negotiated_version) = device_context.negotiated_version { + negotiated_version + } else { + *tdisp_error_code = Some(TdispErrorCode::UNSPECIFIED); + return Ok(()); + }; + *interface_id = device_context.interface_id; + } + + Ok(()) +} + +fn pci_tdisp_device_vdm_response( + //IN + _vendor_context: usize, + vendor_defined_req_payload_struct: &VendorDefinedReqPayloadStruct, +) -> SpdmResult { + println!( + "vdm request: {:X?}", + &vendor_defined_req_payload_struct.vendor_defined_req_payload + [..vendor_defined_req_payload_struct.req_length as usize] + ); + + let mut vendor_defined_rsp_payload_struct = VendorDefinedRspPayloadStruct { + rsp_length: 19, + vendor_defined_rsp_payload: [0u8; MAX_SPDM_VENDOR_DEFINED_PAYLOAD_SIZE], + }; + + vendor_defined_rsp_payload_struct.vendor_defined_rsp_payload[0] = TDISP_PROTOCOL_ID; + vendor_defined_rsp_payload_struct.vendor_defined_rsp_payload[1..19] + .copy_from_slice(b"tdisp vdm response"); + + Ok(vendor_defined_rsp_payload_struct) +} + +fn pci_tdisp_device_version( + // IN + vendor_context: usize, + // OUT + interface_id: &mut InterfaceId, + version_num_count: &mut u8, + version_num_entry: &mut [TdispVersion; MAX_TDISP_VERSION_COUNT], +) -> SpdmResult { + let device_context = vendor_context as *mut DeviceContext; + + let device_context = unsafe { &mut *device_context as &mut DeviceContext }; + + *interface_id = device_context.interface_id; + *version_num_count = 1; + version_num_entry[0] = TdispVersion { + major_version: 1, + minor_version: 0, + }; + + device_context.negotiated_version = Some(TdispVersion { + major_version: 1, + minor_version: 0, + }); + + Ok(()) +} + +pub fn init_device_tdisp_instance() { + pci_tdisp_rsp_tdisp_capabilities::register(PciTdispDeviceCapabilities { + pci_tdisp_device_capabilities_cb: pci_tdisp_device_capabilities, + }); + pci_tdisp_rsp_tdisp_error::register(PciTdispDeviceError { + pci_tdisp_device_error_cb: pci_tdisp_device_error, + }); + pci_tdisp_rsp_device_interface_report::register(PciTdispDeviceInterfaceReport { + pci_tdisp_device_interface_report_cb: pci_tdisp_device_interface_report, + }); + pci_tdisp_rsp_device_interface_state::register(PciTdispDeviceInterfaceState { + pci_tdisp_device_interface_state_cb: pci_tdisp_device_interface_state, + }); + pci_tdisp_rsp_lock_interface_request::register(PciTdispDeviceLockInterface { + pci_tdisp_device_lock_interface_cb: pci_tdisp_device_lock_interface, + }); + pci_tdisp_rsp_start_interface_request::register(PciTdispDeviceStartInterface { + pci_tdisp_device_start_interface_cb: pci_tdisp_device_start_interface, + }); + pci_tdisp_rsp_stop_interface_request::register(PciTdispDeviceStopInterface { + pci_tdisp_device_stop_interface_cb: pci_tdisp_device_stop_interface, + }); + pci_tdisp_rsp_tdisp_version::register(PciTdispDeviceVersion { + pci_tdisp_device_version_cb: pci_tdisp_device_version, + }); + pci_tdisp_rsp_bind_p2p_stream_request::register(PciTdispDeviceBindP2pStream { + pci_tdisp_device_bind_p2p_stream_cb: pci_tdisp_device_bind_p2p_stream, + }); + pci_tdisp_rsp_unbind_p2p_stream_request::register(PciTdispDeviceUnBindP2pStream { + pci_tdisp_device_unbind_p2p_stream_cb: pci_tdisp_device_unbind_p2p_stream, + }); + pci_tdisp_rsp_set_mmio_attribute_request::register(PciTdispDeviceSetMmioAttribute { + pci_tdisp_device_set_mmio_attribute_cb: pci_tdisp_device_set_mmio_attribute, + }); + pci_tdisp_rsp_vdm_response::register(PciTdispDeviceVdmResponse { + pci_tdisp_device_vdm_response_cb: pci_tdisp_device_vdm_response, + }); +}