// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // //! UV related functions for pvapconfig // use pv_core::uv::{ListCmd, SecretList, UvDevice, UvcSuccess}; use regex::Regex; use std::path::Path; /// The byte size of association secret of type 2 in struct SecretEntry pub const AP_ASSOC_SECRET_ID_SIZE: usize = 32; const PATH_SYS_FW_UV_FACILITIES: &str = "/sys/firmware/uv/query/facilities"; const RE_UV_FACILITIES: &str = r"^(0x)?([[:xdigit:]]+)"; const RE_UV_FAC_BIT_LIST_SECRETS: u32 = 30; /// Check UV facilities to offer the 'list secrets' call. /// Returns a Result with Ok(()) if the 'list secrets' feature /// is available, otherwise an Err(reasonstring) is returned where /// the string denotes a hint which can be displayed. /// # Panics /// Panics if the compilation of a static regular expression fails. /// Panics if RE_UV_FACILITIES does not match. pub fn has_list_secrets_facility() -> Result<(), String> { if !Path::new(PATH_SYS_FW_UV_FACILITIES).is_file() { return Err(format!( "UV facilities sysfs attribute not found (file {} does not exist).", PATH_SYS_FW_UV_FACILITIES )); } let facstr = match crate::helper::sysfs_read_string(PATH_SYS_FW_UV_FACILITIES) { Ok(s) => s, Err(err) => { return Err(format!( "Failure reading UV facilities from {PATH_SYS_FW_UV_FACILITIES} ({:?}).", err )) } }; let re_uv_facilities = Regex::new(RE_UV_FACILITIES).unwrap(); if !re_uv_facilities.is_match(&facstr) { Err(format!("Failure parsing UV facilities entry '{facstr}'.")) } else { let caps = re_uv_facilities.captures(&facstr).unwrap(); let fachex = caps.get(2).unwrap().as_str(); let i: usize = RE_UV_FAC_BIT_LIST_SECRETS as usize / 4; if i >= fachex.len() { return Err(format!("Failure parsing UV facilities entry '{fachex}'.")); } let nibble = u32::from_str_radix(&fachex[i..i + 1], 16).unwrap(); const THEBIT: u32 = 1 << (3 - (RE_UV_FAC_BIT_LIST_SECRETS % 4)); if nibble & THEBIT == 0 { return Err("The 'list secret' feature is missing on this UV.".to_string()); } Ok(()) } } /// Fetch the list of secrets from the UV. /// Returns Err(errorstring) on error or /// Ok(SecretList) on success. /// The list may be empty if the UV doesn't have any secrets stored. pub fn gather_secrets() -> Result { let uv = match UvDevice::open() { Err(e) => return Err(format!("Failed to open UV device: {:?}.", e)), Ok(u) => u, }; let mut cmd = ListCmd::default(); match uv.send_cmd(&mut cmd).map_err(|e| format!("{e:?}"))? { UvcSuccess::RC_SUCCESS => (), UvcSuccess::RC_MORE_DATA => println!("Warning: There is more data available than expected"), }; cmd.try_into().map_err(|e| format!("{e:?}")) } #[cfg(test)] mod tests { use super::*; // As the name says: check for list secrets feature bit in UV facilities. #[test] fn test_has_list_secrets_facility() { let r = has_list_secrets_facility(); if pv_core::misc::pv_guest_bit_set() { assert!(r.is_ok()); } else { assert!(r.is_err()); } } // Simple invocation of the list_secrets function. Should not fail #[test] fn test_list_secrets() { let r = gather_secrets(); if pv_core::misc::pv_guest_bit_set() { assert!(r.is_ok()); } else { assert!(r.is_err()); } } }