#[cfg(target_os = "linux")]
use super::*;
use std::fmt::Debug;
use std::{error, io};
#[cfg(target_os = "linux")]
pub use super::Firmware;
use crate::firmware::linux::guest::types::_4K_PAGE;
pub use crate::firmware::linux::host::types::{PlatformStatusFlags, SnpConfig, TcbVersion};
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub enum Indeterminate<T: Debug> {
Known(T),
Unknown,
}
#[derive(Debug)]
pub enum UserApiError {
FirmwareError(Error),
ApiError(SnpCertError),
UuidError(uuid::Error),
}
impl error::Error for UserApiError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
UserApiError::ApiError(uapi_error) => Some(uapi_error),
UserApiError::FirmwareError(firmware_error) => Some(firmware_error),
UserApiError::UuidError(uuid_error) => Some(uuid_error),
}
}
}
impl std::fmt::Display for UserApiError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let err_msg: String = match self {
UserApiError::FirmwareError(error) => format!("Firmware Error Encountered: {error}"),
UserApiError::ApiError(error) => format!("Certificate Error Encountered: {error}"),
UserApiError::UuidError(error) => format!("UUID Error Encountered: {error}"),
};
write!(f, "{err_msg}")
}
}
impl std::convert::From<uuid::Error> for UserApiError {
fn from(uuid_error: uuid::Error) -> Self {
UserApiError::UuidError(uuid_error)
}
}
impl std::convert::From<Error> for UserApiError {
fn from(firmware_error: Error) -> Self {
UserApiError::FirmwareError(firmware_error)
}
}
impl std::convert::From<std::io::Error> for UserApiError {
fn from(io_error: std::io::Error) -> Self {
UserApiError::FirmwareError(Error::IoError(io_error))
}
}
impl std::convert::From<SnpCertError> for UserApiError {
fn from(cert_error: SnpCertError) -> Self {
UserApiError::ApiError(cert_error)
}
}
#[derive(Debug)]
pub enum SnpCertError {
InvalidGUID,
PageMisalignment,
BufferOverflow,
UnknownError,
}
impl std::fmt::Display for SnpCertError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
SnpCertError::InvalidGUID => write!(f, "Invalid GUID provided in certificate chain."),
SnpCertError::PageMisalignment => {
write!(f, "Certificate Buffer not aligned with 4K Pages.")
}
SnpCertError::BufferOverflow => {
write!(f, "Buffer overflow prevented: Bytes provided exceed space allocated for the buffer provided.")
}
SnpCertError::UnknownError => {
write!(f, "Unknown Error encountered within the certificate chain.")
}
}
}
}
impl error::Error for SnpCertError {}
#[derive(Debug)]
pub enum Error {
IoError(io::Error),
InvalidPlatformState,
InvalidGuestState,
InvalidConfig,
InvalidLen,
AlreadyOwned,
InvalidCertificate,
PolicyFailure,
Inactive,
InvalidAddress,
BadSignature,
BadMeasurement,
AsidOwned,
InvalidAsid,
WbinvdRequired,
DfFlushRequired,
InvalidGuest,
InvalidCommand,
Active,
HardwarePlatform,
HardwareUnsafe,
Unsupported,
InvalidParam,
ResourceLimit,
SecureDataInvalid,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let err_description = match self {
Error::IoError(_) => "I/O Error",
Error::InvalidPlatformState => "Invalid platform state",
Error::InvalidGuestState => "Invalid guest state",
Error::InvalidConfig => "Platform configuration invalid",
Error::InvalidLen => "Memory buffer too small",
Error::AlreadyOwned => "Platform is already owned",
Error::InvalidCertificate => "Invalid certificate",
Error::PolicyFailure => "Policy failure",
Error::Inactive => "Guest is inactive",
Error::InvalidAddress => "Provided address is invalid",
Error::BadSignature => "Provided signature is invalid",
Error::BadMeasurement => "Provided measurement is invalid",
Error::AsidOwned => "ASID is already owned",
Error::InvalidAsid => "ASID is invalid",
Error::WbinvdRequired => "WBINVD instruction required",
Error::DfFlushRequired => "DF_FLUSH invocation required",
Error::InvalidGuest => "Guest handle is invalid",
Error::InvalidCommand => "Issued command is invalid",
Error::Active => "Guest is active",
Error::HardwarePlatform => {
"Hardware condition occured, safe to re-allocate parameter buffers"
}
Error::HardwareUnsafe => {
"Hardware condition occured, unsafe to re-allocate parameter buffers"
}
Error::Unsupported => "Feature is unsupported",
Error::InvalidParam => "Given parameter is invalid",
Error::ResourceLimit => {
"SEV firmware has run out of required resources to carry out command"
}
Error::SecureDataInvalid => "SEV platform observed a failed integrity check",
};
write!(f, "{err_description}")
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::IoError(e) => Some(e),
_ => None,
}
}
}
impl From<io::Error> for Error {
#[inline]
fn from(error: io::Error) -> Error {
Error::IoError(error)
}
}
impl From<io::Error> for Indeterminate<Error> {
#[inline]
fn from(error: io::Error) -> Indeterminate<Error> {
Indeterminate::Known(error.into())
}
}
impl From<Indeterminate<Error>> for io::Error {
#[inline]
fn from(indeterminate: Indeterminate<Error>) -> io::Error {
match indeterminate {
Indeterminate::Known(e) => io::Error::new(io::ErrorKind::Other, e),
Indeterminate::Unknown => io::Error::new(io::ErrorKind::Other, "unknown SEV error"),
}
}
}
impl From<u32> for Indeterminate<Error> {
#[inline]
fn from(error: u32) -> Indeterminate<Error> {
Indeterminate::Known(match error {
0 => io::Error::last_os_error().into(),
1 => Error::InvalidPlatformState,
2 => Error::InvalidGuestState,
3 => Error::InvalidConfig,
4 => Error::InvalidLen,
5 => Error::AlreadyOwned,
6 => Error::InvalidCertificate,
7 => Error::PolicyFailure,
8 => Error::Inactive,
9 => Error::InvalidAddress,
10 => Error::BadSignature,
11 => Error::BadMeasurement,
12 => Error::AsidOwned,
13 => Error::InvalidAsid,
14 => Error::WbinvdRequired,
15 => Error::DfFlushRequired,
16 => Error::InvalidGuest,
17 => Error::InvalidCommand,
18 => Error::Active,
19 => Error::HardwarePlatform,
20 => Error::HardwareUnsafe,
21 => Error::Unsupported,
22 => Error::InvalidParam,
23 => Error::ResourceLimit,
24 => Error::SecureDataInvalid,
_ => return Indeterminate::Unknown,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum State {
Uninitialized,
Initialized,
Working,
}
impl std::fmt::Display for State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let state = match self {
State::Uninitialized => "uninitialized",
State::Initialized => "initialized",
State::Working => "working",
};
write!(f, "{state}")
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Status {
pub build: Build,
pub state: State,
pub flags: PlatformStatusFlags,
pub guests: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Identifier(pub Vec<u8>);
impl From<Identifier> for Vec<u8> {
fn from(id: Identifier) -> Vec<u8> {
id.0
}
}
impl std::fmt::Display for Identifier {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for b in self.0.iter() {
write!(f, "{b:02X}")?;
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SnpTcbStatus {
pub platform_version: TcbVersion,
pub reported_version: TcbVersion,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SnpStatus {
pub build: SnpBuild,
pub state: State,
pub is_rmp_init: bool,
pub mask_chip_id: bool,
pub guests: u32,
pub tcb: SnpTcbStatus,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[repr(C)]
pub enum SnpCertType {
ARK,
ASK,
VCEK,
OTHER(String),
Empty,
}
impl SnpCertType {
fn from_guid(guid: &str) -> Self {
match guid {
"c0b406a4-a803-4952-9743-3fb6014cd0ae" => SnpCertType::ARK,
"4ab7b379-bbac-4fe4-a02f-05aef327c782" => SnpCertType::ASK,
"63da758d-e664-4564-adc5-f4b93be8accd" => SnpCertType::VCEK,
"00000000-0000-0000-0000-000000000000" => SnpCertType::Empty,
guid => SnpCertType::OTHER(guid.to_string()),
}
}
fn guid(&self) -> String {
match self {
SnpCertType::ARK => "c0b406a4-a803-4952-9743-3fb6014cd0ae",
SnpCertType::ASK => "4ab7b379-bbac-4fe4-a02f-05aef327c782",
SnpCertType::VCEK => "63da758d-e664-4564-adc5-f4b93be8accd",
SnpCertType::Empty => "00000000-0000-0000-0000-000000000000",
SnpCertType::OTHER(guid) => guid,
}
.to_string()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[repr(C)]
pub struct CertTableEntry {
pub cert_type: SnpCertType,
pub data: Vec<u8>,
}
impl CertTableEntry {
pub fn guid(&self) -> String {
self.cert_type.guid()
}
pub fn data(&self) -> &[u8] {
self.data.as_slice()
}
pub fn from_guid(guid: &str, data: Vec<u8>) -> Self {
Self {
cert_type: SnpCertType::from_guid(guid),
data,
}
}
pub fn new(cert_type: SnpCertType, data: Vec<u8>) -> Self {
Self { cert_type, data }
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SnpExtConfig {
pub config: Option<SnpConfig>,
pub certs: Option<Vec<CertTableEntry>>,
pub certs_len: u32,
}
fn round_to_whole_pages(size: usize) -> usize {
match size % _4K_PAGE {
0 => size,
rem => size + (_4K_PAGE - rem),
}
}
impl SnpExtConfig {
pub fn update_certs_only(certificates: Vec<CertTableEntry>) -> Result<Self, SnpCertError> {
let certs_length: usize = certificates.iter().map(|entry| entry.data().len()).sum();
let certs_len: u32 = round_to_whole_pages(certs_length) as u32;
Ok(Self {
config: None,
certs: Some(certificates),
certs_len,
})
}
}
#[cfg(test)]
mod test {
use super::{CertTableEntry, SnpCertType, SnpConfig, SnpExtConfig};
use crate::firmware::linux::host::types::TcbVersion;
fn build_ext_config() -> SnpExtConfig {
let test_cfg: SnpConfig = SnpConfig::new(TcbVersion::new(2, 0, 6, 39), 31);
let cert_table: Vec<CertTableEntry> =
vec![CertTableEntry::new(SnpCertType::ARK, vec![1; 28])];
SnpExtConfig {
config: Some(test_cfg),
certs: Some(cert_table),
certs_len: 4096,
}
}
#[test]
fn snp_ext_config_get_config() {
let expected_data: SnpConfig = SnpConfig::new(TcbVersion::new(2, 0, 6, 39), 31);
let cfg: SnpExtConfig = build_ext_config();
assert_eq!(cfg.config, Some(expected_data));
}
#[test]
fn snp_ext_config_get_certs() {
let cert_table: Vec<CertTableEntry> =
vec![CertTableEntry::new(SnpCertType::ARK, vec![1; 28])];
let cfg: SnpExtConfig = build_ext_config();
assert_eq!(cfg.certs, Some(cert_table));
}
}