Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update; \
sudo apt-get install -y cmake libclang-dev libpci-dev libssl-dev python3-dev gem; \
sudo apt-get install -y cmake libclang-dev libpci-dev libssl-dev python3-dev gem libnvme-dev; \
sudo gem install cbor-diag;

- name: Build libspdm
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ std = [
"futures",
"nix",
"inquire",
"page_size",
]

[lib]
Expand Down Expand Up @@ -61,6 +62,7 @@ colored = { version = "2.1", optional = true }
which = { version = "6.0", optional = true }
asn1-rs = { version = "0.6", optional = true }
x509-parser = { version = "0.15", optional = true }
page_size = { version = "0.6", optional = true }
minicbor = { version = "0.25", features = [
"half",
"alloc",
Expand All @@ -72,7 +74,7 @@ minicbor-derive = { version = "0.15", features = [
], optional = true }
tokio = { version = "1.49", features = ["full"], optional = true }
futures = { version = "0.3", optional = true }
nix = { version = "0.29.0", features = ["user"], optional = true }
nix = { version = "0.29", features = ["user", "fs", "ioctl"], optional = true }
libmctp = { version = "0.2" }
inquire = { version = "0.7.5", optional = true}

Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ Note: `dnf` commands are for Fedora, and `apt` is used for Debian/Ubuntu based
distributions.

```shell
$ sudo dnf install cmake clang-libs clang-devel pciutils-devel openssl openssl-devel python3-devel systemd-devel
$ sudo dnf install cmake clang-libs clang-devel pciutils-devel openssl openssl-devel python3-devel systemd-devel libnvme

or

$ sudo apt install cmake clang libclang-dev pciutils libpci-dev openssl libssl-dev libsystemd-dev python3-dev pkg-config
$ sudo apt install cmake clang libclang-dev pciutils libpci-dev openssl libssl-dev libsystemd-dev python3-dev pkg-config libnvme
```

### Ruby
Expand Down Expand Up @@ -296,6 +296,14 @@ invoked as below:
```shell
./target/debug/spdm_utils --pcie-vid <VendorID> --pcie-devid <DeviceID> --doe-pci-cfg request get-digests
```
### SPDM for NVMe over the SPDM Storage Transport

SPDM-utils supports the SPDM over storage transport as defined by the DMTF DSP0286.
For example, the following command can be used to interact with an NVMe device.

```shell
$ ./target/debug/spdm_utils --blk-dev-path /dev/nvme0 --nvme --no-session request get-version,get-capabilities
```

## Retrieving Certificates

Expand Down
2 changes: 2 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fn main() {
println!("cargo:rustc-link-arg=-Wl,--start-group");

println!("cargo:rustc-link-arg=-lpci");
println!("cargo:rustc-link-arg=-lnvme");

println!("cargo:rustc-link-arg=-lmemlib");
println!("cargo:rustc-link-arg=-lmalloclib");
Expand All @@ -41,6 +42,7 @@ fn main() {
println!("cargo:rustc-link-arg=-lspdm_crypt_ext_lib");
println!("cargo:rustc-link-arg=-lspdm_transport_pcidoe_lib");
println!("cargo:rustc-link-arg=-lspdm_transport_mctp_lib");
println!("cargo:rustc-link-arg=-lspdm_transport_storage_lib");

// Link SPDM Test Libraries
let mut builder = if cfg!(feature = "libspdm_tests") {
Expand Down
111 changes: 65 additions & 46 deletions src/io_buffers.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,57 @@
use nix::errno::Errno;
use once_cell::sync::Lazy;
use std::alloc::dealloc;
use std::alloc::{Layout, alloc_zeroed};
use std::ffi::c_void;
use std::ptr::NonNull;
use std::sync::Mutex;

static SEND_BUFFER: Lazy<Mutex<Option<Vec<u8>>>> = Lazy::new(|| Mutex::new(None));
static RECEIVE_BUFFER: Lazy<Mutex<Option<Vec<u8>>>> = Lazy::new(|| Mutex::new(None));
type IoBuffer = Option<(Vec<u8>, Layout)>;
static SEND_BUFFER: Lazy<Mutex<IoBuffer>> = Lazy::new(|| Mutex::new(None));
static RECEIVE_BUFFER: Lazy<Mutex<IoBuffer>> = Lazy::new(|| Mutex::new(None));

pub fn libspdm_setup_io_buffers(
context: *mut c_void,
send_len: usize,
recv_len: usize,
send_recv_len: usize,
libsdpm_buff_len: usize,
) -> Result<(), ()> {
let result = std::panic::catch_unwind(|| {
let buffer_send = vec![0; send_len];
let buffer_receive = vec![0; recv_len];
(buffer_send, buffer_receive)
});

match result {
Ok(buffers) => {
*(SEND_BUFFER.lock().unwrap()) = Some(buffers.0);
*(RECEIVE_BUFFER.lock().unwrap()) = Some(buffers.1);
// NVMe requires page aligned buffers
if !send_recv_len.is_multiple_of(page_size::get()) {
error!("Requested SEND/RECV buffer size is not system page size aligned");
return Err(());
}

unsafe {
libspdm::libspdm_rs::libspdm_register_device_buffer_func(
context,
send_len as u32,
recv_len as u32,
Some(acquire_sender_buffer),
Some(release_sender_buffer),
Some(acquire_receiver_buffer),
Some(release_receiver_buffer),
)
}
Ok(())
}
Err(_) => {
error!("Failed to allocate transport buffers");
Err(())
}
fn page_aligned_buffer(size: usize) -> Result<(Vec<u8>, Layout), Errno> {
let layout = Layout::from_size_align(size, page_size::get()).map_err(|_| Errno::EINVAL)?;
let ptr = unsafe { alloc_zeroed(layout) };
let ptr = NonNull::new(ptr).ok_or(Errno::ENOMEM)?;
let vec = unsafe { Vec::from_raw_parts(ptr.as_ptr(), 0, size) };
Ok((vec, layout))
}

let buffer_send = page_aligned_buffer(send_recv_len).map_err(|e| {
error!("Failed to allocate SEND buffer: {e}");
})?;
let buffer_recv = page_aligned_buffer(send_recv_len).map_err(|e| {
error!("Failed to allocate RECV buffer: {e}");
})?;

*(SEND_BUFFER.lock().unwrap()) = Some((buffer_send.0, buffer_send.1));
*(RECEIVE_BUFFER.lock().unwrap()) = Some((buffer_recv.0, buffer_recv.1));

unsafe {
libspdm::libspdm_rs::libspdm_register_device_buffer_func(
context,
libsdpm_buff_len as u32,
libsdpm_buff_len as u32,
Some(acquire_sender_buffer),
Some(release_sender_buffer),
Some(acquire_receiver_buffer),
Some(release_receiver_buffer),
)
};

Ok(())
}

/// # Summary
Expand All @@ -59,12 +72,11 @@ pub unsafe extern "C" fn acquire_sender_buffer(
_context: *mut c_void,
msg_buf_ptr: *mut *mut c_void,
) -> u32 {
if let Some(ref buf) = *SEND_BUFFER.lock().unwrap() {
let buf_ptr = buf.as_ptr() as *mut c_void;
if let Some(ref mem) = *SEND_BUFFER.lock().unwrap() {
let buf_ptr = mem.0.as_ptr() as *mut c_void;
unsafe { *msg_buf_ptr = buf_ptr };
return 0;
}

error!("Sender buffer is lost or not initialized");
1
}
Expand All @@ -87,8 +99,8 @@ pub unsafe extern "C" fn acquire_receiver_buffer(
_context: *mut c_void,
msg_buf_ptr: *mut *mut c_void,
) -> u32 {
if let Some(ref buf) = *RECEIVE_BUFFER.lock().unwrap() {
let buf_ptr = buf.as_ptr() as *mut c_void;
if let Some(ref mem) = *RECEIVE_BUFFER.lock().unwrap() {
let buf_ptr = mem.0.as_ptr() as *mut c_void;
unsafe { *msg_buf_ptr = buf_ptr };
return 0;
}
Expand All @@ -114,17 +126,24 @@ pub unsafe extern "C" fn release_sender_buffer(_context: *mut c_void, _msg_buf_p
/// Drop the IO buffers out of scope, this should release the underlying
/// memory.
pub unsafe fn libspdm_drop_io_buffers() {
let mut send_buf = SEND_BUFFER.lock().unwrap();
if send_buf.is_some() {
*send_buf = None;
} else {
warn!("Send buffer is lost or not initialized");
let free = |buf: &mut IoBuffer, err: &str| {
if let Some(mut mem) = buf.take() {
let ptr = mem.0.as_mut_ptr();
let layout = mem.1;
std::mem::forget(mem.0);
unsafe { dealloc(ptr, layout) };
} else {
error!("{}", err);
}
};

{
let mut send_buf = SEND_BUFFER.lock().unwrap();
free(&mut send_buf, "Send buffer is lost or not initialized");
}

let mut recv_buf = RECEIVE_BUFFER.lock().unwrap();
if recv_buf.is_some() {
*recv_buf = None;
} else {
warn!("Receive buffer is lost or not initialized");
{
let mut recv_buf = RECEIVE_BUFFER.lock().unwrap();
free(&mut recv_buf, "Receive buffer is lost or not initialized");
}
}
14 changes: 14 additions & 0 deletions src/libspdm/spdm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ pub enum TransportLayer {
Doe,
/// DMTF Management Component Transport Protocol
Mctp,
/// SCSI Security Protocol In/Out commands
Storage,
}

#[cfg(feature = "no_std")]
Expand Down Expand Up @@ -506,6 +508,18 @@ pub unsafe fn setup_transport_layer(
)
};
}
TransportLayer::Storage => {
unsafe {
libspdm_register_transport_layer_func(
context,
libspdm_max_spdm_msg_size,
LIBSPDM_STORAGE_TRANSPORT_HEADER_SIZE,
LIBSPDM_STORAGE_TRANSPORT_TAIL_SIZE,
Some(libspdm_transport_storage_encode_message),
Some(libspdm_transport_storage_decode_message),
)
};
}
}

let libspdm_scratch_buffer_size =
Expand Down
36 changes: 36 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,20 @@ use tokio::task;
extern crate log;
use env_logger::Env;
use libspdm::{responder, responder::CertModel, spdm};
use nix::errno::Errno;
use once_cell::sync::Lazy;

pub static SOCKET_PATH: &str = "SPDM-Utils-loopback-socket";

mod cli_helpers;
mod doe_pci_cfg;
mod io_buffers;
mod nvme;
mod qemu_server;
mod request;
mod socket_client;
mod socket_server;
mod storage_standards;
mod tcg_concise_evidence_binding;
mod test_suite;
mod usb_i2c;
Expand All @@ -43,6 +46,18 @@ struct Args {
#[command(subcommand)]
command: Commands,

/// Use NVMe commands for the target device
#[arg(short, long, requires_ifs([("true", "blk_dev_path")]))]
nvme: bool,

/// NVME NameSpace Identifier
#[arg(long, default_value_t = 1)]
nsid: u32,

/// Path to the block device
#[arg(long)]
blk_dev_path: Option<String>,

/// Use the Linux PCIe extended configuration backend
/// This is generally run on the Linux host machine
#[arg(short, long)]
Expand Down Expand Up @@ -664,6 +679,10 @@ async fn main() -> Result<(), ()> {

let mut count = 0;

cli.nvme.then(|| {
count += 1;
});

cli.doe_pci_cfg.then(|| {
count += 1;
});
Expand Down Expand Up @@ -728,12 +747,21 @@ async fn main() -> Result<(), ()> {
}

usb_i2c::register_device(cntx_ptr, cli.usb_i2c_dev, cli.usb_i2c_baud)?;
} else if cli.nvme {
if let Err(e) = nvme::nvme_get_sec_info(&cli.blk_dev_path.clone().unwrap(), cli.nsid) {
if e == Errno::ENOTSUP {
error!("SPDM is not supported by this NVMe device");
}
return Err(());
}
nvme::register_device(cntx_ptr, &cli.blk_dev_path.clone().unwrap(), cli.nsid).unwrap();
} else if cli.qemu_server {
if let Commands::Request { .. } = cli.command {
error!("QEMU Server does not support running an SPDM requester");
return Err(());
}
if let Some(proto) = cli.spdm_transport_protocol {
info!("Using {:?} transport for QEMU", proto);
qemu_server::register_device(cntx_ptr, cli.qemu_port, proto)?;
} else {
qemu_server::register_device(cntx_ptr, cli.qemu_port, spdm::TransportLayer::Doe)?;
Expand All @@ -745,13 +773,21 @@ async fn main() -> Result<(), ()> {

unsafe {
if let Some(proto) = cli.spdm_transport_protocol {
info!("Using {:?} transport", proto);
spdm::setup_transport_layer(cntx_ptr, proto, spdm::LIBSPDM_MAX_SPDM_MSG_SIZE)?;
} else if cli.usb_i2c {
spdm::setup_transport_layer(
cntx_ptr,
spdm::TransportLayer::Mctp,
spdm::LIBSPDM_MAX_SPDM_MSG_SIZE,
)?;
} else if cli.nvme {
spdm::setup_transport_layer(
cntx_ptr,
spdm::TransportLayer::Storage,
spdm::LIBSPDM_MAX_SPDM_MSG_SIZE,
)
.unwrap();
} else {
spdm::setup_transport_layer(
cntx_ptr,
Expand Down
Loading