From 0ec4e9e760c7fec66de57da98109c00d07e186ea Mon Sep 17 00:00:00 2001 From: DGonzalezVillal Date: Wed, 28 Aug 2024 20:00:23 +0000 Subject: [PATCH] Changing Firmware error structure to use SEV errors and modifying IOCTL calls to bubble correct errors The AMD PSP provides defined SEV errors in the API with clear codes and mistakes. The library currently only bubbles up IO errors and not the correct SEV error when IOCTL calls fail. This commit rearranges the error file to use SEV errors correctly, and then changes the way IOCTL calls returns errors, so that the appropriate SEV error is bubbled up when an error is encountered. Guest IOCTL calls do not fail the same way host IOCTLS do. You need to manually check for failures even if the ioctl function itself might not return an error. Signed-off-by: DGonzalezVillal --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/error.rs | 720 +++++++++++++++--------------- src/firmware/guest/mod.rs | 148 +++--- src/firmware/host/mod.rs | 91 ++-- src/firmware/linux/guest/ioctl.rs | 6 +- src/firmware/linux/host/ioctl.rs | 7 + src/launch/linux/ioctl.rs | 14 +- src/launch/sev.rs | 58 ++- src/launch/snp.rs | 32 +- src/lib.rs | 7 +- tests/api.rs | 10 + tests/guest.rs | 22 + 13 files changed, 610 insertions(+), 509 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f17ea88a..bd5c1041 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -864,7 +864,7 @@ dependencies = [ [[package]] name = "sev" -version = "4.0.0" +version = "5.0.0" dependencies = [ "base64", "bincode", diff --git a/Cargo.toml b/Cargo.toml index 75c61576..dd13bb53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sev" -version = "4.0.0" +version = "5.0.0" authors = [ "Nathaniel McCallum ", "The VirTEE Project Developers", diff --git a/src/error.rs b/src/error.rs index dc61a513..57d41ac4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -139,7 +139,7 @@ impl From for (u32, u32) { } } -impl From for (VmmError, Indeterminate) { +impl From for (VmmError, SevError) { fn from(value: RawFwError) -> Self { ((value.0 >> 0x20).into(), value.0.into()) } @@ -151,31 +151,375 @@ impl Display for RawFwError { } } +/// Error conditions returned by the SEV platform or by layers above it +/// (i.e., the Linux kernel). +/// +/// These error conditions are documented in the AMD SEV API spec, but +/// their documentation has been copied here for completeness. +#[derive(Debug, Clone, Copy, PartialEq)] +#[repr(u32)] +pub enum SevError { + /// The platform state is invalid for this command. + InvalidPlatformState = 0x0001, + + /// The guest state is invalid for this command. + InvalidGuestState = 0x0002, + + /// The platform configuration is invalid. + InvalidConfig = 0x0003, + + /// A memory buffer is too small. + InvalidLen = 0x0004, + + /// The platform is already owned. + AlreadyOwned = 0x0005, + + /// The certificate is invalid. + InvalidCertificate = 0x0006, + + /// Request is not allowed by guest policy. + PolicyFailure = 0x0007, + + /// The guest is inactive. + Inactive = 0x0008, + + /// The address provided is invalid. + InvalidAddress = 0x0009, + + /// The provided signature is invalid. + BadSignature = 0x000A, + + /// The provided measurement is invalid. + BadMeasurement = 0x000B, + + /// The ASID is already owned. + AsidOwned = 0x000C, + + /// The ASID is invalid. + InvalidAsid = 0x000D, + + /// WBINVD instruction required. + WbinvdRequired = 0x000E, + + /// `DF_FLUSH` invocation required. + DfFlushRequired = 0x000F, + + /// The guest handle is invalid. + InvalidGuest = 0x0010, + + /// The command issued is invalid. + InvalidCommand = 0x0011, + + /// The guest is active. + Active = 0x0012, + + /// A hardware condition has occurred affecting the platform. It is safe + /// to re-allocate parameter buffers. + HardwarePlatform = 0x0013, + + /// A hardware condition has occurred affecting the platform. Re-allocating + /// parameter buffers is not safe. + HardwareUnsafe = 0x0014, + + /// Feature is unsupported. + Unsupported = 0x0015, + + /// A given parameter is invalid. + InvalidParam = 0x0016, + + /// The SEV firmware has run out of a resource required to carry out the + /// command. + ResourceLimit = 0x0017, + + /// The SEV platform observed a failed integrity check. + SecureDataInvalid = 0x0018, + + /// The RMP page size is incorrect. + InvalidPageSize = 0x0019, + + /// The RMP page state is incorrect + InvalidPageState = 0x001A, + + /// The metadata entry is invalid. + InvalidMdataEntry = 0x001B, + + /// The page ownership is incorrect + InvalidPageOwner = 0x001C, + + /// The AEAD algorithm would have overflowed + AEADOFlow = 0x001D, + + /// A Mailbox mode command was sent while the SEV FW was in Ring Buffer + /// mode. Ring Buffer mode has been exited; the Mailbox mode command + /// has been ignored. Retry is recommended. + RbModeExited = 0x001F, // 0x001F + + /// The RMP must be reinitialized. + RMPInitRequired = 0x0020, // 0x0020 + + /// SVN of provided image is lower than the committed SVN. + BadSvn = 0x0021, + + /// Firmware version anti-rollback. + BadVersion = 0x0022, + + /// An invocation of SNP_SHUTDOWN is required to complete this action. + ShutdownRequired = 0x0023, + + /// Update of the firmware internal state or a guest context page has failed. + UpdateFailed = 0x0024, + + /// Installation of the committed firmware image required + RestoreRequired = 0x0025, + + /// The RMP initialization failed. + RMPInitFailed = 0x0026, + + /// The key requested is invalid, not present, or not allowed. + InvalidKey = 0x0027, + + /// Unknown status code + UnknownError = 0x0000, +} + +impl std::fmt::Display for SevError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let code = *self as u32; + match self { + SevError::InvalidPlatformState => write!(f,"Status Code: 0x{:x}: Invalid platform state.", code), + SevError::InvalidGuestState => write!(f,"Status Code: 0x{:x}: Invalid guest state.", code), + SevError::InvalidConfig => write!(f,"Status Code: 0x{:x}: Platform configuration invalid.", code), + SevError::InvalidLen => write!(f,"Status Code: 0x{:x}: Memory buffer too small.", code), + SevError::AlreadyOwned => write!(f,"Status Code: 0x{:x}: Platform is already owned.", code), + SevError::InvalidCertificate => write!(f,"Status Code: 0x{:x}: Invalid certificate.", code), + SevError::PolicyFailure => write!(f,"Status Code: 0x{:x}: Policy failure.", code), + SevError::Inactive => write!(f,"Status Code: 0x{:x}: Guest is inactive.", code), + SevError::InvalidAddress => write!(f,"Status Code: 0x{:x}: Provided address is invalid.", code), + SevError::BadSignature => write!(f,"Status Code: 0x{:x}: Provided signature is invalid.", code), + SevError::BadMeasurement => write!(f,"Status Code: 0x{:x}: Provided measurement is invalid.", code), + SevError::AsidOwned => write!(f,"Status Code: 0x{:x}: ASID is already owned.", code), + SevError::InvalidAsid => write!(f,"Status Code: 0x{:x}: ASID is invalid.", code), + SevError::WbinvdRequired => write!(f,"Status Code: 0x{:x}: WBINVD instruction required.", code), + SevError::DfFlushRequired => write!(f,"Status Code: 0x{:x}: DF_FLUSH invocation required.", code), + SevError::InvalidGuest => write!(f,"Status Code: 0x{:x}: Guest handle is invalid.", code), + SevError::InvalidCommand => write!(f,"Status Code: 0x{:x}: Issued command is invalid.", code), + SevError::Active => write!(f,"Status Code: 0x{:x}: Guest is active.", code), + SevError::HardwarePlatform => { + write!(f,"Status Code: 0x{:x}: Hardware condition occured, safe to re-allocate parameter buffers.", code) + } + SevError::HardwareUnsafe => { + write!(f,"Status Code: 0x{:x}: Hardware condition occured, unsafe to re-allocate parameter buffers.", code) + } + SevError::Unsupported => write!(f,"Status Code: 0x{:x}: Feature is unsupported.", code), + SevError::InvalidParam => write!(f,"Status Code: 0x{:x}: Given parameter is invalid.", code), + SevError::ResourceLimit => { + write!(f,"Status Code: 0x{:x}: SEV firmware has run out of required resources to carry out command.", code) + } + SevError::SecureDataInvalid => write!(f,"Status Code: 0x{:x}: SEV platform observed a failed integrity check.", code), + SevError::InvalidPageSize => write!(f,"Status Code: 0x{:x}: The RMP page size is incorrect.", code), + SevError::InvalidPageState => write!(f,"Status Code: 0x{:x}: The RMP page state is incorrect.", code), + SevError::InvalidMdataEntry => write!(f,"Status Code: 0x{:x}: The metadata entry is invalid.", code), + SevError::InvalidPageOwner => write!(f,"Status Code: 0x{:x}: The page ownership is incorrect.", code), + SevError::AEADOFlow => write!(f,"Status Code: 0x{:x}: The AEAD algorithm would have overflowed.", code), + SevError::RbModeExited => write!(f,"Status Code: 0x{:x}: A Mailbox mode command was sent while the SEV FW was in Ring Buffer \ + mode. Ring Buffer mode has been exited; the Mailbox mode command has \ + been ignored. Retry is recommended.", code), + SevError::RMPInitRequired => write!(f,"Status Code: 0x{:x}: The RMP must be reinitialized.", code), + SevError::BadSvn => write!(f,"Status Code: 0x{:x}: SVN of provided image is lower than the committed SVN.", code), + SevError::BadVersion => write!(f,"Status Code: 0x{:x}: Firmware version anti-rollback.", code), + SevError::ShutdownRequired => write!(f,"Status Code: 0x{:x}: An invocation of SNP_SHUTDOWN is required to complete this action.", code), + SevError::UpdateFailed => write!(f,"Status Code: 0x{:x}: Update of the firmware internal state or a guest context page has failed.", code), + SevError::RestoreRequired => write!(f,"Status Code: 0x{:x}: Installation of the committed firmware image required.", code), + SevError::RMPInitFailed => write!(f,"Status Code: 0x{:x}: The RMP initialization failed.", code), + SevError::InvalidKey => write!(f,"Status Code: 0x{:x}: The key requested is invalid, not present, or not allowed.", code), + SevError::UnknownError => write!(f,"Unknown SEV Error"), + } + } +} + +impl From for SevError { + fn from(value: u64) -> Self { + Self::from(value as u32) + } +} + +impl From for SevError { + #[inline] + fn from(error: u32) -> SevError { + match error { + 0x01 => SevError::InvalidPlatformState, + 0x02 => SevError::InvalidGuestState, + 0x03 => SevError::InvalidConfig, + 0x04 => SevError::InvalidLen, + 0x05 => SevError::AlreadyOwned, + 0x06 => SevError::InvalidCertificate, + 0x07 => SevError::PolicyFailure, + 0x08 => SevError::Inactive, + 0x09 => SevError::InvalidAddress, + 0x0A => SevError::BadSignature, + 0x0B => SevError::BadMeasurement, + 0x0C => SevError::AsidOwned, + 0x0D => SevError::InvalidAsid, + 0x0E => SevError::WbinvdRequired, + 0x0F => SevError::DfFlushRequired, + 0x10 => SevError::InvalidGuest, + 0x11 => SevError::InvalidCommand, + 0x12 => SevError::Active, + 0x13 => SevError::HardwarePlatform, + 0x14 => SevError::HardwareUnsafe, + 0x15 => SevError::Unsupported, + 0x16 => SevError::InvalidParam, + 0x17 => SevError::ResourceLimit, + 0x18 => SevError::SecureDataInvalid, + 0x19 => SevError::InvalidPageSize, + 0x1A => SevError::InvalidPageState, + 0x1B => SevError::InvalidMdataEntry, + 0x1C => SevError::InvalidPageOwner, + 0x1D => SevError::AEADOFlow, + 0x1F => SevError::RbModeExited, + 0x20 => SevError::RMPInitRequired, + 0x21 => SevError::BadSvn, + 0x22 => SevError::BadVersion, + 0x23 => SevError::ShutdownRequired, + 0x24 => SevError::UpdateFailed, + 0x25 => SevError::RestoreRequired, + 0x26 => SevError::RMPInitFailed, + 0x27 => SevError::InvalidKey, + _ => SevError::UnknownError, + } + } +} + +impl From for c_int { + fn from(err: SevError) -> Self { + match err { + SevError::InvalidPlatformState => 0x01, + SevError::InvalidGuestState => 0x02, + SevError::InvalidConfig => 0x03, + SevError::InvalidLen => 0x04, + SevError::AlreadyOwned => 0x05, + SevError::InvalidCertificate => 0x06, + SevError::PolicyFailure => 0x07, + SevError::Inactive => 0x08, + SevError::InvalidAddress => 0x09, + SevError::BadSignature => 0x0A, + SevError::BadMeasurement => 0x0B, + SevError::AsidOwned => 0x0C, + SevError::InvalidAsid => 0x0D, + SevError::WbinvdRequired => 0x0E, + SevError::DfFlushRequired => 0x0F, + SevError::InvalidGuest => 0x10, + SevError::InvalidCommand => 0x11, + SevError::Active => 0x12, + SevError::HardwarePlatform => 0x13, + SevError::HardwareUnsafe => 0x14, + SevError::Unsupported => 0x15, + SevError::InvalidParam => 0x16, + SevError::ResourceLimit => 0x17, + SevError::SecureDataInvalid => 0x18, + SevError::InvalidPageSize => 0x19, + SevError::InvalidPageState => 0x1A, + SevError::InvalidMdataEntry => 0x1B, + SevError::InvalidPageOwner => 0x1C, + SevError::AEADOFlow => 0x1D, + SevError::RbModeExited => 0x1F, + SevError::RMPInitRequired => 0x20, + SevError::BadSvn => 0x21, + SevError::BadVersion => 0x22, + SevError::ShutdownRequired => 0x23, + SevError::UpdateFailed => 0x24, + SevError::RestoreRequired => 0x25, + SevError::RMPInitFailed => 0x26, + SevError::InvalidKey => 0x27, + SevError::UnknownError => -1, + } + } +} + +impl std::error::Error for SevError {} + /// There are a number of error conditions that can occur between this /// layer all the way down to the SEV platform. Most of these cases have /// been enumerated; however, there is a possibility that some error /// conditions are not encapsulated here. #[derive(Debug)] -pub enum Indeterminate { +pub enum FirmwareError { /// The error condition is known. - Known(T), + KnownSevError(SevError), /// The error condition is unknown. - Unknown, + UnknownSevError(u32), + + /// IO Error + IoError(std::io::Error), } -impl From for Indeterminate { - /// Creates an easy - fn from(value: T) -> Self { - Self::Known(value) +impl error::Error for FirmwareError {} + +impl Display for FirmwareError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let err_description = match self { + FirmwareError::KnownSevError(error) => format!("Known SEV FW Error: {error}"), + FirmwareError::UnknownSevError(code) => { + format!("Unknown SEV FW Error Encountered: {code}") + } + FirmwareError::IoError(error) => format!("IO Error Encountered: {error}"), + }; + + write!(f, "{err_description}") + } +} + +impl std::convert::From for FirmwareError { + fn from(sev_error: SevError) -> Self { + match sev_error { + SevError::UnknownError => FirmwareError::UnknownSevError(sev_error as u32), + _ => FirmwareError::KnownSevError(sev_error), + } + } +} + +impl From for FirmwareError { + #[inline] + fn from(error: io::Error) -> FirmwareError { + FirmwareError::IoError(error) + } +} + +impl From for FirmwareError { + fn from(value: u64) -> Self { + Self::from(value as u32) + } +} + +impl From for FirmwareError { + #[inline] + fn from(error: u32) -> FirmwareError { + match error { + 0x00 => FirmwareError::IoError(io::Error::last_os_error()), + 0x01..0x027 => FirmwareError::KnownSevError(error.into()), + _ => FirmwareError::UnknownSevError(error), + } + } +} + +impl From for c_int { + fn from(err: FirmwareError) -> Self { + match err { + FirmwareError::UnknownSevError(_) | FirmwareError::IoError(_) => -0x01, + FirmwareError::KnownSevError(e) => e.into(), + } } } #[derive(Debug)] /// Wrapper Error for Firmware or User API Errors pub enum UserApiError { - /// Firmware related errors. - FirmwareError(Error), + /// Sev Firmware related errors. + FirmwareError(FirmwareError), + + /// IO related errors + IOError(io::Error), /// User API related errors. ApiError(CertError), @@ -201,6 +545,7 @@ impl error::Error for UserApiError { match self { Self::ApiError(uapi_error) => Some(uapi_error), Self::FirmwareError(firmware_error) => Some(firmware_error), + Self::IOError(io_error) => Some(io_error), Self::UuidError(uuid_error) => Some(uuid_error), Self::VmmError(vmm_error) => Some(vmm_error), Self::HashstickError(hashstick_error) => Some(hashstick_error), @@ -214,6 +559,7 @@ impl std::fmt::Display for UserApiError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let err_msg: String = match self { Self::FirmwareError(error) => format!("Firmware Error Encountered: {error}"), + Self::IOError(error) => format!("I/O Error Encountered: {error}"), Self::ApiError(error) => format!("Certificate Error Encountered: {error}"), Self::UuidError(error) => format!("UUID Error Encountered: {error}"), Self::VmmError(error) => format!("VMM Error Encountered: {error}"), @@ -243,15 +589,27 @@ impl std::convert::From for UserApiError { } } -impl std::convert::From for UserApiError { - fn from(firmware_error: Error) -> Self { +impl std::convert::From for UserApiError { + fn from(firmware_error: FirmwareError) -> Self { Self::FirmwareError(firmware_error) } } +impl std::convert::From for UserApiError { + fn from(sev_error: SevError) -> Self { + Self::FirmwareError(sev_error.into()) + } +} + impl std::convert::From for UserApiError { fn from(io_error: std::io::Error) -> Self { - Self::FirmwareError(Error::IoError(io_error)) + Self::IOError(io_error) + } +} + +impl From for io::Error { + fn from(value: UserApiError) -> Self { + io::Error::new(io::ErrorKind::Other, value) } } @@ -341,342 +699,6 @@ impl std::fmt::Display for CertError { impl error::Error for CertError {} -/// Error conditions returned by the SEV platform or by layers above it -/// (i.e., the Linux kernel). -/// -/// These error conditions are documented in the AMD SEV API spec, but -/// their documentation has been copied here for completeness. -#[derive(Debug)] -#[repr(u32)] -pub enum Error { - /// Something went wrong when communicating with the "outside world" - /// (kernel, SEV platform). - IoError(io::Error), - - /// The platform state is invalid for this command. - InvalidPlatformState, // 0x0001 - - /// The guest state is invalid for this command. - InvalidGuestState, // 0x0002 - - /// The platform configuration is invalid. - InvalidConfig, // 0x0003 - - /// A memory buffer is too small. - InvalidLen, // 0x0004 - - /// The platform is already owned. - AlreadyOwned, // 0x0005 - - /// The certificate is invalid. - InvalidCertificate, // 0x0006 - - /// Request is not allowed by guest policy. - PolicyFailure, // 0x0007 - - /// The guest is inactive. - Inactive, // 0x0008 - - /// The address provided is invalid. - InvalidAddress, // 0x0009 - - /// The provided signature is invalid. - BadSignature, // 0x000A - - /// The provided measurement is invalid. - BadMeasurement, // 0x000B - - /// The ASID is already owned. - AsidOwned, // 0x000C - - /// The ASID is invalid. - InvalidAsid, // 0x000D - - /// WBINVD instruction required. - WbinvdRequired, // 0x000E - - /// `DF_FLUSH` invocation required. - DfFlushRequired, // 0x000F - - /// The guest handle is invalid. - InvalidGuest, // 0x0010 - - /// The command issued is invalid. - InvalidCommand, // 0x0011 - - /// The guest is active. - Active, // 0x0012 - - /// A hardware condition has occurred affecting the platform. It is safe - /// to re-allocate parameter buffers. - HardwarePlatform, // 0x0013 - - /// A hardware condition has occurred affecting the platform. Re-allocating - /// parameter buffers is not safe. - HardwareUnsafe, // 0x0014 - - /// Feature is unsupported. - Unsupported, // 0x0015 - - /// A given parameter is invalid. - InvalidParam, // 0x0016 - - /// The SEV firmware has run out of a resource required to carry out the - /// command. - ResourceLimit, // 0x0017 - - /// The SEV platform observed a failed integrity check. - SecureDataInvalid, // 0x0018 - - /// The RMP page size is incorrect. - InvalidPageSize, // 0x0019 - - /// The RMP page state is incorrect - InvalidPageState, // 0x001A - - /// The metadata entry is invalid. - InvalidMdataEntry, // 0x001B - - /// The page ownership is incorrect - InvalidPageOwner, // 0x001C - - /// The AEAD algorithm would have overflowed - AEADOFlow, // 0x001D - - /// A Mailbox mode command was sent while the SEV FW was in Ring Buffer - /// mode. Ring Buffer mode has been exited; the Mailbox mode command - /// has been ignored. Retry is recommended. - RbModeExited = 0x001F, // 0x001F - - /// The RMP must be reinitialized. - RMPInitRequired = 0x0020, // 0x0020 - - /// SVN of provided image is lower than the committed SVN. - BadSvn, // 0x0021 - - /// Firmware version anti-rollback. - BadVersion, // 0x0022 - - /// An invocation of SNP_SHUTDOWN is required to complete this action. - ShutdownRequired, // 0x0023 - - /// Update of the firmware internal state or a guest context page has failed. - UpdateFailed, // 0x0024 - - /// Installation of the committed firmware image required - RestoreRequired, // 0x0025 - - /// The RMP initialization failed. - RMPInitFailed, // 0x0026 - - /// The key requested is invalid, not present, or not allowed. - InvalidKey, // 0x0027 -} - -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", - Error::InvalidPageSize => "The RMP page size is incorrect.", - Error::InvalidPageState => "The RMP page state is incorrect.", - Error::InvalidMdataEntry => "The metadata entry is invalid.", - Error::InvalidPageOwner => "The page ownership is incorrect", - Error::AEADOFlow => "The AEAD algorithm would have overflowed.", - Error::RbModeExited => "A Mailbox mode command was sent while the SEV FW was in Ring Buffer \ - mode. Ring Buffer mode has been exited; the Mailbox mode command has \ - been ignored. Retry is recommended.", - Error::RMPInitRequired => "The RMP must be reinitialized.", - Error::BadSvn => "SVN of provided image is lower than the committed SVN", - Error::BadVersion => "Firmware version anti-rollback.", - Error::ShutdownRequired => "An invocation of SNP_SHUTDOWN is required to complete this action.", - Error::UpdateFailed => "Update of the firmware internal state or a guest context page has failed.", - Error::RestoreRequired => "Installation of the committed firmware image required.", - Error::RMPInitFailed => "The RMP initialization failed.", - Error::InvalidKey => "The key requested is invalid, not present, or not allowed", - }; - 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 for Error { - #[inline] - fn from(error: io::Error) -> Error { - Error::IoError(error) - } -} - -impl error::Error for Indeterminate {} - -impl Display for Indeterminate { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let err_description = match self { - Indeterminate::Known(error) => format!("Known Error: {error}"), - Indeterminate::Unknown => "Unknown Error Encountered".to_string(), - }; - - write!(f, "{err_description}") - } -} - -impl From for Indeterminate { - #[inline] - fn from(error: io::Error) -> Indeterminate { - Indeterminate::Known(error.into()) - } -} - -impl From> for io::Error { - #[inline] - fn from(indeterminate: Indeterminate) -> 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 for Indeterminate { - fn from(value: u64) -> Self { - Self::from(value as u32) - } -} - -impl From for Indeterminate { - #[inline] - fn from(error: u32) -> Indeterminate { - Indeterminate::Known(match error { - 0x00 => io::Error::last_os_error().into(), - 0x01 => Error::InvalidPlatformState, - 0x02 => Error::InvalidGuestState, - 0x03 => Error::InvalidConfig, - 0x04 => Error::InvalidLen, - 0x05 => Error::AlreadyOwned, - 0x06 => Error::InvalidCertificate, - 0x07 => Error::PolicyFailure, - 0x08 => Error::Inactive, - 0x09 => Error::InvalidAddress, - 0x0A => Error::BadSignature, - 0x0B => Error::BadMeasurement, - 0x0C => Error::AsidOwned, - 0x0D => Error::InvalidAsid, - 0x0E => Error::WbinvdRequired, - 0x0F => Error::DfFlushRequired, - 0x10 => Error::InvalidGuest, - 0x11 => Error::InvalidCommand, - 0x12 => Error::Active, - 0x13 => Error::HardwarePlatform, - 0x14 => Error::HardwareUnsafe, - 0x15 => Error::Unsupported, - 0x16 => Error::InvalidParam, - 0x17 => Error::ResourceLimit, - 0x18 => Error::SecureDataInvalid, - 0x19 => Error::InvalidPageSize, - 0x1A => Error::InvalidPageState, - 0x1B => Error::InvalidMdataEntry, - 0x1C => Error::InvalidPageOwner, - 0x1D => Error::AEADOFlow, - 0x1F => Error::RbModeExited, - 0x20 => Error::RMPInitRequired, - 0x21 => Error::BadSvn, - 0x22 => Error::BadVersion, - 0x23 => Error::ShutdownRequired, - 0x24 => Error::UpdateFailed, - 0x25 => Error::RestoreRequired, - 0x26 => Error::RMPInitFailed, - 0x27 => Error::InvalidKey, - _ => return Indeterminate::Unknown, - }) - } -} - -impl From> for c_int { - fn from(err: Indeterminate) -> Self { - match err { - Indeterminate::Unknown => -0x01, - Indeterminate::Known(e) => match e { - Error::IoError(_) => -0x01, - Error::InvalidPlatformState => 0x01, - Error::InvalidGuestState => 0x02, - Error::InvalidConfig => 0x03, - Error::InvalidLen => 0x04, - Error::AlreadyOwned => 0x05, - Error::InvalidCertificate => 0x06, - Error::PolicyFailure => 0x07, - Error::Inactive => 0x08, - Error::InvalidAddress => 0x09, - Error::BadSignature => 0x0A, - Error::BadMeasurement => 0x0B, - Error::AsidOwned => 0x0C, - Error::InvalidAsid => 0x0D, - Error::WbinvdRequired => 0x0E, - Error::DfFlushRequired => 0x0F, - Error::InvalidGuest => 0x10, - Error::InvalidCommand => 0x11, - Error::Active => 0x12, - Error::HardwarePlatform => 0x13, - Error::HardwareUnsafe => 0x14, - Error::Unsupported => 0x15, - Error::InvalidParam => 0x16, - Error::ResourceLimit => 0x17, - Error::SecureDataInvalid => 0x18, - Error::InvalidPageSize => 0x19, - Error::InvalidPageState => 0x1A, - Error::InvalidMdataEntry => 0x1B, - Error::InvalidPageOwner => 0x1C, - Error::AEADOFlow => 0x1D, - Error::RbModeExited => 0x1F, - Error::RMPInitRequired => 0x20, - Error::BadSvn => 0x21, - Error::BadVersion => 0x22, - Error::ShutdownRequired => 0x23, - Error::UpdateFailed => 0x24, - Error::RestoreRequired => 0x25, - Error::RMPInitFailed => 0x26, - Error::InvalidKey => 0x27, - }, - } - } -} - #[derive(Debug)] /// Errors which may be encountered when building custom guest context. pub enum GCTXError { diff --git a/src/firmware/guest/mod.rs b/src/firmware/guest/mod.rs index a201c166..43caa1d7 100644 --- a/src/firmware/guest/mod.rs +++ b/src/firmware/guest/mod.rs @@ -10,43 +10,36 @@ mod types; pub use types::*; +use crate::error::*; + #[cfg(target_os = "linux")] -use crate::{ - error::*, - firmware::{ - host::CertTableEntry, - linux::{ - guest::{ioctl::*, types::*}, - host as HostFFI, - }, +use crate::firmware::{ + host::CertTableEntry, + linux::{ + guest::{ioctl::*, types::*}, + host as HostFFI, }, }; #[cfg(target_os = "linux")] use std::fs::{File, OpenOptions}; -// Disabled until upstream Linux kernel is patched. -// -// /// Checks the `fw_err` field on the [GuestRequest](crate::firmware::linux::guest::ioctl::GuestRequest) structure -// /// to make sure that no errors were encountered by the VMM or the AMD -// /// Secure Processor. -// fn check_fw_err(raw_error: RawFwError) -> Result<(), UserApiError> { -// if raw_error != 0.into() { -// let (upper, lower): (u32, u32) = raw_error.into(); -// -// if upper != 0 { -// return Err(VmmError::from(upper).into()); -// } -// -// if lower != 0 { -// match lower.into() { -// Indeterminate::Known(error) => return Err(error)?, -// Indeterminate::Unknown => return Err(UserApiError::Unknown), -// } -// } -// } -// Ok(()) -// } +/// Checks the `fw_err` field on the [GuestRequest](crate::firmware::linux::guest::ioctl::GuestRequest) structure +/// to make sure that no errors were encountered by the VMM or the AMD +/// Secure Processor. +fn map_fw_err(raw_error: RawFwError) -> UserApiError { + let (upper, lower): (u32, u32) = raw_error.into(); + + if upper != 0 { + return VmmError::from(upper).into(); + } + + if lower != 0 { + return FirmwareError::from(lower).into(); + } + + FirmwareError::UnknownSevError(lower).into() +} /// A handle to the SEV-SNP guest device. #[cfg(target_os = "linux")] @@ -95,7 +88,7 @@ impl Firmware { /// ``` pub fn get_report( &mut self, - message_version: Option, + message_version: Option, data: Option<[u8; 64]>, vmpl: Option, ) -> Result { @@ -105,10 +98,14 @@ impl Firmware { let mut request: GuestRequest = GuestRequest::new(message_version, &mut input, &mut response); - SNP_GET_REPORT.ioctl(&mut self.0, &mut request)?; + SNP_GET_REPORT + .ioctl(&mut self.0, &mut request) + .map_err(|_| map_fw_err(request.fw_err.into()))?; - // Disabled until upstream Linux kernel is patched. - // check_fw_err(request.fw_err.into())?; + // Make sure response status is successful + if response.status != 0 { + Err(FirmwareError::from(response.status))? + } Ok(response.report) } @@ -119,7 +116,7 @@ impl Firmware { /// Behaves the same as [get_report](crate::firmware::guest::Firmware::get_report). pub fn get_ext_report( &mut self, - message_version: Option, + message_version: Option, data: Option<[u8; 64]>, vmpl: Option, ) -> Result<(AttestationReport, Option>), UserApiError> { @@ -146,37 +143,40 @@ impl Firmware { ); // KEEP for Kernels before 47894e0f (5.19), as userspace broke at that hash. - if let Err(ioctl_error) = SNP_GET_EXT_REPORT.ioctl(&mut self.0, &mut guest_request) { + if SNP_GET_EXT_REPORT + .ioctl(&mut self.0, &mut guest_request) + .is_err() + { match guest_request.fw_err.into() { - VmmError::InvalidCertificatePageLength => (), - VmmError::RateLimitRetryRequest => return Err(VmmError::RateLimitRetryRequest)?, - _ => return Err(ioctl_error)?, + // The kernel patch by pgonda@google.com in kernel hash 47894e0f + // changed the ioctl return to succeed instead of returning an + // error when encountering an invalid certificate length. This was + // done to keep the cryptography safe, so we will now just check + // the guest_request.fw_err for a new value. + // + // Check to see if the buffer needs to be resized. If it does, the + // we need to resize the buffer to the correct size, and + // re-request for the certificates. + VmmError::InvalidCertificatePageLength => { + certificate_bytes = vec![0u8; ext_report_request.certs_len as usize]; + ext_report_request.certs_address = certificate_bytes.as_mut_ptr() as u64; + let mut guest_request_retry: GuestRequest = + GuestRequest::new( + message_version, + &mut ext_report_request, + &mut report_response, + ); + SNP_GET_EXT_REPORT + .ioctl(&mut self.0, &mut guest_request_retry) + .map_err(|_| map_fw_err(guest_request_retry.fw_err.into()))?; + } + _ => Err(map_fw_err(guest_request.fw_err.into()))?, } - - // Eventually the code below will be moved back into this scope. } - // The kernel patch by pgonda@google.com in kernel hash 47894e0f - // changed the ioctl return to succeed instead of returning an - // error when encountering an invalid certificate length. This was - // done to keep the cryptography safe, so we will now just check - // the guest_request.fw_err for a new value. - // - // Check to see if the buffer needs to be resized. If it does, the - // we need to resize the buffer to the correct size, and - // re-request for the certificates. - if VmmError::InvalidCertificatePageLength == guest_request.fw_err.into() { - certificate_bytes = vec![0u8; ext_report_request.certs_len as usize]; - ext_report_request.certs_address = certificate_bytes.as_mut_ptr() as u64; - let mut guest_request_retry: GuestRequest = GuestRequest::new( - message_version, - &mut ext_report_request, - &mut report_response, - ); - SNP_GET_EXT_REPORT.ioctl(&mut self.0, &mut guest_request_retry)?; - } else if guest_request.fw_err != 0 { - // This shouldn't be possible, but if it happens, throw an error. - return Err(UserApiError::FirmwareError(Error::InvalidConfig)); + // Make sure response status is successful + if report_response.status != 0 { + Err(FirmwareError::from(report_response.status))? } if ext_report_request.certs_len == 0 { @@ -208,22 +208,28 @@ impl Firmware { /// ``` pub fn get_derived_key( &mut self, - message_version: Option, + message_version: Option, derived_key_request: DerivedKey, ) -> Result<[u8; 32], UserApiError> { let mut ffi_derived_key_request: DerivedKeyReq = derived_key_request.into(); let mut ffi_derived_key_response: DerivedKeyRsp = Default::default(); - let mut request: GuestRequest = GuestRequest::new( - message_version, - &mut ffi_derived_key_request, - &mut ffi_derived_key_response, - ); + { + let mut request: GuestRequest = GuestRequest::new( + message_version, + &mut ffi_derived_key_request, + &mut ffi_derived_key_response, + ); - SNP_GET_DERIVED_KEY.ioctl(&mut self.0, &mut request)?; + SNP_GET_DERIVED_KEY + .ioctl(&mut self.0, &mut request) + .map_err(|_| map_fw_err(request.fw_err.into()))?; + } - // Disabled until upstream Linux kernel is patched. - // check_fw_err(request.fw_err.into())?; + // Make sure response status is successfuls + if ffi_derived_key_response.status != 0 { + Err(FirmwareError::from(ffi_derived_key_response.status))? + } Ok(ffi_derived_key_response.key) } diff --git a/src/firmware/host/mod.rs b/src/firmware/host/mod.rs index 47fd9b89..fdee3ff2 100644 --- a/src/firmware/host/mod.rs +++ b/src/firmware/host/mod.rs @@ -2,7 +2,6 @@ //! Operations for managing the SEV platform. mod types; - pub use types::*; #[cfg(target_os = "linux")] @@ -77,16 +76,22 @@ impl Firmware { /// Reset the platform persistent state. #[cfg(feature = "sev")] - pub fn platform_reset(&mut self) -> Result<(), Indeterminate> { - PLATFORM_RESET.ioctl(&mut self.0, &mut Command::from(&PlatformReset))?; + pub fn platform_reset(&mut self) -> Result<(), UserApiError> { + let mut cmd_buf = Command::from(&PlatformReset); + PLATFORM_RESET + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(()) } /// Query the platform status. #[cfg(feature = "sev")] - pub fn platform_status(&mut self) -> Result> { + pub fn platform_status(&mut self) -> Result { let mut info: PlatformStatus = Default::default(); - PLATFORM_STATUS.ioctl(&mut self.0, &mut Command::from_mut(&mut info))?; + let mut cmd_buf = Command::from_mut(&mut info); + PLATFORM_STATUS + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(Status { build: CertBuild { @@ -102,46 +107,58 @@ impl Firmware { 0 => State::Uninitialized, 1 => State::Initialized, 2 => State::Working, - _ => return Err(Indeterminate::Unknown), + _ => return Err(SevError::InvalidPlatformState)?, }, }) } /// Generate a new Platform Encryption Key (PEK). #[cfg(feature = "sev")] - pub fn pek_generate(&mut self) -> Result<(), Indeterminate> { - PEK_GEN.ioctl(&mut self.0, &mut Command::from(&PekGen))?; + pub fn pek_generate(&mut self) -> Result<(), UserApiError> { + let mut cmd_buf = Command::from(&PekGen); + PEK_GEN + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(()) } /// Request a signature for the PEK. #[cfg(feature = "sev")] - pub fn pek_csr(&mut self) -> Result> { + pub fn pek_csr(&mut self) -> Result { #[allow(clippy::uninit_assumed_init)] let mut pek: Certificate = unsafe { MaybeUninit::uninit().assume_init() }; let mut csr = PekCsr::new(&mut pek); - PEK_CSR.ioctl(&mut self.0, &mut Command::from_mut(&mut csr))?; + let mut cmd_buf = Command::from_mut(&mut csr); + PEK_CSR + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(pek) } /// Generate a new Platform Diffie-Hellman (PDH) key pair. #[cfg(feature = "sev")] - pub fn pdh_generate(&mut self) -> Result<(), Indeterminate> { - PDH_GEN.ioctl(&mut self.0, &mut Command::from(&PdhGen))?; + pub fn pdh_generate(&mut self) -> Result<(), UserApiError> { + let mut cmd_buf = Command::from(&PdhGen); + PDH_GEN + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(()) } /// Export the SEV certificate chain. #[cfg(feature = "sev")] - pub fn pdh_cert_export(&mut self) -> Result> { + pub fn pdh_cert_export(&mut self) -> Result { #[allow(clippy::uninit_assumed_init)] let mut chain: [Certificate; 3] = unsafe { MaybeUninit::uninit().assume_init() }; #[allow(clippy::uninit_assumed_init)] let mut pdh: Certificate = unsafe { MaybeUninit::uninit().assume_init() }; - let mut pdh_cert_export = PdhCertExport::new(&mut pdh, &mut chain); - PDH_CERT_EXPORT.ioctl(&mut self.0, &mut Command::from_mut(&mut pdh_cert_export))?; + let mut cmd_buf = Command::from_mut(&mut pdh_cert_export); + + PDH_CERT_EXPORT + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(Chain { pdh, @@ -157,9 +174,13 @@ impl Firmware { &mut self, pek: &Certificate, oca: &Certificate, - ) -> Result<(), Indeterminate> { + ) -> Result<(), UserApiError> { let pek_cert_import = PekCertImport::new(pek, oca); - PEK_CERT_IMPORT.ioctl(&mut self.0, &mut Command::from(&pek_cert_import))?; + let mut cmd_buf = Command::from(&pek_cert_import); + + PEK_CERT_IMPORT + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(()) } @@ -168,12 +189,14 @@ impl Firmware { /// This is especially helpful for sending AMD an HTTP request to fetch /// the signed CEK certificate. #[cfg(any(feature = "sev", feature = "snp"))] - pub fn get_identifier(&mut self) -> Result> { + pub fn get_identifier(&mut self) -> Result { let mut bytes = [0u8; 64]; let mut id = GetId::new(&mut bytes); + let mut cmd_buf = Command::from_mut(&mut id); - GET_ID.ioctl(&mut self.0, &mut Command::from_mut(&mut id))?; - + GET_ID + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(Identifier(id.as_slice().to_vec())) } @@ -188,10 +211,13 @@ impl Firmware { /// let status: PlatformStatus = firmware.platform_status().unwrap(); /// ``` #[cfg(feature = "snp")] - pub fn snp_platform_status(&mut self) -> Result> { + pub fn snp_platform_status(&mut self) -> Result { let mut platform_status: SnpPlatformStatus = SnpPlatformStatus::default(); + let mut cmd_buf = Command::from_mut(&mut platform_status); - SNP_PLATFORM_STATUS.ioctl(&mut self.0, &mut Command::from_mut(&mut platform_status))?; + SNP_PLATFORM_STATUS + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(platform_status) } @@ -211,7 +237,11 @@ impl Firmware { #[cfg(feature = "snp")] pub fn snp_commit(&mut self) -> Result<(), UserApiError> { let mut buf: SnpCommit = Default::default(); - SNP_COMMIT.ioctl(&mut self.0, &mut Command::from_mut(&mut buf))?; + let mut cmd_buf = Command::from_mut(&mut buf); + + SNP_COMMIT + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(()) } @@ -230,10 +260,12 @@ impl Firmware { /// ``` #[cfg(feature = "snp")] pub fn snp_set_config(&mut self, new_config: Config) -> Result<(), UserApiError> { - SNP_SET_CONFIG.ioctl( - &mut self.0, - &mut Command::from_mut(&mut new_config.try_into()?), - )?; + let mut binding = new_config.try_into()?; + let mut cmd_buf = Command::from_mut(&mut binding); + + SNP_SET_CONFIG + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(()) } @@ -256,8 +288,11 @@ impl Firmware { let parsed_bytes: WrappedVlekHashstick = hashstick_bytes.try_into()?; let mut vlek_load: SnpVlekLoad = SnpVlekLoad::new(&parsed_bytes); + let mut cmd_buf = Command::from_mut(&mut vlek_load); - SNP_VLEK_LOAD.ioctl(&mut self.0, &mut Command::from_mut(&mut vlek_load))?; + SNP_VLEK_LOAD + .ioctl(&mut self.0, &mut cmd_buf) + .map_err(|_| cmd_buf.encapsulate())?; Ok(()) } diff --git a/src/firmware/linux/guest/ioctl.rs b/src/firmware/linux/guest/ioctl.rs index 7b55e2e2..b81cde0c 100644 --- a/src/firmware/linux/guest/ioctl.rs +++ b/src/firmware/linux/guest/ioctl.rs @@ -30,7 +30,7 @@ pub const SNP_GET_EXT_REPORT: Ioctl { /// Message version number (must be non-zero) - pub message_version: u8, + pub message_version: u32, /// Request structure address. pub request_data: u64, /// Response structure address. @@ -47,10 +47,10 @@ impl<'a, 'b, Req, Rsp> GuestRequest<'a, 'b, Req, Rsp> { /// /// # Arguments: /// - /// * `ver` - Option - Version of the message. + /// * `ver` - Option - Version of the message. /// * `req` - &Req - The reference a Request object. /// * `rsp` - &Rsp - The reference a Response object. - pub fn new(ver: Option, req: &'a mut Req, rsp: &'b mut Rsp) -> Self { + pub fn new(ver: Option, req: &'a mut Req, rsp: &'b mut Rsp) -> Self { Self { message_version: ver.unwrap_or(1), request_data: req as *mut Req as u64, diff --git a/src/firmware/linux/host/ioctl.rs b/src/firmware/linux/host/ioctl.rs index 4b033d40..64843c0b 100644 --- a/src/firmware/linux/host/ioctl.rs +++ b/src/firmware/linux/host/ioctl.rs @@ -7,6 +7,8 @@ use super::types::*; use crate::impl_const_id; +use crate::error::FirmwareError; + #[cfg(feature = "snp")] use crate::firmware::host::SnpPlatformStatus; @@ -159,4 +161,9 @@ impl<'a, T: Id> Command<'a, T> { _phantom: PhantomData, } } + + /// encapsulate a SEV errors in command as a Firmware error. + pub fn encapsulate(&self) -> FirmwareError { + FirmwareError::from(self.error) + } } diff --git a/src/launch/linux/ioctl.rs b/src/launch/linux/ioctl.rs index 82766e88..209ab8cd 100644 --- a/src/launch/linux/ioctl.rs +++ b/src/launch/linux/ioctl.rs @@ -3,10 +3,7 @@ //! A collection of type-safe ioctl implementations for the AMD Secure Encrypted Virtualization //! (SEV) platform. These ioctls are exported by the Linux kernel. -use crate::{ - error::{Error, Indeterminate}, - impl_const_id, -}; +use crate::{error::FirmwareError, impl_const_id}; #[cfg(feature = "sev")] use crate::launch::linux::sev; @@ -259,11 +256,8 @@ impl<'a, T: Id> Command<'a, T> { } } - /// encapsulate a `std::io::Error` in an `Indeterminate` - pub fn encapsulate(&self, err: std::io::Error) -> Indeterminate { - match self.error { - 0 => Indeterminate::::from(err), - _ => Indeterminate::::from(self.error), - } + /// encapsulate a SEV errors in command as a Firmware error. + pub fn encapsulate(&self) -> FirmwareError { + FirmwareError::from(self.error) } } diff --git a/src/launch/sev.rs b/src/launch/sev.rs index 3d911ec2..eafca6f8 100644 --- a/src/launch/sev.rs +++ b/src/launch/sev.rs @@ -4,7 +4,7 @@ //! This ensures (at compile time) that the right steps are called in the //! right order. -use crate::error::{Error::InvalidLen, Indeterminate}; +use crate::error::{FirmwareError, SevError}; #[cfg(target_os = "linux")] use crate::launch::linux::ioctl::*; @@ -12,10 +12,7 @@ use crate::launch::linux::ioctl::*; use crate::launch::linux::{sev::*, shared::*}; use crate::*; -use std::convert::TryFrom; -use std::io::Result; -use std::mem::MaybeUninit; -use std::os::unix::io::AsRawFd; +use std::{convert::TryFrom, mem::MaybeUninit, os::unix::io::AsRawFd, result::Result}; use bitflags::bitflags; use serde::{Deserialize, Serialize}; @@ -49,7 +46,7 @@ impl Launcher { impl Launcher { /// Begin the SEV launch process. - pub fn new(kvm: U, sev: V) -> Result { + pub fn new(kvm: U, sev: V) -> Result { let mut launcher = Launcher { vm_fd: kvm, sev, @@ -62,13 +59,13 @@ impl Launcher { INIT2 .ioctl(&mut launcher.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(launcher) } /// Begin the SEV-ES launch process. - pub fn new_es(kvm: U, sev: V) -> Result { + pub fn new_es(kvm: U, sev: V) -> Result { let mut launcher = Launcher { vm_fd: kvm, sev, @@ -80,18 +77,18 @@ impl Launcher { let mut cmd = Command::from(&launcher.sev, &init); INIT2 .ioctl(&mut launcher.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(launcher) } /// Create an encrypted guest context. - pub fn start(mut self, start: Start) -> Result> { + pub fn start(mut self, start: Start) -> Result, FirmwareError> { let mut launch_start = LaunchStart::new(&start.policy, &start.cert, &start.session); let mut cmd = Command::from_mut(&self.sev, &mut launch_start); LAUNCH_START .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; let next = Launcher { state: Started(launch_start.into()), @@ -105,7 +102,7 @@ impl Launcher { impl Launcher { /// Encrypt guest data with its VEK. - pub fn update_data(&mut self, data: &[u8]) -> Result<()> { + pub fn update_data(&mut self, data: &[u8]) -> Result<(), FirmwareError> { let launch_update_data = LaunchUpdateData::new(data); let mut cmd = Command::from(&self.sev, &launch_update_data); @@ -113,50 +110,50 @@ impl Launcher { LAUNCH_UPDATE_DATA .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(()) } /// Register the encrypted memory region to a virtual machine. /// Corresponds to the `KVM_MEMORY_ENCRYPT_REG_REGION` ioctl. - pub fn register_kvm_enc_region(&mut self, data: &[u8]) -> Result<()> { + pub fn register_kvm_enc_region(&mut self, data: &[u8]) -> Result<(), FirmwareError> { KvmEncRegion::new(data).register(&mut self.vm_fd)?; Ok(()) } /// Encrypt guest data with its VEK, while the KVM encrypted memory region is not registered. - pub fn update_data_without_registration(&mut self, data: &[u8]) -> Result<()> { + pub fn update_data_without_registration(&mut self, data: &[u8]) -> Result<(), FirmwareError> { let launch_update_data = LaunchUpdateData::new(data); let mut cmd = Command::from(&self.sev, &launch_update_data); LAUNCH_UPDATE_DATA .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(()) } /// Encrypt the VMSA on SEV-ES. - pub fn update_vmsa(&mut self) -> Result<()> { + pub fn update_vmsa(&mut self) -> Result<(), FirmwareError> { let launch_update_vmsa = LaunchUpdateVmsa::new(); let mut cmd = Command::from(&self.sev, &launch_update_vmsa); LAUNCH_UPDATE_VMSA .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(()) } /// Request a measurement from the SEV firmware. - pub fn measure(mut self) -> Result> { + pub fn measure(mut self) -> Result, FirmwareError> { let mut measurement = MaybeUninit::uninit(); let mut launch_measure = LaunchMeasure::new(&mut measurement); let mut cmd = Command::from_mut(&self.sev, &mut launch_measure); LAUNCH_MEASUREMENT .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; let next = Launcher { state: Measured(self.state.0, unsafe { measurement.assume_init() }), @@ -179,31 +176,31 @@ impl Launcher { /// ## Remarks /// /// This should only be called after a successful attestation flow. - pub fn inject(&mut self, secret: &Secret, guest: usize) -> Result<()> { + pub fn inject(&mut self, secret: &Secret, guest: usize) -> Result<(), FirmwareError> { let launch_secret = LaunchSecret::new(&secret.header, guest, &secret.ciphertext[..]); let mut cmd = Command::from(&self.sev, &launch_secret); LAUNCH_SECRET .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(()) } /// Complete the SEV launch process. - pub fn finish(mut self) -> Result { + pub fn finish(mut self) -> Result { let mut cmd = Command::from(&self.sev, &LaunchFinish); LAUNCH_FINISH .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(self.state.0) } /// Complete the SEV launch process, and produce another Launcher capable of fetching an /// attestation report. - pub fn finish_attestable(mut self) -> Result> { + pub fn finish_attestable(mut self) -> Result, FirmwareError> { let mut cmd = Command::from(&self.sev, &LaunchFinish); LAUNCH_FINISH .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; let next = Launcher { state: Finished, @@ -217,16 +214,17 @@ impl Launcher { impl Launcher { /// Get the attestation report of the VM. - pub fn report(&mut self, mnonce: [u8; 16]) -> Result> { + pub fn report(&mut self, mnonce: [u8; 16]) -> Result, FirmwareError> { let mut first = LaunchAttestation::default(); let mut cmd = Command::from_mut(&self.sev, &mut first); let mut len = 0; let e = LAUNCH_ATTESTATION .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e)); + .map_err(|_| cmd.encapsulate()); + if let Err(err) = e { - if let Indeterminate::Known(InvalidLen) = err { + if let FirmwareError::KnownSevError(SevError::InvalidLen) = err { len = first.len; } else { return Err(err)?; @@ -239,7 +237,7 @@ impl Launcher { LAUNCH_ATTESTATION .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(bytes) } diff --git a/src/launch/snp.rs b/src/launch/snp.rs index 64cde5fc..9f3ccfe6 100644 --- a/src/launch/snp.rs +++ b/src/launch/snp.rs @@ -4,11 +4,14 @@ //! This ensures (at compile time) that the right steps are called in the //! right order. -use crate::firmware::guest::GuestPolicy; #[cfg(target_os = "linux")] -use crate::launch::linux::{ioctl::*, shared::*, snp::*}; +use crate::{ + error::FirmwareError, + firmware::guest::GuestPolicy, + launch::linux::{ioctl::*, shared::*, snp::*}, +}; -use std::{io::Result, marker::PhantomData, os::unix::io::AsRawFd}; +use std::{marker::PhantomData, os::unix::io::AsRawFd, result::Result}; use bitflags::bitflags; use serde::{Deserialize, Serialize}; @@ -43,7 +46,7 @@ impl AsMut for Launcher { impl Launcher { /// Begin the SEV-SNP launch process by creating a Launcher and issuing the /// KVM_SNP_INIT ioctl. - pub fn new(vm_fd: U, sev: V) -> Result { + pub fn new(vm_fd: U, sev: V) -> Result { let mut launcher = Launcher { vm_fd, sev, @@ -56,19 +59,19 @@ impl Launcher { INIT2 .ioctl(&mut launcher.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok(launcher) } /// Initialize the flow to launch a guest. - pub fn start(mut self, start: Start) -> Result> { + pub fn start(mut self, start: Start) -> Result, FirmwareError> { let launch_start = LaunchStart::from(start); let mut cmd = Command::from(&self.sev, &launch_start); SNP_LAUNCH_START .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; let launcher = Launcher { vm_fd: self.vm_fd, @@ -82,7 +85,12 @@ impl Launcher { impl Launcher { /// Encrypt guest SNP data. - pub fn update_data(&mut self, mut update: Update, gpa: u64, gpa_len: u64) -> Result<()> { + pub fn update_data( + &mut self, + mut update: Update, + gpa: u64, + gpa_len: u64, + ) -> Result<(), FirmwareError> { loop { let launch_update_data = LaunchUpdate::from(update); let mut cmd = Command::from(&self.sev, &launch_update_data); @@ -115,9 +123,9 @@ impl Launcher { // Retry the operation if `-EAGAIN` is returned continue; } - Err(e) => { + Err(_) => { // Handle other errors - return Err(cmd.encapsulate(e).into()); + return Err(cmd.encapsulate()); } } } @@ -126,13 +134,13 @@ impl Launcher { } /// Complete the SNP launch process. - pub fn finish(mut self, finish: Finish) -> Result<(U, V)> { + pub fn finish(mut self, finish: Finish) -> Result<(U, V), FirmwareError> { let launch_finish = LaunchFinish::from(finish); let mut cmd = Command::from(&self.sev, &launch_finish); SNP_LAUNCH_FINISH .ioctl(&mut self.vm_fd, &mut cmd) - .map_err(|e| cmd.encapsulate(e))?; + .map_err(|_| cmd.encapsulate())?; Ok((self.vm_fd, self.sev)) } diff --git a/src/lib.rs b/src/lib.rs index 414d8d4d..4ce2a1ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,7 +129,7 @@ use certs::sev::builtin as SevBuiltin; use certs::snp::builtin as SnpBuiltin; #[cfg(all(feature = "sev", target_os = "linux"))] -use crate::{certs::sev::sev::Certificate as SevCertificate, error::Indeterminate, launch::sev::*}; +use crate::{certs::sev::sev::Certificate as SevCertificate, error::FirmwareError, launch::sev::*}; #[cfg(any(feature = "sev", feature = "snp"))] use std::convert::TryFrom; @@ -139,7 +139,6 @@ use std::io::{Read, Write}; #[cfg(all(feature = "sev", target_os = "linux"))] use std::{ collections::HashMap, - io, mem::size_of, os::{ fd::RawFd, @@ -403,8 +402,8 @@ lazy_static! { } #[cfg(all(feature = "sev", target_os = "linux"))] -fn set_fw_err(ptr: *mut c_int, err: io::Error) { - unsafe { *ptr = Indeterminate::from(err).into() }; +fn set_fw_err(ptr: *mut c_int, err: FirmwareError) { + unsafe { *ptr = err.into() }; } /// A C FFI interface to the SEV_INIT ioctl. diff --git a/tests/api.rs b/tests/api.rs index d49c5617..d6d6bcde 100644 --- a/tests/api.rs +++ b/tests/api.rs @@ -185,4 +185,14 @@ mod snp { let new_config = Config::new(TcbVersion::new(1, 0, 1, 1), MaskId(31)); fw.snp_set_config(new_config).unwrap(); } + + #[cfg_attr(not(all(has_sev, feature = "dangerous_hw_tests")), ignore)] + #[test] + #[serial] + fn test_host_fw_error() { + let mut fw: Firmware = Firmware::open().unwrap(); + let invalid_config = Config::new(TcbVersion::new(100, 100, 100, 100), MaskId(31)); + let fw_error = fw.snp_set_config(invalid_config).unwrap_err().to_string(); + assert_eq!(fw_error, "Firmware Error Encountered: Known SEV FW Error: Status Code: 0x16: Given parameter is invalid.") + } } diff --git a/tests/guest.rs b/tests/guest.rs index 88ba7b60..c8e57530 100644 --- a/tests/guest.rs +++ b/tests/guest.rs @@ -35,3 +35,25 @@ fn get_derived_key() { fw.get_derived_key(None, derived_key).unwrap(); } + +#[cfg(all(feature = "snp", target_os = "linux"))] +#[cfg_attr(not(has_sev_guest), ignore)] +#[test] +fn guest_fw_error() { + let derived_key = DerivedKey::new( + false, + GuestFieldSelect(48), + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFFFFFFFFFF, + ); + + let mut fw = Firmware::open().unwrap(); + + let fw_err = fw + .get_derived_key(None, derived_key) + .unwrap_err() + .to_string(); + + assert_eq!(fw_err, "Firmware Error Encountered: Known SEV FW Error: Status Code: 0x16: Given parameter is invalid.") +}