From c8c64ad6a68379276c04d94db78c4043bcd02d45 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Thu, 23 Mar 2023 08:55:06 +0200 Subject: [PATCH] protocols: Add services manifest handling Introduce a global SERVICES object to register SVSM services and their data, and functionality to serialize the services list to a binary manifest according to the SVSM spec. Signed-off-by: Dov Murik --- src/protocols/mod.rs | 3 + src/protocols/services_manifest.rs | 158 +++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 src/protocols/services_manifest.rs diff --git a/src/protocols/mod.rs b/src/protocols/mod.rs index 39bfc5a..55b58ab 100644 --- a/src/protocols/mod.rs +++ b/src/protocols/mod.rs @@ -9,8 +9,11 @@ pub mod core; /// Error codes returned from the SVSM calls pub mod error_codes; +/// Services manifest table +pub mod services_manifest; pub use crate::protocols::core::*; +pub use crate::protocols::services_manifest::*; /// 0 pub const SVSM_CORE_PROTOCOL: u32 = ProtocolId::ProtocolId0 as u32; diff --git a/src/protocols/services_manifest.rs b/src/protocols/services_manifest.rs new file mode 100644 index 0000000..d27080b --- /dev/null +++ b/src/protocols/services_manifest.rs @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2023 IBM Corporation + * + * Authors: Dov Murik + */ + +use crate::locking::SpinLock; + +use alloc::vec::Vec; +use core::mem::size_of; +use core::slice; +use lazy_static::lazy_static; +use uuid::{uuid, Uuid}; + +const SERVICES_MANIFEST_HEADER_UUID: Uuid = uuid!("63849ebb-3d92-4670-a1ff-58f9c94b87bb"); + +/// SVSM Spec Chapter 7 (Attestation): Table 12: Services Manifest +#[allow(dead_code)] +#[repr(C, packed)] +struct ManifestHeader { + guid: uuid::Bytes, + size: u32, + num_services: u32, +} + +impl ManifestHeader { + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self as *const Self as *const u8, size_of::()) } + } +} + +/// SVSM Spec Chapter 7 (Attestation): Table 12: Services Manifest +#[allow(dead_code)] +#[repr(C, packed)] +struct ServiceEntry { + guid: uuid::Bytes, + data_offset: u32, + data_size: u32, +} + +impl ServiceEntry { + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self as *const Self as *const u8, size_of::()) } + } +} + +struct Service { + guid: Uuid, + data: Vec, +} + +pub struct Services { + list: Vec, +} + +impl Services { + pub const fn new() -> Self { + Services { list: Vec::new() } + } + + pub fn add_service(&mut self, guid: Uuid, data: &[u8]) { + self.list.push(Service { + guid, + data: data.to_vec(), + }); + } + + /// Serialize the services manifest. Set `single_service_guid` to `None` to + /// include all services in the manifest, or to `Some(guid)` to include only + /// a single service. + pub fn get_manifest( + &self, + single_service_guid: Option, + _manifest_version: Option, + ) -> Vec { + let mut service_entries: Vec = Vec::new(); + let data_start_offset: usize = size_of::() as usize + + (size_of::() as usize * self.list.len()); + let mut data: Vec = Vec::new(); + for service in &self.list { + if let Some(filter_guid) = single_service_guid { + if service.guid != filter_guid { + continue; + } + } + let entry: ServiceEntry = ServiceEntry { + guid: service.guid.to_bytes_le(), + data_offset: (data_start_offset + data.len()) as u32, + data_size: service.data.len() as u32, + }; + service_entries.extend_from_slice(entry.as_bytes()); + data.extend_from_slice(&service.data); + } + let total_size: usize = + size_of::() as usize + service_entries.len() + data.len(); + let header: ManifestHeader = ManifestHeader { + guid: SERVICES_MANIFEST_HEADER_UUID.to_bytes_le(), + size: total_size as u32, + num_services: self.list.len() as u32, + }; + let mut res: Vec = Vec::with_capacity(total_size); + res.extend_from_slice(header.as_bytes()); + res.extend_from_slice(&service_entries); + res.extend_from_slice(&data); + res + } +} + +lazy_static! { + /// Global registry of services + pub static ref SERVICES: SpinLock = SpinLock::new(Services::new()); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_serialize_empty_manifest() { + let s: Services = Services::new(); + let b: Vec = s.get_manifest(None, None); + assert_eq!(b.len(), 24); + } + + #[test] + pub fn test_serialize_manifest_with_services_and_data() { + let mut s: Services = Services::new(); + s.add_service( + uuid!("11112222-1234-5678-9abc-ddddeeeeffff"), + b"TheServiceData", + ); + s.add_service( + uuid!("88889999-8888-9999-8888-999988889999"), + b"OtherServiceData", + ); + let b: Vec = s.get_manifest(None, None); + assert_eq!(b.len(), 24 + 24 + 24 + 14 + 16); + assert_eq!(&b[72..86], b"TheServiceData"); + assert_eq!(&b[86..], b"OtherServiceData"); + } + + #[test] + pub fn test_serialize_single_service_manifest() { + let mut s: Services = Services::new(); + s.add_service( + uuid!("11112222-1234-5678-9abc-ddddeeeeffff"), + b"TheServiceData", + ); + s.add_service( + uuid!("88889999-8888-9999-8888-999988889999"), + b"OtherServiceData", + ); + let b: Vec = s.get_manifest(Some(uuid!("11112222-1234-5678-9abc-ddddeeeeffff")), None); + assert_eq!(b.len(), 24 + 24 + 14); + assert_eq!(&b[48..], b"TheServiceData"); + } +}