From cdaca4dce1fae58b3cf357700adfb5fd5cf8cf20 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 18 May 2026 12:04:57 -0700 Subject: [PATCH 1/5] update `gateway-messages` and `gateway-ereport-messages` (#2519) This commit updates our Git dep on the `management-gateway-service` repo to oxidecomputer/management-gateway-service@745a508cb97b7ca9b4c10ec9592c980eb769b10d. This is primarily in order to pick up oxidecomputer/management-gateway-service#488, which adds new `DeviceCapabilities` bits for PMBus devices and for devices which have VPD. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d262f6ec6..1a2995737 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3146,7 +3146,7 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "gateway-ereport-messages" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/management-gateway-service#177c9c719e12896c566a1b6b5416c9bc686531d3" +source = "git+https://github.com/oxidecomputer/management-gateway-service#745a508cb97b7ca9b4c10ec9592c980eb769b10d" dependencies = [ "zerocopy 0.8.27", ] @@ -3154,7 +3154,7 @@ dependencies = [ [[package]] name = "gateway-messages" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/management-gateway-service#177c9c719e12896c566a1b6b5416c9bc686531d3" +source = "git+https://github.com/oxidecomputer/management-gateway-service#745a508cb97b7ca9b4c10ec9592c980eb769b10d" dependencies = [ "bitflags 2.9.4", "hubpack", From 7115f2641c3072f52faf7f86257454028fef7ed4 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 18 May 2026 12:42:03 -0700 Subject: [PATCH 2/5] build-i2c: add `PmbusDeviceDescription` (#2519) This commit adds a new `PmbusDeviceDescription` to the `build-i2c` crate. `I2cDeviceDescription` now includes an optional `PmbusDeviceDescription` field which is `Some` for PMBus devices. The presence of this field can be used to determine when a device is a PMBus device, such as when determining if the `IS_PMBUS` capability should be advertised. I did this by adding a whole new optional struct rather than just sticking an `is_pmbus` bool on `I2cDeviceDescription`, because I felt like future code, such as what @jamesmunns is working on for #2463, will almost certainly want a way to get a PMBus device's rail and phase names in codegen that lives outside of `build-i2c` (i.e. in `task-validate-api`'s build script). Currently, there isn't a nice way to get this stuff in codegen that lives outside of `build-i2c`, so this should help make that nicer. --- build/i2c/src/lib.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/build/i2c/src/lib.rs b/build/i2c/src/lib.rs index 6774116bd..3ca2e1487 100644 --- a/build/i2c/src/lib.rs +++ b/build/i2c/src/lib.rs @@ -1782,6 +1782,27 @@ pub struct I2cDeviceDescription { pub sensors: Vec, pub device_id: Option, pub name: Option, + /// If this is a PMBus device, this field contains additional data about the + /// PMBus device to be used for generating PMBus-y code. + pub pmbus: Option, +} + +#[derive(Debug, Clone)] +pub struct PmbusDeviceDescription { + pub rails: Vec, +} + +#[derive(Debug, Clone)] +pub struct PmbusRailDescription { + pub name: String, + pub phases: Vec, +} + +impl I2cDeviceDescription { + /// Returns `true` if this device is a PMBus device. + pub fn is_pmbus(&self) -> bool { + self.pmbus.is_some() + } } /// @@ -1801,12 +1822,50 @@ pub fn device_descriptions() -> impl Iterator { g.devices.into_iter().zip(sensors.device_sensors).map( |(device, sensors)| { let device_id = device.refdes.as_ref().map(Refdes::to_component_id); + let pmbus = device.power.as_ref().and_then(|power| { + if !power.pmbus { + return None; + } + + let rails = power + .rails + .iter() + .flatten() + .enumerate() + .map(|(i, rail)| { + let phases = if let Some(ref phases_list) = power.phases + { + match phases_list.get(i) { + Some(phases) => phases.clone(), + None => { + // The rails and phases arrays are expected to + // be the same length; if this is not the case, + // the config file is malformed! + panic!( + "PMBus device {device_id:?} is missing + phases for its {i}th rail ({rail})", + ); + } + } + } else { + Vec::new() + }; + PmbusRailDescription { + name: rail.clone(), + phases, + } + }) + .collect::>(); + Some(PmbusDeviceDescription { rails }) + }); + I2cDeviceDescription { device: device.device, description: device.description, sensors, device_id, name: device.name, + pmbus, } }, ) From 9526186ac4baf9e80efaefc77fe9f0777b2520ea Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 20 May 2026 09:41:57 -0700 Subject: [PATCH 3/5] control-plane-agent: set `IS_PMBUS` if a device is PMBus (#2519) Now that we have the appropriate information available at codegen-time to know if an I2C device is a PMbus device, we can actually set this flag. Yay! Now the control plane can know which devices are PMBus! --- task/control-plane-agent/src/inventory.rs | 3 +++ task/validate-api/build.rs | 2 ++ task/validate-api/src/lib.rs | 1 + 3 files changed, 6 insertions(+) diff --git a/task/control-plane-agent/src/inventory.rs b/task/control-plane-agent/src/inventory.rs index f9bead6fa..1ccc860bd 100644 --- a/task/control-plane-agent/src/inventory.rs +++ b/task/control-plane-agent/src/inventory.rs @@ -123,6 +123,9 @@ impl Inventory { }; let mut capabilities = DeviceCapabilities::empty(); + if device.is_pmbus { + capabilities |= DeviceCapabilities::IS_PMBUS; + } if !device.sensors.is_empty() { capabilities |= DeviceCapabilities::HAS_MEASUREMENT_CHANNELS; } diff --git a/task/validate-api/build.rs b/task/validate-api/build.rs index 869ef1c4d..a0dc35ce8 100644 --- a/task/validate-api/build.rs +++ b/task/validate-api/build.rs @@ -59,6 +59,7 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { let mut id2idx = std::collections::BTreeMap::new(); for (idx, dev) in devices.into_iter().enumerate() { + let is_pmbus = dev.is_pmbus(); writeln!(file, " DeviceDescription {{")?; writeln!(file, " device: {:?},", dev.device)?; writeln!(file, " description: {:?},", dev.description)?; @@ -83,6 +84,7 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { ); missing_ids += 1; }; + writeln!(file, " is_pmbus: {is_pmbus:?},")?; writeln!(file, " sensors: &[")?; for s in dev.sensors { writeln!(file, " SensorDescription {{")?; diff --git a/task/validate-api/src/lib.rs b/task/validate-api/src/lib.rs index 3884406f0..b55d93cd1 100644 --- a/task/validate-api/src/lib.rs +++ b/task/validate-api/src/lib.rs @@ -75,6 +75,7 @@ pub struct DeviceDescription { pub description: &'static str, pub sensors: &'static [SensorDescription], pub id: [u8; MAX_ID_LENGTH], + pub is_pmbus: bool, } include!(concat!(env!("OUT_DIR"), "/device_descriptions.rs")); From d6613082b7e58680a3f04846180ed228e447193f Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 22 May 2026 14:11:52 -0700 Subject: [PATCH 4/5] build-i2c: refactor `PmbusDeviceDescription` a bit (#2519) As suggested by @jamesmunns in [this comment][1]. I think this is a bit easier to follow now, thanks James! [1]: https://github.com/oxidecomputer/hubris/pull/2519#discussion_r3282888353 --- build/i2c/src/lib.rs | 67 +++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/build/i2c/src/lib.rs b/build/i2c/src/lib.rs index 3ca2e1487..4305f8ccf 100644 --- a/build/i2c/src/lib.rs +++ b/build/i2c/src/lib.rs @@ -1827,35 +1827,44 @@ pub fn device_descriptions() -> impl Iterator { return None; } - let rails = power - .rails - .iter() - .flatten() - .enumerate() - .map(|(i, rail)| { - let phases = if let Some(ref phases_list) = power.phases - { - match phases_list.get(i) { - Some(phases) => phases.clone(), - None => { - // The rails and phases arrays are expected to - // be the same length; if this is not the case, - // the config file is malformed! - panic!( - "PMBus device {device_id:?} is missing - phases for its {i}th rail ({rail})", - ); - } - } - } else { - Vec::new() - }; - PmbusRailDescription { - name: rail.clone(), - phases, - } - }) - .collect::>(); + let rails = match (power.rails.as_ref(), power.phases.as_ref()) + { + (Some(rails), Some(phases)) => { + assert_eq!( + rails.len(), + phases.len(), + "invalid config: PMBus device {device_id:?}'s \ + `power.rails` and `power.phases` lists are not \ + the same length" + ); + rails + .iter() + .cloned() + .zip(phases.iter().cloned()) + .map(|(name, phases)| PmbusRailDescription { + name, + phases, + }) + .collect() + } + (Some(rails), None) => rails + .iter() + .cloned() + .map(|name| PmbusRailDescription { + name, + phases: Vec::new(), + }) + .collect(), + (None, Some(_)) => { + panic!( + "invalid config: PMBus device {device_id:?} \ + defines a `power.phases` list, but not a \ + `power.rails` list" + ); + } + (None, None) => Vec::new(), + }; + Some(PmbusDeviceDescription { rails }) }); From 66552683c73f8a2cd8b85b65fb9d6671ac00cb3d Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 26 May 2026 14:22:48 -0700 Subject: [PATCH 5/5] validate-api: fix ugly whitespace in codegen (#2519) --- task/validate-api/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/task/validate-api/build.rs b/task/validate-api/build.rs index a0dc35ce8..4f0d3c8b4 100644 --- a/task/validate-api/build.rs +++ b/task/validate-api/build.rs @@ -65,7 +65,7 @@ fn write_pub_device_descriptions() -> anyhow::Result<()> { writeln!(file, " description: {:?},", dev.description)?; if let Some(id) = dev.device_id { if let Ok(component) = SpComponent::try_from(id.as_ref()) { - write!(file, " id: {:?},", component.id)?; + writeln!(file, " id: {:?},", component.id)?; if id2idx.insert(component.id, idx).is_some() { println!("cargo::error=duplicate device id {id:?}",); duplicate_ids += 1;