@@ -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,31 +1326,88 @@ 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 ( ) ) ] ) ;
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 (
1350+ version_compare:: compare ( & fw, MIN_BMC_FW_IPMI_HOST_IFACE )
1351+ . is_ok_and ( |c| c != version_compare:: Cmp :: Lt ) ,
1352+ )
1353+ }
1354+
1355+ /// Disable/enable SSIF in-band access via `IPMIHostInterface` on `Systems/{id}`.
1356+ /// Used for MGX C2 systems that lack the KCSInterface endpoint.
1357+ /// No-op when the BMC firmware is older than 01.05.01.
1358+ async fn set_ipmi_host_interface (
1359+ & self ,
1360+ enabled : bool ,
1361+ ) -> Result < ( ) , RedfishError > {
1362+ if !self . bmc_supports_ipmi_host_iface ( ) . await ? {
1363+ let smc_bmc_ip = self . s . client . host ( ) ;
1364+ tracing:: warn!(
1365+ smc_bmc_ip,
1366+ "MGX C2 BMC firmware is older than {MIN_BMC_FW_IPMI_HOST_IFACE}; \
1367+ skipping IPMIHostInterface write"
1368+ ) ;
1369+ return Ok ( ( ) ) ;
1370+ }
1371+
1372+ use crate :: model:: system:: IpmiHostInterface ;
1373+ let url = format ! ( "Systems/{}" , self . s. system_id( ) ) ;
1374+ let body = HashMap :: from ( [ (
1375+ "IPMIHostInterface" ,
1376+ IpmiHostInterface {
1377+ service_enabled : enabled,
1378+ } ,
1379+ ) ] ) ;
13231380 self . s
13241381 . client
13251382 . patch ( & url, body)
13261383 . 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- } )
13401384 . map ( |_status_code| ( ) )
13411385 }
13421386
1387+ /// Get whether SSIF in-band access is enabled via `IPMIHostInterface` on `Systems/{id}`.
1388+ /// Used for MGX C2 systems that lack the KCSInterface endpoint.
1389+ /// Returns `false` when the BMC firmware is older than 01.05.01.
1390+ async fn get_ipmi_host_interface_enabled ( & self ) -> Result < bool , RedfishError > {
1391+ if !self . bmc_supports_ipmi_host_iface ( ) . await ? {
1392+ let smc_bmc_ip = self . s . client . host ( ) ;
1393+ tracing:: warn!(
1394+ smc_bmc_ip,
1395+ "MGX C2 BMC firmware is older than {MIN_BMC_FW_IPMI_HOST_IFACE}; \
1396+ IPMIHostInterface unavailable, reporting disabled"
1397+ ) ;
1398+ return Ok ( false ) ;
1399+ }
1400+
1401+ let system = self . s . get_system ( ) . await ?;
1402+ let iface = system. ipmi_host_interface . ok_or_else ( || {
1403+ RedfishError :: MissingKey {
1404+ key : "IPMIHostInterface" . to_string ( ) ,
1405+ url : format ! ( "Systems/{}" , self . s. system_id( ) ) ,
1406+ }
1407+ } ) ?;
1408+ Ok ( iface. service_enabled )
1409+ }
1410+
13431411 async fn is_host_interface_enabled ( & self ) -> Result < bool , RedfishError > {
13441412 let url = format ! ( "Managers/{}/HostInterfaces" , self . s. manager_id( ) ) ;
13451413 let host_interface_ids = self . s . get_members ( & url) . await ?;
@@ -1581,7 +1649,13 @@ impl Bmc {
15811649 Ok ( by_name)
15821650 }
15831651
1584- // Check if this is a Grace-Grace SMC (ARS-121L-DNR) that needs host_interface enabled
1652+ /// MGX C2 systems use SSIF instead of x86 KCS, so the KCSInterface
1653+ /// endpoint doesn't exist. Detect them by matching the system model.
1654+ async fn is_mgx_c2 ( & self ) -> Result < bool , RedfishError > {
1655+ let model = self . s . get_system ( ) . await ?. model . unwrap_or_default ( ) ;
1656+ Ok ( MGX_C2_MODELS . iter ( ) . any ( |m| model. contains ( m) ) )
1657+ }
1658+
15851659 async fn is_grace_grace_smc ( & self ) -> Result < bool , RedfishError > {
15861660 Ok ( self
15871661 . s
0 commit comments