Skip to content

Commit

Permalink
protocols: Implement the vTPM protocol
Browse files Browse the repository at this point in the history
The vTPM protocol is introduced in the SVSM specification v1.0 to allow other
software components (e.g. OVMF and guest kernel) to communicate with the
SVSM vTPM at runtime. It follows the Microsoft TPM 2.0 Simulator protocol.

The specification enumerates two call IDs supportted by the vTPM protocol:
1. SVSM_VTPM_QUERY: query vTPM command and features support.
2. SVSM_VTPM_CMD: Execute a TPM platform command.

As for the SVSM_VTPM_CMD, the TPM platform commands are described in
libvtpm/ms-tpm-20-ref/TPMCmd/Simulator/include/prototypes/Simulator_fp.h
e.g.:
- TPM_SIGNAL_HASH_DATA
- TPM_SEND_COMMAND
- TPM_REMOTE_HANDSHAKE
- TPM_SET_ALTERNATIVE_RESULT

This patch implements both the SVSM_VTPM_QUERY and the SVSM_VTPM_CMD call IDs.

For the SVSM_VTPM_CMD, only the TPM_SEND_COMMAND platform command is
implemented.

Signed-off-by: Claudio Carvalho <[email protected]>
  • Loading branch information
cclaudio committed Oct 17, 2023
1 parent fa0b221 commit 407b60f
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 2 deletions.
8 changes: 8 additions & 0 deletions libvtpm/libvtpm.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#pragma once

typedef unsigned int uint32_t;
void _plat__RunCommand(
uint32_t requestSize, // IN: command buffer size
unsigned char *request, // IN: command buffer
uint32_t *responseSize, // IN/OUT: response buffer size
unsigned char **response // IN/OUT: response buffer
);

void _plat__LocalitySet(unsigned char locality);
void _plat__SetNvAvail(void);
int _plat__Signal_PowerOn(void);
Expand Down
3 changes: 3 additions & 0 deletions src/protocols/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

pub mod core;
pub mod errors;
#[cfg(any(not(test), rust_analyzer))]
pub mod vtpm;

use crate::sev::vmsa::{GuestVMExit, VMSA};

// SVSM protocols
pub const SVSM_CORE_PROTOCOL: u32 = 0;
pub const SVSM_VTPM_PROTOCOL: u32 = 2;

#[derive(Debug, Default)]
pub struct RequestParams {
Expand Down
109 changes: 109 additions & 0 deletions src/protocols/vtpm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2023 IBM Corp
//
// Author: Claudio Carvalho <[email protected]>

/// vTPM Protocol defined in the Chapter 8 of the SVSM spec v0.62.
use crate::address::{Address, PhysAddr};
use crate::mm::{valid_phys_address, GuestPtr, PerCPUPageMappingGuard, PAGE_SIZE};
use crate::protocols::errors::SvsmReqError;
use crate::protocols::RequestParams;
use crate::vtpm::mssim::{mssim_platform_request, mssim_platform_supported_commands};

/// Table 14: vTPM Protocol Services
const SVSM_VTPM_QUERY: u32 = 0;
const SVSM_VTPM_COMMAND: u32 = 1;

/// Table 15: vTPM Common Request/Response Structure
///
/// Each MSSIM TPM command can build upon this common request/response
/// structure to create a structure specific to the command.
///
/// @command: gPA of the MSIM TPM Command structure.
/// The first field of this structure must be
/// the command number.
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
struct VtpmCmdRequest {
command: u32,
}

/// Table 16: TPM_SEND_COMMAND request structure
///
/// @command: The command (must be TPM_SEND_COMMAND)
/// @locality: The locality
/// @inbuf_size: The size of the input buffer following
/// @inbuf: A buffer of size inbuf_size
///
/// Note that @inbuf_size must be large enough to hold the response so
/// it represents the maximum buffer size, not the size of the specific
/// TPM command.
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct TpmSendCommandRequest {
pub command: u32,
pub locality: u8,
pub inbuf_size: u32,
//pub inbuf: u64,
}

/// Table 17: TPM_SEND_COMMAND response structure
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct TpmSendCommandResponse {
pub outbuf_size: u32,
//pub outbuf: u64,
}

fn vtpm_command_request(params: &mut RequestParams) -> Result<(), SvsmReqError> {
// FIXME: Remove this check when the guest tpm driver is able to probe
// using the SVSM_VTPM_QUERY call ID.
if params.rcx == 0 {
log::info!("SVSM: vtpm command probe");
return Ok(());
}

let paddr = PhysAddr::from(params.rcx);

if !valid_phys_address(paddr) {
log::error!(
"vTPM buffer not valid physical address {} {}",
paddr,
params.rcx
);
return Err(SvsmReqError::invalid_parameter());
}

// The buffer gpa size is one page, but it not required to be page aligned.
let start = paddr.page_align();
let offset = paddr.page_offset();
let end = (paddr + PAGE_SIZE).page_align_up();

let guard = PerCPUPageMappingGuard::create(start, end, 0)?;
let vaddr = guard.virt_addr() + offset;

let guest_page = GuestPtr::<VtpmCmdRequest>::new(vaddr);
let request = guest_page.read()?;

mssim_platform_request(request.command, vaddr)?;

Ok(())
}

fn vtpm_query_request(params: &mut RequestParams) -> Result<(), SvsmReqError> {
// Bitmap of the supported vTPM commands
params.rcx = mssim_platform_supported_commands();
// Supported vTPM features. Must-be-zero
params.rdx = 0;

Ok(())
}

pub fn vtpm_protocol_request(request: u32, params: &mut RequestParams) -> Result<(), SvsmReqError> {
match request {
SVSM_VTPM_QUERY => vtpm_query_request(params),
SVSM_VTPM_COMMAND => vtpm_command_request(params),
_ => Err(SvsmReqError::unsupported_call()),
}
}
4 changes: 4 additions & 0 deletions src/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::error::SvsmError;
use crate::mm::GuestPtr;
use crate::protocols::core::core_protocol_request;
use crate::protocols::errors::{SvsmReqError, SvsmResultCode};
#[cfg(any(not(test), rust_analyzer))]
use crate::protocols::{vtpm::vtpm_protocol_request, SVSM_VTPM_PROTOCOL};
use crate::protocols::{RequestParams, SVSM_CORE_PROTOCOL};
use crate::sev::vmsa::GuestVMExit;
use crate::types::GUEST_VMPL;
Expand Down Expand Up @@ -66,6 +68,8 @@ fn request_loop_once(

match protocol {
SVSM_CORE_PROTOCOL => core_protocol_request(request, params).map(|_| true),
#[cfg(any(not(test), rust_analyzer))]
SVSM_VTPM_PROTOCOL => vtpm_protocol_request(request, params).map(|_| true),
_ => Err(SvsmReqError::unsupported_protocol()),
}
}
Expand Down
110 changes: 108 additions & 2 deletions src/vtpm/mssim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,124 @@
// because we are not compiling the TPM Simulator (-DSIMULATION=NO), as it brings in more
// dependencies on libc and a higher memory footprint.

extern crate alloc;

use alloc::vec::Vec;
use core::ffi::c_void;
use core::ptr::{copy_nonoverlapping, write_bytes};

use crate::address::VirtAddr;
use crate::mm::{GuestPtr, PAGE_SIZE};
use crate::protocols::errors::SvsmReqError;
use crate::protocols::vtpm::{TpmSendCommandRequest, TpmSendCommandResponse};
use crate::vtpm::bindings::{
_plat__NVDisable, _plat__NVEnable, _plat__SetNvAvail, _plat__Signal_PowerOn,
_plat__Signal_Reset,
_plat__LocalitySet, _plat__NVDisable, _plat__NVEnable, _plat__RunCommand, _plat__SetNvAvail,
_plat__Signal_PowerOn, _plat__Signal_Reset,
};
use crate::vtpm::mstpm::{mstpm_manufacture, mstpm_teardown};

pub const TPM_BUFFER_MAX_SIZE: usize = PAGE_SIZE;

static mut VTPM_IS_POWERED_ON: bool = false;

/// Current MSSIM TPM commands we support. A complete list can be found in:
/// libvtpm/ms-tpm-20-ref/TPMCmd/Simulator/include/TpmTcpProtocol.h
const TPM_SEND_COMMAND: u32 = 8;

const TPM_SUPPORTED_CMDS: &[u32] = &[TPM_SEND_COMMAND];

pub fn mssim_platform_supported_commands() -> u64 {
let mut bitmap: u64 = 0;

for cmd in TPM_SUPPORTED_CMDS {
bitmap |= 1u64 << *cmd;
}

bitmap
}

pub fn mssim_platform_request(command: u32, buffer: VirtAddr) -> Result<(), SvsmReqError> {
match command {
TPM_SEND_COMMAND => {
mssim_send_tpm_command(buffer)?;
}
_ => return Err(SvsmReqError::unsupported_call()),
}

Ok(())
}

/// Send a TPM command for a given locality
pub fn mssim_send_tpm_command(buffer: VirtAddr) -> Result<(), SvsmReqError> {
const REQ_INBUF_OFFSET: usize = core::mem::size_of::<TpmSendCommandRequest>();

let guest_page = GuestPtr::<TpmSendCommandRequest>::new(buffer);
let request = guest_page.read()?;

// TODO: Before implementing locality, we need to agree what it means
// to the platform
if request.locality != 0 {
return Err(SvsmReqError::invalid_parameter());
}
unsafe {
if !VTPM_IS_POWERED_ON {
return Err(SvsmReqError::invalid_request());
}
}

let mut inbuf: Vec<u8> = Vec::with_capacity(TPM_BUFFER_MAX_SIZE);
let inbuf_p: *mut u8 = inbuf.as_mut_ptr();

let mut outbuf: Vec<u8> = Vec::with_capacity(TPM_BUFFER_MAX_SIZE);
let mut outbuf_p: *mut u8 = outbuf.as_mut_ptr();
let outbuf_pp: *mut *mut u8 = &mut outbuf_p;

let mut outbuf_size = TPM_BUFFER_MAX_SIZE as u32;
let outbuf_size_p = &mut outbuf_size;

unsafe {
// let b = core::slice::from_raw_parts(buffer.as_ptr::<u8>().add(REQ_INBUF_OFFSET), request.inbuf_size as usize);
// let sz = request.inbuf_size;
// log::info!("vTPM request buf({}) {:x?}", sz, b);

copy_nonoverlapping(
buffer.as_mut_ptr::<u8>().add(REQ_INBUF_OFFSET),
inbuf_p,
request.inbuf_size as usize,
);

_plat__LocalitySet(request.locality);

_plat__RunCommand(request.inbuf_size, inbuf_p, outbuf_size_p, outbuf_pp);
outbuf.set_len(*outbuf_size_p as usize);

// log::info!("vTPM response buf({}) {:x?}", *outbuf_size_p, outbuf);
}

// Request buffer not large enough to hold the response
let max_out_buf_size = TPM_BUFFER_MAX_SIZE - core::mem::size_of::<TpmSendCommandResponse>();
if *outbuf_size_p == 0 || *outbuf_size_p as usize > max_out_buf_size {
return Err(SvsmReqError::invalid_parameter());
}

// Populate buffer with size and data
unsafe {
write_bytes(buffer.as_mut_ptr::<u8>(), 0, PAGE_SIZE);
copy_nonoverlapping(
outbuf_size_p as *const _ as *const u8,
buffer.as_mut_ptr::<u8>(),
4usize,
);
copy_nonoverlapping(
outbuf.as_ptr() as *const u8,
buffer.as_mut_ptr::<u8>().add(4),
outbuf_size as usize,
);
}

Ok(())
}

/// Initialize the vTPM
pub fn mssim_vtpm_init() -> Result<(), SvsmReqError> {
// Manufacture the MS TPM following the same steps done in the Simulator:
Expand Down

0 comments on commit 407b60f

Please sign in to comment.