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.") +}