@@ -58,6 +58,20 @@ use crate::{
5858
5959const MELLANOX_UEFI_HTTP_IPV4 : & str = "UEFI HTTP IPv4 Mellanox Network Adapter" ;
6060const NVIDIA_UEFI_HTTP_IPV4 : & str = "UEFI HTTP IPv4 Nvidia Network Adapter" ;
61+
62+ /// MGX C2 systems use SSIF instead of x86 KCS for in-band BMC communication,
63+ /// so the KCSInterface endpoint doesn't exist. These models require the
64+ /// IPMIHostInterface fallback on Systems/{id}.
65+ const MGX_C2_MODELS : [ & str ; 4 ] = [
66+ "ARS-121L-DNR" ,
67+ "ARS-221GL-NR" ,
68+ "SYS-221H-TNR" ,
69+ "SYS-221H-TN24R" ,
70+ ] ;
71+
72+ /// Minimum BMC firmware version that exposes `IPMIHostInterface` on
73+ /// `Systems/{id}` for MGX C2 systems.
74+ const MIN_BMC_FW_IPMI_HOST_IFACE : & str = "01.05.01" ;
6175const HARD_DISK : & str = "UEFI Hard Disk" ;
6276const NETWORK : & str = "UEFI Network" ;
6377
@@ -325,19 +339,7 @@ impl Redfish for Bmc {
325339 fn lockdown_status < ' a > ( & ' a self ) -> crate :: RedfishFuture < ' a , Result < Status , RedfishError > > {
326340 Box :: pin ( async move {
327341 let is_hi_on = self . is_host_interface_enabled ( ) . await ?;
328- let kcs_privilege = match self . get_kcs_privilege ( ) . await {
329- Ok ( priviledge) => Ok ( Some ( priviledge) ) ,
330- Err ( e) => {
331- // The Grace-Grace Supermicros in our GB200 lab do not seem to support
332- // querying KCS access from the host to its BMC. Use this workaround to
333- // temporarily enable ingesting these servers.
334- if e. not_found ( ) {
335- Ok ( None )
336- } else {
337- Err ( e)
338- }
339- }
340- } ?;
342+ let kcs_privilege = self . get_kcs_privilege ( ) . await ?;
341343
342344 let is_syslockdown = self . get_syslockdown ( ) . await ?;
343345 let message = format ! ( "SysLockdownEnabled={is_syslockdown}, kcs_privilege={kcs_privilege:#?}, host_interface_enabled={is_hi_on}" ) ;
@@ -346,14 +348,10 @@ impl Redfish for Bmc {
346348 let is_grace_grace = self . is_grace_grace_smc ( ) . await ?;
347349
348350 let is_locked = is_syslockdown
349- && kcs_privilege
350- . clone ( )
351- . unwrap_or ( supermicro:: Privilege :: Callback )
352- == supermicro:: Privilege :: Callback
351+ && kcs_privilege == supermicro:: Privilege :: Callback
353352 && ( is_grace_grace || !is_hi_on) ;
354353 let is_unlocked = !is_syslockdown
355- && kcs_privilege. unwrap_or ( supermicro:: Privilege :: Administrator )
356- == supermicro:: Privilege :: Administrator
354+ && kcs_privilege == supermicro:: Privilege :: Administrator
357355 && is_hi_on;
358356 Ok ( Status {
359357 message,
@@ -1286,11 +1284,24 @@ impl Bmc {
12861284 }
12871285
12881286 async fn get_kcs_privilege ( & self ) -> Result < supermicro:: Privilege , RedfishError > {
1287+ if self . is_mgx_c2 ( ) . await ? {
1288+ let enabled = self . get_ipmi_host_interface_enabled ( ) . await ?;
1289+ return if enabled {
1290+ Ok ( supermicro:: Privilege :: Administrator )
1291+ } else {
1292+ Ok ( supermicro:: Privilege :: Callback )
1293+ } ;
1294+ }
1295+
12891296 let url = format ! (
12901297 "Managers/{}/Oem/Supermicro/KCSInterface" ,
12911298 self . s. manager_id( )
12921299 ) ;
1293- let ( _, body) : ( _ , HashMap < String , serde_json:: Value > ) = self . s . client . get ( & url) . await ?;
1300+ let ( _, body) = self
1301+ . s
1302+ . client
1303+ . get :: < HashMap < String , serde_json:: Value > > ( & url)
1304+ . await ?;
12941305 let key = "Privilege" ;
12951306 let p_str = body
12961307 . get ( key)
@@ -1315,29 +1326,77 @@ impl Bmc {
13151326 & self ,
13161327 privilege : supermicro:: Privilege ,
13171328 ) -> Result < ( ) , RedfishError > {
1329+ if self . is_mgx_c2 ( ) . await ? {
1330+ let enabled = privilege == supermicro:: Privilege :: Administrator ;
1331+ return self . set_ipmi_host_interface ( enabled) . await ;
1332+ }
1333+
13181334 let url = format ! (
13191335 "Managers/{}/Oem/Supermicro/KCSInterface" ,
13201336 self . s. manager_id( )
13211337 ) ;
13221338 let body = HashMap :: from ( [ ( "Privilege" , privilege. to_string ( ) ) ] ) ;
1323- self . s
1324- . client
1325- . patch ( & url, body)
1326- . await
1327- . or_else ( |err| {
1328- // The Grace-Grace Supermicros in our GB200 lab do not seem to support
1329- // disabling KCS access from the host to its BMC. Use this workaround to
1330- // temporarily enable ingesting these servers.
1331- if err. not_found ( ) {
1332- tracing:: warn!(
1333- "Supermicro was uanble to find {url}: {err}; not returning error to caller"
1334- ) ;
1335- Ok ( ( StatusCode :: OK , None ) )
1336- } else {
1337- Err ( err)
1338- }
1339- } )
1340- . map ( |_status_code| ( ) )
1339+ self . s . client . patch ( & url, body) . await ?;
1340+ Ok ( ( ) )
1341+ }
1342+
1343+ /// Returns `true` when the BMC firmware version is at least
1344+ /// [`MIN_BMC_FW_IPMI_HOST_IFACE`] (`01.05.01`), which is the first
1345+ /// version to expose `IPMIHostInterface` on `Systems/{id}`.
1346+ async fn bmc_supports_ipmi_host_iface ( & self ) -> Result < bool , RedfishError > {
1347+ let manager = self . s . get_manager ( ) . await ?;
1348+ let fw = manager. firmware_version . unwrap_or_default ( ) ;
1349+ Ok ( version_compare:: compare ( & fw, MIN_BMC_FW_IPMI_HOST_IFACE )
1350+ . is_ok_and ( |c| c != version_compare:: Cmp :: Lt ) )
1351+ }
1352+
1353+ /// Disable/enable SSIF in-band access via `IPMIHostInterface` on `Systems/{id}`.
1354+ /// Used for MGX C2 systems that lack the KCSInterface endpoint.
1355+ /// No-op when the BMC firmware is older than 01.05.01.
1356+ async fn set_ipmi_host_interface ( & self , enabled : bool ) -> Result < ( ) , RedfishError > {
1357+ if !self . bmc_supports_ipmi_host_iface ( ) . await ? {
1358+ let smc_bmc_ip = self . s . client . host ( ) ;
1359+ tracing:: warn!(
1360+ smc_bmc_ip,
1361+ "MGX C2 BMC firmware is older than {MIN_BMC_FW_IPMI_HOST_IFACE}; \
1362+ skipping IPMIHostInterface write"
1363+ ) ;
1364+ return Ok ( ( ) ) ;
1365+ }
1366+
1367+ use crate :: model:: system:: IpmiHostInterface ;
1368+ let url = format ! ( "Systems/{}" , self . s. system_id( ) ) ;
1369+ let body = HashMap :: from ( [ (
1370+ "IPMIHostInterface" ,
1371+ IpmiHostInterface {
1372+ service_enabled : enabled,
1373+ } ,
1374+ ) ] ) ;
1375+ self . s . client . patch ( & url, body) . await . map ( |_status_code| ( ) )
1376+ }
1377+
1378+ /// Get whether SSIF in-band access is enabled via `IPMIHostInterface` on `Systems/{id}`.
1379+ /// Used for MGX C2 systems that lack the KCSInterface endpoint.
1380+ /// Returns `false` when the BMC firmware is older than 01.05.01.
1381+ async fn get_ipmi_host_interface_enabled ( & self ) -> Result < bool , RedfishError > {
1382+ if !self . bmc_supports_ipmi_host_iface ( ) . await ? {
1383+ let smc_bmc_ip = self . s . client . host ( ) ;
1384+ tracing:: warn!(
1385+ smc_bmc_ip,
1386+ "MGX C2 BMC firmware is older than {MIN_BMC_FW_IPMI_HOST_IFACE}; \
1387+ IPMIHostInterface unavailable, reporting disabled"
1388+ ) ;
1389+ return Ok ( false ) ;
1390+ }
1391+
1392+ let system = self . s . get_system ( ) . await ?;
1393+ let iface = system
1394+ . ipmi_host_interface
1395+ . ok_or_else ( || RedfishError :: MissingKey {
1396+ key : "IPMIHostInterface" . to_string ( ) ,
1397+ url : format ! ( "Systems/{}" , self . s. system_id( ) ) ,
1398+ } ) ?;
1399+ Ok ( iface. service_enabled )
13411400 }
13421401
13431402 async fn is_host_interface_enabled ( & self ) -> Result < bool , RedfishError > {
@@ -1581,7 +1640,13 @@ impl Bmc {
15811640 Ok ( by_name)
15821641 }
15831642
1584- // Check if this is a Grace-Grace SMC (ARS-121L-DNR) that needs host_interface enabled
1643+ /// MGX C2 systems use SSIF instead of x86 KCS, so the KCSInterface
1644+ /// endpoint doesn't exist. Detect them by matching the system model.
1645+ async fn is_mgx_c2 ( & self ) -> Result < bool , RedfishError > {
1646+ let model = self . s . get_system ( ) . await ?. model . unwrap_or_default ( ) ;
1647+ Ok ( MGX_C2_MODELS . iter ( ) . any ( |m| model. contains ( m) ) )
1648+ }
1649+
15851650 async fn is_grace_grace_smc ( & self ) -> Result < bool , RedfishError > {
15861651 Ok ( self
15871652 . s
0 commit comments