Skip to content

Commit

Permalink
psp/request: Add get_report()
Browse files Browse the repository at this point in the history
The get_report() can be used to request an attestation report to the PSP
firmware. If the last parameter CertBuf is provided, the certificate
chain required to verify the attestation report is saved in the
CertBuf.addr provided.

If get_report() fails an Err(error_code) is returned. The wrapped error_code is
compliant with the Core Protocol error codes defined in the SVSM spec 0.62
(draft). The psp_rc reference provided can be used to further understand
the error.

Example:

let buf: x86_64::addr::VirtAddr = mem::mem_allocate(0x4000).unwrap();
let mut certs: CertsBuf = CertsBuf::new(buf, 0x4000usize);
let mut psp_rc: u64 = 0;
let mut data: [u8; USER_DATA_SIZE] = [0u8; USER_DATA_SIZE];
data[0] = 0x31;
data[1] = 0x32;
data[2] = 0x33;
data[4] = 0x34;

// Test extended attestation report request
let result: Result<psp::msg_report::SnpReportResponse, u64> =
    psp::request::get_report(&data, &mut psp_rc, Some(&mut certs));

if let Ok(resp) = result {
    prints!("INFO: Report, {} bytes, vmpl {}\n",
            { resp.get_report_size() },
            { resp.get_report().get_vmpl() }
    );
    prints!("INFO: report_id: {:x?}\n", {
            resp.get_report().get_report_id()
    });
    prints!("INFO: report_data: {:x?}\n", {
            resp.get_report().get_report_data()
    });

    let sample: *const [u8; 500] = buf.as_ptr() as *const [u8; 500];
    prints!("INFO: certs sample {:x?}\n", { unsafe { *sample } });
}

Signed-off-by: Claudio Carvalho <[email protected]>
  • Loading branch information
cclaudio committed Jun 22, 2023
1 parent 5de275c commit 438a008
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 2 deletions.
32 changes: 32 additions & 0 deletions src/psp/guest_request_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ pub const SNP_GUEST_REQ_ERR_BUSY: u64 = BIT!(33);
/// 0
pub const SNP_MSG_TYPE_INVALID: u8 = 0;
/// 5
pub const SNP_MSG_REPORT_REQ: u8 = 5;
/// 6
pub const SNP_MSG_REPORT_RSP: u8 = 6;

/// 1
const HDR_VERSION: u8 = 1;
Expand Down Expand Up @@ -738,4 +742,32 @@ impl SnpGuestRequestCmd {

result
}

/// Copy to buf the certificates obtained in the last extended report request
pub fn copy_from_data(&self, buf: VirtAddr, buf_size: usize) {
unsafe {
ptr::copy_nonoverlapping(
self.data_gva.as_mut_ptr::<u8>(),
buf.as_mut_ptr::<u8>(),
min(buf_size, SNP_GUEST_REQ_MAX_DATA_SIZE as usize),
);
}
}

/// Check if the first sz bytes of the data buffer are empty
pub fn is_data_bytes_empty(&self, sz: usize) -> bool {
let m: usize = min(sz, SNP_GUEST_REQ_MAX_DATA_SIZE);
let buf: *const [u8; SNP_GUEST_REQ_MAX_DATA_SIZE] =
self.data_gva.as_ptr() as *const [u8; SNP_GUEST_REQ_MAX_DATA_SIZE];
unsafe { (*buf)[..m].is_empty() }
}

/// Clear sz bytes from the data buffer
pub fn clear_data_bytes(&self, sz: usize) {
memset(
self.data_gva.as_mut_ptr::<u8>(),
0u8,
min(sz, SNP_GUEST_REQ_MAX_DATA_SIZE),
);
}
}
2 changes: 2 additions & 0 deletions src/psp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
pub mod guest_request_cmd;
/// Attestation report structures
pub mod msg_report;
/// SNP Guest Request services
pub mod request;
68 changes: 68 additions & 0 deletions src/psp/msg_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
* Claudio Carvalho <[email protected]>
*/

use crate::{getter_func, prints};

use alloc::boxed::Box;
use core::slice;

/// SnpReportRequest size
const REQUEST_SIZE: usize = core::mem::size_of::<SnpReportRequest>();

Expand All @@ -20,6 +25,24 @@ pub struct SnpReportRequest {
rsvd: [u8; 28usize],
}

impl SnpReportRequest {
pub fn new() -> Self {
Self {
user_data: [0u8; USER_DATA_SIZE],
vmpl: 0u32,
rsvd: [0u8; 28],
}
}

pub fn set_user_data(&mut self, data: &[u8; USER_DATA_SIZE]) {
self.user_data.copy_from_slice(data);
}

pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const _ as *const u8, REQUEST_SIZE) }
}
}

#[repr(C)]
#[repr(align(2048))]
#[derive(Debug, Copy, Clone)]
Expand All @@ -30,6 +53,51 @@ pub struct SnpReportResponse {
report: AttestationReport,
}

impl SnpReportResponse {
getter_func!(status, u32);
getter_func!(report_size, u32);
getter_func!(report, AttestationReport);

pub fn is_valid(&self) -> bool {
// Check status
if self.status != 0 {
prints!("ERR: Bad report status={}\n", { self.status });
return false;
}

const REPORT_SIZE: usize = core::mem::size_of::<AttestationReport>();

// Check report size
if self.report_size != REPORT_SIZE as u32 {
prints!(
"ERR: Report size {:#x}, but should be {:#x} bytes)\n",
{ self.report_size },
REPORT_SIZE
);
return false;
}

true
}
}

impl TryFrom<Box<[u8]>> for SnpReportResponse {
type Error = ();

fn try_from(payload: Box<[u8]>) -> Result<Self, Self::Error> {
let resp: SnpReportResponse = {
let (head, body, _tail) = unsafe { payload.align_to::<SnpReportResponse>() };
if !head.is_empty() {
prints!("ERR: Report response not aligned\n");
return Err(());
}
body[0]
};

Ok(resp)
}
}

// Converted tcb_version from enum to
// struct to make alignment simple.
#[repr(C, packed)]
Expand Down
126 changes: 124 additions & 2 deletions src/psp/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,138 @@
*/

use crate::cpu::vc_terminate_svsm_general;
use crate::psp::guest_request_cmd::SnpGuestRequestCmd;
use crate::protocols::error_codes::*;
use crate::psp::guest_request_cmd::{
SnpGuestRequestCmd, SNP_GUEST_REQ_INVALID_LEN, SNP_GUEST_REQ_MAX_DATA_SIZE, SNP_MSG_REPORT_REQ,
};
use crate::psp::msg_report::{SnpReportRequest, SnpReportResponse, USER_DATA_SIZE};
use crate::util::locking::{LockGuard, SpinLock};
use crate::prints;
use crate::{prints, ALIGN, ALIGNED, PAGE_COUNT, PAGE_SHIFT, PAGE_SIZE};

use alloc::boxed::Box;
use x86_64::VirtAddr;

/// SNP_GUEST_REQUEST Object
static GUEST_REQUEST_CMD: SpinLock<SnpGuestRequestCmd> = SpinLock::new(SnpGuestRequestCmd::new());

/// SnpReportRequest size
const REQUEST_SIZE: usize = core::mem::size_of::<SnpReportRequest>();

pub struct CertsBuf {
addr: VirtAddr,
size: usize,
}

impl CertsBuf {
pub fn new(va: VirtAddr, sz: usize) -> Self {
CertsBuf { addr: va, size: sz }
}
}

pub fn snp_guest_request_init() {
if GUEST_REQUEST_CMD.lock().init().is_err() {
prints!("ERR: Failed to initialize SNP_GUEST_REQUEST\n");
vc_terminate_svsm_general();
}
}

/// Request a vmpl0 attestation report to the platform security processor (PSP).
///
/// @user_data: data that will be included in the attestation report and signed
/// @psp_rc : PSP return code.
/// @certs_buf: Optional. Buffer to store the certificate chain needed to verify
/// the attestation report. Make sure to load the certificates from
/// from the host using the sev-guest tools.
///
/// It returns the SnpReportResponse if success, otherwise an error code.
///
/// Further information can be found in the Secure Nested Paging Firmware ABI
/// Specification, Chapter 7, subsection Attestation
pub fn get_report(
user_data: &[u8; USER_DATA_SIZE],
psp_rc: &mut u64,
mut certs_buf: Option<&mut CertsBuf>,
) -> Result<SnpReportResponse, u64> {
// The size of the SnpReportRequest structure needs to fit in the
// SnpGuestRequest.hdr.msg_size field, which is a u16.
let req_size: u16 = match u16::try_from(REQUEST_SIZE) {
Ok(sz) => sz,
Err(_) => {
prints!(
"ERR: BUG: Report request size={} is too big for u16\n",
REQUEST_SIZE
);
return Err(SVSM_ERR_PROTOCOL_BASE);
}
};

let mut cmd: LockGuard<SnpGuestRequestCmd> = GUEST_REQUEST_CMD.lock();
let extended: bool = certs_buf.is_some();

if extended {
// Get a mutable raw pointer, otherwise we will not be able to use certs_buf later again
let buf: &mut CertsBuf = certs_buf.as_mut().unwrap();

if buf.addr.is_null() || buf.size == 0 {
return Err(SVSM_ERR_INVALID_PARAMETER);
}
if buf.size > SNP_GUEST_REQ_MAX_DATA_SIZE as usize {
prints!("ERR: certs_buf_size={:#x} too big\n", { buf.size });
return Err(SVSM_ERR_INVALID_PARAMETER);
}
if !ALIGNED!({ buf.addr.as_u64() }, PAGE_SIZE) {
prints!("ERR: certs_buf_size={:#x} not page aligned\n", { buf.addr });
return Err(SVSM_ERR_INVALID_PARAMETER);
}
let npages: usize = PAGE_COUNT!({ buf.size as u64 }) as usize;
cmd.set_data_npages(npages);
cmd.clear_data_bytes(buf.size);
}

// Instantiate a vmpl0 report request
let mut req: SnpReportRequest = SnpReportRequest::new();
req.set_user_data(user_data);

let result: Result<Box<[u8]>, ()> = cmd.send_request(
SNP_MSG_REPORT_REQ,
extended,
req.as_slice(),
req_size,
psp_rc,
);

if result.is_err() {
if extended && *psp_rc == SNP_GUEST_REQ_INVALID_LEN {
let buf: &mut CertsBuf = certs_buf.as_mut().unwrap();
prints!("ERR: Certificate buffer is too small, {} bytes\n", {
buf.size
});
buf.size = (cmd.data_npages() << PAGE_SHIFT) as usize;
return Err(SVSM_ERR_INVALID_PARAMETER);
}

return Err(SVSM_ERR_PROTOCOL_BASE);
}

let message: Box<[u8]> = result.unwrap();
let resp: SnpReportResponse = match SnpReportResponse::try_from(message) {
Ok(r) => r,
Err(()) => return Err(SVSM_ERR_PROTOCOL_BASE),
};
if !resp.is_valid() {
return Err(SVSM_ERR_PROTOCOL_BASE);
}

// The sev-guest tools, in the host, are used to load the certificates needed to
// verify the attestation report. If they were not loaded (yet), print a warning.
if extended {
let buf: &mut CertsBuf = certs_buf.as_mut().unwrap();
if cmd.is_data_bytes_empty(buf.size) {
prints!("WARNING: Attestation report certificates not found.\n");
} else {
cmd.copy_from_data(buf.addr, buf.size);
}
}

Ok(resp)
}

0 comments on commit 438a008

Please sign in to comment.