diff --git a/Cargo.lock b/Cargo.lock index 9d370b92bb..0dad28735c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6720,7 +6720,7 @@ dependencies = [ [[package]] name = "libredfish" version = "0.0.0" -source = "git+https://github.com/NVIDIA/libredfish.git?tag=v0.44.21#63431424e2f5eea93e3df7eb41f0b2b3e98f72fb" +source = "git+https://github.com/NVIDIA/libredfish.git?tag=v0.44.22#53c0b12569d6de1ecccde826d495618ff987d8c6" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 1d7b04d922..02996d1993 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ authors = ["NVIDIA Carbide Engineering "] [workspace.dependencies] clap = { version = "4", features = ["derive", "env"] } -libredfish = { git = "https://github.com/NVIDIA/libredfish.git", tag = "v0.44.21" } +libredfish = { git = "https://github.com/NVIDIA/libredfish.git", tag = "v0.44.22" } librms = { git = "https://github.com/NVIDIA/nv-rms-client.git", tag = "v0.9.1" } ansi-to-html = "0.2.2" diff --git a/crates/bmc-explorer/src/chassis.rs b/crates/bmc-explorer/src/chassis.rs index 37cd659569..de6075c71a 100644 --- a/crates/bmc-explorer/src/chassis.rs +++ b/crates/bmc-explorer/src/chassis.rs @@ -200,6 +200,17 @@ impl ExploredChassisCollection { }) } + pub fn is_mgx_c2(&self) -> bool { + self.members.iter().any(|m| { + let hardware_id = m.chassis.hardware_id(); + is_mgx_c2_processor_module( + hardware_id.manufacturer.map(|v| v.into_inner()), + hardware_id.model.map(|v| v.into_inner()), + hardware_id.part_number.map(|v| v.into_inner()), + ) + }) + } + pub fn is_lenovo(&self) -> bool { self.members .iter() @@ -489,6 +500,16 @@ fn is_delta_powershelf_chassis(chassis_id: &str, manufacturer: Option<&str>) -> && manufacturer.is_some_and(|mfg| mfg.to_lowercase().contains("delta")) } +fn is_mgx_c2_processor_module( + manufacturer: Option<&str>, + model: Option<&str>, + part_number: Option<&str>, +) -> bool { + manufacturer.is_some_and(|v| v.eq_ignore_ascii_case("NVIDIA")) + && (model.is_some_and(|v| v.starts_with("PG535")) + || part_number.is_some_and(|v| v.contains("2G535"))) +} + /// Aggregates per-PSU commanded on/off states into a single power-shelf state. /// /// All PSUs reporting `Some(true)` means the shelf is on; all `Some(false)` @@ -543,7 +564,46 @@ impl LiteOnSuppliesState<'_> { #[cfg(test)] mod tests { - use super::{ModelPowerState, is_delta_powershelf_chassis, powershelf_power_state}; + use super::{ + ModelPowerState, is_delta_powershelf_chassis, is_mgx_c2_processor_module, + powershelf_power_state, + }; + + #[test] + fn identifies_mgx_c2_processor_modules() { + let cases = [ + ("PG535 model", Some("NVIDIA"), Some("PG535-B02"), None, true), + ( + "2G535 part number", + Some("Nvidia"), + None, + Some("699-2G535-0200-310"), + true, + ), + ( + "unrelated NVIDIA module", + Some("NVIDIA"), + Some("B4240"), + Some("900-9D3B4-00CC-EA0"), + false, + ), + ( + "non-NVIDIA PG535", + Some("Supermicro"), + Some("PG535-B02"), + Some("699-2G535-0200-310"), + false, + ), + ]; + + for (case, manufacturer, model, part_number, expected) in cases { + assert_eq!( + is_mgx_c2_processor_module(manufacturer, model, part_number), + expected, + "{case}", + ); + } + } // is_delta_powershelf_chassis gates Delta detection: a power-shelf chassis // id ("chassis"/"powershelf") AND a Delta manufacturer. The manufacturer diff --git a/crates/bmc-explorer/src/hw/supermicro.rs b/crates/bmc-explorer/src/hw/supermicro.rs index 054d01f518..76fe94e9dc 100644 --- a/crates/bmc-explorer/src/hw/supermicro.rs +++ b/crates/bmc-explorer/src/hw/supermicro.rs @@ -20,7 +20,7 @@ use crate::hw::BiosAttr; // Real attribute names examples: // "IPv4HTTPSupport_009F", "DeviceSelect_0034", "DeviceSelect_003D", "SR_IOVSupport_002B" // This constant defines prefixes till last underscore. -pub const EXPECTED_BIOS_ATTRS_PREFIXES: [BiosAttr; 14] = [ +pub const EXPECTED_BIOS_ATTRS_PREFIXES: [BiosAttr; 15] = [ BiosAttr::new_bool("QuietBoot", false), BiosAttr::new_str("Re_tryBoot", "EFI Boot"), BiosAttr::new_str("CSMSupport", "Disabled"), @@ -33,6 +33,7 @@ pub const EXPECTED_BIOS_ATTRS_PREFIXES: [BiosAttr; 14] = [ // Not that some are "Enable" and some are "Enabled". Subtle. BiosAttr::new_str("IntelVTforDirectedI_O_VT_d", "Enable"), BiosAttr::new_str("IntelVirtualizationTechnology", "Enable"), + BiosAttr::new_str("SR-IOVSupport", "Enabled"), BiosAttr::new_str("SR_IOVSupport", "Enabled"), // UEFI NIC boot BiosAttr::new_str("IPv4HTTPSupport", "Enabled"), diff --git a/crates/bmc-explorer/src/lib.rs b/crates/bmc-explorer/src/lib.rs index b9f63e0023..e2a52d7a5f 100644 --- a/crates/bmc-explorer/src/lib.rs +++ b/crates/bmc-explorer/src/lib.rs @@ -170,6 +170,7 @@ pub async fn nv_generate_exploration_report( let explored_system = ExploredComputerSystem::explore(system, &system_explore_config).await?; let hw_type = hw_type(&root, &explored_system, &explored_chassis); + let is_mgx_c2 = explored_chassis.is_mgx_c2(); let manager_explore_config = hw_type .map(|hw_type| match hw_type { hw::HwType::Ami => manager::Config { @@ -194,7 +195,7 @@ pub async fn nv_generate_exploration_report( }, hw::HwType::Supermicro => manager::Config { need_host_interfaces: true, - need_oem_supermicro_kcs_interface: true, + need_oem_supermicro_kcs_interface: !is_mgx_c2, need_oem_supermicro_sys_lockdown: true, ..Default::default() }, @@ -239,7 +240,14 @@ pub async fn nv_generate_exploration_report( .await?; let lockdown_status = hw_type - .map(|hw_type| lockdown_status(&hw_type, &explored_system, &explored_manager)) + .map(|hw_type| { + lockdown_status( + &hw_type, + &explored_system, + &explored_manager, + &explored_chassis, + ) + }) .transpose()? .and_then(identity); @@ -426,6 +434,7 @@ fn lockdown_status( hw_type: &hw::HwType, explored_system: &ExploredComputerSystem, explored_manager: &ExploredManager, + explored_chassis: &ExploredChassisCollection, ) -> Result, Error> { let bios = &explored_system.bios; let system = &explored_system.system; @@ -663,33 +672,42 @@ fn lockdown_status( .as_ref() .and_then(|lck| lck.sys_lockdown_enabled()) .ok_or_else(Error::bmc_not_provided("Supermicro lockdown status"))?; + let ipmi_host_interface_enabled = system + .raw() + .ipmi_host_interface + .as_ref() + .and_then(|interface| interface.service_enabled); let message = format!( - "SysLockdownEnabled={is_syslockdown}, kcs_privilege={kcs_privilege:#?}, host_interface_enabled={hi_enabled}" + "SysLockdownEnabled={is_syslockdown}, kcs_privilege={kcs_privilege:#?}, \ + host_interface_enabled={hi_enabled}, \ + ipmi_host_interface_enabled={ipmi_host_interface_enabled:?}" ); let model = system.hardware_id().model.map(|v| v.into_inner()); - if model == Some("ARS-121L-DNR") { - // Grace-Grace SMCs (ARS-121L-DNR): - // 1. Need host_interface enabled even with lockdown - // 2. Doesn't provide KCSInterface - match (hi_enabled, is_syslockdown) { - (true, true) => Ok(InternalLockdownStatus::Enabled), - (true, false) => Ok(InternalLockdownStatus::Disabled), - _ => Ok(InternalLockdownStatus::Partial), - } + let is_ars_121l_dnr = model == Some("ARS-121L-DNR"); + let (inband_locked, inband_unlocked) = if explored_chassis.is_mgx_c2() { + ( + ipmi_host_interface_enabled.is_none_or(|enabled| !enabled), + ipmi_host_interface_enabled.is_none_or(identity), + ) } else { - match (hi_enabled, kcs_privilege, is_syslockdown) { - (false, Some(SupermicroPrivilege::Callback), true) => { - Ok(InternalLockdownStatus::Enabled) - } - (true, Some(SupermicroPrivilege::Administrator), false) => { - Ok(InternalLockdownStatus::Disabled) - } - (true, None, false) => Ok(InternalLockdownStatus::Disabled), - _ => Ok(InternalLockdownStatus::Partial), - } - } - .map(|status| Some(LockdownStatus { status, message })) + ( + kcs_privilege == Some(SupermicroPrivilege::Callback), + kcs_privilege.is_none() + || kcs_privilege == Some(SupermicroPrivilege::Administrator), + ) + }; + // ARS-121L-DNR must keep HostInterface enabled to PXE boot. + let host_interface_locked = hi_enabled == is_ars_121l_dnr; + + let status = if is_syslockdown && inband_locked && host_interface_locked { + InternalLockdownStatus::Enabled + } else if !is_syslockdown && inband_unlocked && hi_enabled { + InternalLockdownStatus::Disabled + } else { + InternalLockdownStatus::Partial + }; + Ok(Some(LockdownStatus { status, message })) } hw::HwType::Hpe => {