Skip to content

Commit 438a008

Browse files
committed
psp/request: Add get_report()
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 <cclaudio@linux.ibm.com>
1 parent 5de275c commit 438a008

4 files changed

Lines changed: 226 additions & 2 deletions

File tree

src/psp/guest_request_cmd.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ pub const SNP_GUEST_REQ_ERR_BUSY: u64 = BIT!(33);
5454
5555
/// 0
5656
pub const SNP_MSG_TYPE_INVALID: u8 = 0;
57+
/// 5
58+
pub const SNP_MSG_REPORT_REQ: u8 = 5;
59+
/// 6
60+
pub const SNP_MSG_REPORT_RSP: u8 = 6;
5761

5862
/// 1
5963
const HDR_VERSION: u8 = 1;
@@ -738,4 +742,32 @@ impl SnpGuestRequestCmd {
738742

739743
result
740744
}
745+
746+
/// Copy to buf the certificates obtained in the last extended report request
747+
pub fn copy_from_data(&self, buf: VirtAddr, buf_size: usize) {
748+
unsafe {
749+
ptr::copy_nonoverlapping(
750+
self.data_gva.as_mut_ptr::<u8>(),
751+
buf.as_mut_ptr::<u8>(),
752+
min(buf_size, SNP_GUEST_REQ_MAX_DATA_SIZE as usize),
753+
);
754+
}
755+
}
756+
757+
/// Check if the first sz bytes of the data buffer are empty
758+
pub fn is_data_bytes_empty(&self, sz: usize) -> bool {
759+
let m: usize = min(sz, SNP_GUEST_REQ_MAX_DATA_SIZE);
760+
let buf: *const [u8; SNP_GUEST_REQ_MAX_DATA_SIZE] =
761+
self.data_gva.as_ptr() as *const [u8; SNP_GUEST_REQ_MAX_DATA_SIZE];
762+
unsafe { (*buf)[..m].is_empty() }
763+
}
764+
765+
/// Clear sz bytes from the data buffer
766+
pub fn clear_data_bytes(&self, sz: usize) {
767+
memset(
768+
self.data_gva.as_mut_ptr::<u8>(),
769+
0u8,
770+
min(sz, SNP_GUEST_REQ_MAX_DATA_SIZE),
771+
);
772+
}
741773
}

src/psp/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
pub mod guest_request_cmd;
1111
/// Attestation report structures
1212
pub mod msg_report;
13+
/// SNP Guest Request services
14+
pub mod request;

src/psp/msg_report.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
* Claudio Carvalho <cclaudio@linux.ibm.com>
77
*/
88

9+
use crate::{getter_func, prints};
10+
11+
use alloc::boxed::Box;
12+
use core::slice;
13+
914
/// SnpReportRequest size
1015
const REQUEST_SIZE: usize = core::mem::size_of::<SnpReportRequest>();
1116

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

28+
impl SnpReportRequest {
29+
pub fn new() -> Self {
30+
Self {
31+
user_data: [0u8; USER_DATA_SIZE],
32+
vmpl: 0u32,
33+
rsvd: [0u8; 28],
34+
}
35+
}
36+
37+
pub fn set_user_data(&mut self, data: &[u8; USER_DATA_SIZE]) {
38+
self.user_data.copy_from_slice(data);
39+
}
40+
41+
pub fn as_slice(&self) -> &[u8] {
42+
unsafe { slice::from_raw_parts(self as *const _ as *const u8, REQUEST_SIZE) }
43+
}
44+
}
45+
2346
#[repr(C)]
2447
#[repr(align(2048))]
2548
#[derive(Debug, Copy, Clone)]
@@ -30,6 +53,51 @@ pub struct SnpReportResponse {
3053
report: AttestationReport,
3154
}
3255

56+
impl SnpReportResponse {
57+
getter_func!(status, u32);
58+
getter_func!(report_size, u32);
59+
getter_func!(report, AttestationReport);
60+
61+
pub fn is_valid(&self) -> bool {
62+
// Check status
63+
if self.status != 0 {
64+
prints!("ERR: Bad report status={}\n", { self.status });
65+
return false;
66+
}
67+
68+
const REPORT_SIZE: usize = core::mem::size_of::<AttestationReport>();
69+
70+
// Check report size
71+
if self.report_size != REPORT_SIZE as u32 {
72+
prints!(
73+
"ERR: Report size {:#x}, but should be {:#x} bytes)\n",
74+
{ self.report_size },
75+
REPORT_SIZE
76+
);
77+
return false;
78+
}
79+
80+
true
81+
}
82+
}
83+
84+
impl TryFrom<Box<[u8]>> for SnpReportResponse {
85+
type Error = ();
86+
87+
fn try_from(payload: Box<[u8]>) -> Result<Self, Self::Error> {
88+
let resp: SnpReportResponse = {
89+
let (head, body, _tail) = unsafe { payload.align_to::<SnpReportResponse>() };
90+
if !head.is_empty() {
91+
prints!("ERR: Report response not aligned\n");
92+
return Err(());
93+
}
94+
body[0]
95+
};
96+
97+
Ok(resp)
98+
}
99+
}
100+
33101
// Converted tcb_version from enum to
34102
// struct to make alignment simple.
35103
#[repr(C, packed)]

src/psp/request.rs

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,138 @@
77
*/
88

99
use crate::cpu::vc_terminate_svsm_general;
10-
use crate::psp::guest_request_cmd::SnpGuestRequestCmd;
10+
use crate::protocols::error_codes::*;
11+
use crate::psp::guest_request_cmd::{
12+
SnpGuestRequestCmd, SNP_GUEST_REQ_INVALID_LEN, SNP_GUEST_REQ_MAX_DATA_SIZE, SNP_MSG_REPORT_REQ,
13+
};
14+
use crate::psp::msg_report::{SnpReportRequest, SnpReportResponse, USER_DATA_SIZE};
1115
use crate::util::locking::{LockGuard, SpinLock};
12-
use crate::prints;
16+
use crate::{prints, ALIGN, ALIGNED, PAGE_COUNT, PAGE_SHIFT, PAGE_SIZE};
17+
18+
use alloc::boxed::Box;
19+
use x86_64::VirtAddr;
1320

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

24+
/// SnpReportRequest size
25+
const REQUEST_SIZE: usize = core::mem::size_of::<SnpReportRequest>();
26+
27+
pub struct CertsBuf {
28+
addr: VirtAddr,
29+
size: usize,
30+
}
31+
32+
impl CertsBuf {
33+
pub fn new(va: VirtAddr, sz: usize) -> Self {
34+
CertsBuf { addr: va, size: sz }
35+
}
36+
}
37+
1738
pub fn snp_guest_request_init() {
1839
if GUEST_REQUEST_CMD.lock().init().is_err() {
1940
prints!("ERR: Failed to initialize SNP_GUEST_REQUEST\n");
2041
vc_terminate_svsm_general();
2142
}
2243
}
44+
45+
/// Request a vmpl0 attestation report to the platform security processor (PSP).
46+
///
47+
/// @user_data: data that will be included in the attestation report and signed
48+
/// @psp_rc : PSP return code.
49+
/// @certs_buf: Optional. Buffer to store the certificate chain needed to verify
50+
/// the attestation report. Make sure to load the certificates from
51+
/// from the host using the sev-guest tools.
52+
///
53+
/// It returns the SnpReportResponse if success, otherwise an error code.
54+
///
55+
/// Further information can be found in the Secure Nested Paging Firmware ABI
56+
/// Specification, Chapter 7, subsection Attestation
57+
pub fn get_report(
58+
user_data: &[u8; USER_DATA_SIZE],
59+
psp_rc: &mut u64,
60+
mut certs_buf: Option<&mut CertsBuf>,
61+
) -> Result<SnpReportResponse, u64> {
62+
// The size of the SnpReportRequest structure needs to fit in the
63+
// SnpGuestRequest.hdr.msg_size field, which is a u16.
64+
let req_size: u16 = match u16::try_from(REQUEST_SIZE) {
65+
Ok(sz) => sz,
66+
Err(_) => {
67+
prints!(
68+
"ERR: BUG: Report request size={} is too big for u16\n",
69+
REQUEST_SIZE
70+
);
71+
return Err(SVSM_ERR_PROTOCOL_BASE);
72+
}
73+
};
74+
75+
let mut cmd: LockGuard<SnpGuestRequestCmd> = GUEST_REQUEST_CMD.lock();
76+
let extended: bool = certs_buf.is_some();
77+
78+
if extended {
79+
// Get a mutable raw pointer, otherwise we will not be able to use certs_buf later again
80+
let buf: &mut CertsBuf = certs_buf.as_mut().unwrap();
81+
82+
if buf.addr.is_null() || buf.size == 0 {
83+
return Err(SVSM_ERR_INVALID_PARAMETER);
84+
}
85+
if buf.size > SNP_GUEST_REQ_MAX_DATA_SIZE as usize {
86+
prints!("ERR: certs_buf_size={:#x} too big\n", { buf.size });
87+
return Err(SVSM_ERR_INVALID_PARAMETER);
88+
}
89+
if !ALIGNED!({ buf.addr.as_u64() }, PAGE_SIZE) {
90+
prints!("ERR: certs_buf_size={:#x} not page aligned\n", { buf.addr });
91+
return Err(SVSM_ERR_INVALID_PARAMETER);
92+
}
93+
let npages: usize = PAGE_COUNT!({ buf.size as u64 }) as usize;
94+
cmd.set_data_npages(npages);
95+
cmd.clear_data_bytes(buf.size);
96+
}
97+
98+
// Instantiate a vmpl0 report request
99+
let mut req: SnpReportRequest = SnpReportRequest::new();
100+
req.set_user_data(user_data);
101+
102+
let result: Result<Box<[u8]>, ()> = cmd.send_request(
103+
SNP_MSG_REPORT_REQ,
104+
extended,
105+
req.as_slice(),
106+
req_size,
107+
psp_rc,
108+
);
109+
110+
if result.is_err() {
111+
if extended && *psp_rc == SNP_GUEST_REQ_INVALID_LEN {
112+
let buf: &mut CertsBuf = certs_buf.as_mut().unwrap();
113+
prints!("ERR: Certificate buffer is too small, {} bytes\n", {
114+
buf.size
115+
});
116+
buf.size = (cmd.data_npages() << PAGE_SHIFT) as usize;
117+
return Err(SVSM_ERR_INVALID_PARAMETER);
118+
}
119+
120+
return Err(SVSM_ERR_PROTOCOL_BASE);
121+
}
122+
123+
let message: Box<[u8]> = result.unwrap();
124+
let resp: SnpReportResponse = match SnpReportResponse::try_from(message) {
125+
Ok(r) => r,
126+
Err(()) => return Err(SVSM_ERR_PROTOCOL_BASE),
127+
};
128+
if !resp.is_valid() {
129+
return Err(SVSM_ERR_PROTOCOL_BASE);
130+
}
131+
132+
// The sev-guest tools, in the host, are used to load the certificates needed to
133+
// verify the attestation report. If they were not loaded (yet), print a warning.
134+
if extended {
135+
let buf: &mut CertsBuf = certs_buf.as_mut().unwrap();
136+
if cmd.is_data_bytes_empty(buf.size) {
137+
prints!("WARNING: Attestation report certificates not found.\n");
138+
} else {
139+
cmd.copy_from_data(buf.addr, buf.size);
140+
}
141+
}
142+
143+
Ok(resp)
144+
}

0 commit comments

Comments
 (0)