| # Onboarding NPIs to tlBMC |
| |
| go/tlbmc-onboarding-npis |
| |
| <!--* |
| # Document freshness: For more information, see go/fresh-source. |
| freshness: { owner: 'tlbmc-dev' reviewed: '2025-03-12' } |
| *--> |
| |
| This document outlines the additions necessary to Entity-Manager (EM) |
| configuration files to onboard an NPI to tlBMC. |
| [Entity-Manager](https://github.com/openbmc/entity-manager) provides mappings |
| from hardware components to software components within a system, these mappings |
| are managed through EM configuration files as described here. |
| |
| Note: tlBMC requires only additions to existing EM config files, no deletions or |
| modifications of existing properties should occur. This allows tlBMC to be |
| backward compatible with existing bmcweb without conflicting behavior. Original |
| design doc can be viewed at go/tlbmc-data-completeness |
| |
| Tip: Existing daemons, e.g. PSU sensors, will still coexist if not disabled |
| explicitly. Whether these should be configured depends on the use case. If the |
| sensor is only used in telemetry reporting not control, e.g. PID, tlBMC can |
| fully replace existing daemons. In this case, see the section **SkipDbusRead |
| Property** which describes how to disable sensor polling over dbus to |
| significantly improve CPU utilization. |
| |
| [TOC] |
| |
| ## Chassis |
| |
| ### ProbeV2 |
| |
| The addition of the ProbeV2 field makes the config detectable by tlBMC. The |
| value of the `IpmiFru` JSON within the ProbeV2 field corresponds with the |
| existing `Probe` field in EM configs. `BOARD_CONFIG_NAME`, |
| `PRODUCT_CONFIG_NAME`, and `BOARD_PART_NUMBER` are valid probe fields, and if a |
| scanned FRU matches these fields, the object will be created from this config. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ProbeV2": { |
| "IpmiFru": { |
| "BOARD_PRODUCT_NAME": "foo" |
| } |
| }, |
| ... |
| ``` |
| |
| The `ProbeV2` field can also be set to `TRUE` to always be populated regardless |
| of matching a scanned FRU, or it can be set to `FALSE` to be ignored. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ProbeV2": "TRUE", |
| ... |
| ``` |
| |
| #### Logical AND and OR Syntax |
| |
| Logical AND and OR syntax is supported for the IpmiFru field if given an array |
| of JSON objects instead of a single JSON. |
| |
| The logical AND operation is applied for all fields within a single IpmiFru |
| JSON. For the matcher to evaluate as True, every field must be a match. |
| |
| An example definition: |
| |
| ``` |
| // In this example, the scanned FRU must have both the BOARD_PRODUCT_NAME = foo |
| // and the BOARD_PART_NUMBER = 1234567890 to successfully probe to True |
| ... |
| "ProbeV2": { |
| "IpmiFru": [ |
| { |
| "BOARD_PRODUCT_NAME": "foo", |
| "BOARD_PART_NUMBER": "1234567890" |
| } |
| ] |
| } |
| ... |
| ``` |
| |
| The logical OR operation is applied between matcher objects within the array of |
| IpmiFru JSONs. If any one of the matchers evaluates to True, considering the |
| logical AND syntax above, the Probe will be evaluated as True. |
| |
| An example definition: |
| |
| ``` |
| // In this example, the scanned FRU must have either the BOARD_PRODUCT_NAME = foo |
| // or the BOARD_PART_NUMBER = 1234567890 to successfully probe to True. If either |
| // field matches, the probe will be successful regardless of the value of the other field |
| ... |
| "ProbeV2": { |
| "IpmiFru": [ |
| { |
| "BOARD_PRODUCT_NAME": "foo" |
| }, |
| { |
| "BOARD_PART_NUMBER": "1234567890" |
| } |
| ] |
| } |
| ... |
| ``` |
| |
| Note: `FOUND` behaviors are not yet supported by tlBMC. |
| |
| ### ResourceType |
| |
| `ResourceType` has parity with the `Type` field in existing EM configs. |
| |
| Supported resource types are defined in `tlbmc/resource/resource.proto`. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ResourceType": "RESOURCE_TYPE_BOARD", |
| ... |
| ``` |
| |
| #### ChassisType |
| |
| For configs of `RESOURCE_TYPE_BOARD` an `ChassisType` is needed as a additional |
| field. This field has parity with the EM config field |
| `xyz.openbmc_project.Inventory.Item.Chassis.ChassisType`. This field will |
| default to type `RackMount`. |
| |
| Supported chassis types are defined in `tlbmc/resource/resource.proto`. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ChassisType": "CHASSIS_TYPE_COMPONENT", |
| ... |
| ``` |
| |
| ### Downstream/Upstream Ports |
| |
| Physical ports are modeled as downstream/upstream ports between Chassis. These |
| are fields in the `Exposes` array of a config and may correspond to existing |
| ports. Similar to existing EM configs, a port on an upstream chassis to a |
| downstream chassis has a `Label` field, used to populate the devpath. These |
| ports will have the `Type` of `PortDownstream`. On the downstream chassis, there |
| will be a corresponding port with the same name, but the `Type` will be |
| `UpstreamConnection`. |
| |
| Tip: Only one chassis (the root chassis) will have no `UpstreamConnection` or |
| `CableUpstreamConnection`. This is enforced by tlBMC and the store will fail to |
| create if this condition is not true. |
| |
| Note: In contrast to Entity Manager, "Upstream" and "Downstream" in tlBMC refers |
| to the relationship of a component that is upstream/downstream of the component |
| where the port exists. For instance, a root chassis has no `UpstreamConnection`, |
| but can have many `PortDownstream` since all other components must be downstream |
| in relation to the root chassis. In traditional Entity Manager, |
| upstream/downstream refers to the role of the component directly. |
| |
| An example of a `PortDownstream` port: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Label": "bar", |
| "Name": "Foo $bus Port", |
| "Type": "PortDownstream", |
| "User": "tlbmc" |
| }, |
| ... |
| ], |
| ``` |
| |
| and the corresponding `UpstreamConnection` port: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Name": "Foo $bus Port", |
| "Type": "UpstreamConnection", |
| "User": "tlbmc" |
| }, |
| ... |
| ], |
| ``` |
| |
| Note: The name field must match exactly between `PortDownstream` and |
| `UpstreamConnection` ports. This is how topology is created and `contains` and |
| `contained_by` associations are populated. |
| |
| ### Substitution |
| |
| Any fields in the `Exposes` field, including all Port configurations will |
| support `$bus` and `$index` substitution. This includes substitution with |
| mathematical expressions. Mathematical expressions are evaluated strictly left |
| to right in substitution and will stop at the end of the string or first |
| non-numeric/operator character encountered. An ill-formed mathematical |
| expression will cause parsing error. |
| |
| Examples of substitution: |
| |
| ``` |
| Assuming bus = 3, |
| |
| "Exposes": [ |
| { |
| "Label": "IO0", |
| "Name": "Port $bus", |
| "Type": "PortDownstream", |
| "REASON": "Valid, simple bus substitution. Evaluates to `Port 3`." |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus - 5 * 3 + 54", |
| "Type": "PortDownstream", |
| "REASON": "Valid, bus substitution with mathematical expression. Evaluates to `Port 48`." |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus - 5 * 3 + 54 something", |
| "Type": "PortDownstream", |
| "REASON": "Valid, simple bus substitution with mathematical expression and remainder. Evaluates to `Port 48 something`." |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus / 0", |
| "Type": "PortDownstream", |
| "REASON": "Invalid, division by zero" |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus - something", |
| "Type": "PortDownstream", |
| "REASON": "Invalid, hanging operator" |
| } |
| ] |
| |
| ``` |
| |
| ### PartLocationType |
| |
| `PartLocationType` has parity with the `xyz.openbmc_project.Inventory.Connector` |
| field in existing EM configs. |
| |
| PartLocationTypes are defined in |
| [topology_config.proto](https://gbmc.googlesource.com/gbmcweb/+/refs/heads/master/tlbmc/topology_config.proto). |
| |
| Note: All subfrus MUST have a PartLocationType of PART_LOCATION_TYPE_EMBEDDED. |
| tlBMC will error if a subfru is misconfigured to not use the embedded type. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "PartLocationType": "PART_LOCATION_TYPE_SLOT", |
| ... |
| ``` |
| |
| ### Processors |
| |
| `Processor` is a field unique to tlBMC, since these can no longer be detected |
| through dbus, they must be defined in the static configuration. Processors are |
| defined in the `Exposes` field of a config, with a `Name` and `"Type": |
| "Processor"`. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Name": "cpu0", |
| "Type": "Processor", |
| "User": "tlbmc" |
| }, |
| ... |
| ], |
| ``` |
| |
| ### Storage |
| |
| `Storage` has parity with the `xyz.openbmc_project.Inventory.Item.Storage` |
| field. This field is a JSON with a single `Id` field. The storage field is used |
| to create the link to the associated Storage object in the redfish query |
| response, the value of `Id` should be the `Id` of the associated Storage object. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "Storage": { |
| "Id": "foo_storage_$index" |
| }, |
| ... |
| ``` |
| |
| ### Asset |
| |
| `Asset` has parity with the `xyz.openbmc_project.Inventory.Decorator.Asset` |
| field in existing EM configs. This field allows users to override the |
| Manufacturer, Model, PartNumber, or SerialNumber in the redfish response. Valid |
| substitutions are defined in the same way as in the OpenBMC definition, for |
| example: `$PRODUCT_PART_NUMBER` or `$BOARD_MANUFACTURER` will substitute the |
| respective field from the scanned FRU object. Fields can also be overridden with |
| any string that is not prefixed with `$`. |
| |
| Tip: if a substitution is attempted (prefixed with `$`) but fails, tlBMC will |
| error. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "Asset": { |
| "Manufacturer": "$BOARD_MANUFACTURER", |
| "Model": "foo", |
| "PartNumber": "$BOARD_PART_NUMBER", |
| "SerialNumber": "$BOARD_SERIAL_NUMBER", |
| "User": "tlbmc" |
| }, |
| ... |
| ``` |
| |
| ### Ad-Hoc Frus |
| |
| Certain FRUs require special handling as they may not be scannable when bmcweb |
| comes up. For this purpose, we have created something called an "Ad-Hoc Fru |
| Scanner". This will allow you to configure exactly how you want to scan a FRU. |
| |
| An example is below: |
| |
| ``` |
| "AdHocFruConfig": { |
| "name": "AdHocFru", |
| "i2c_common_config": { |
| "bus": "4", |
| "address": "83" |
| }, |
| "delay_between_reads": "0.001s", |
| "periodic_scan_config": [ |
| { |
| "scan_interval": "10s", |
| "scan_count": 6 |
| }, |
| { |
| "scan_interval": "60s", |
| "scan_count": 9 |
| }, |
| { |
| "scan_interval": "600s", |
| "scan_count": 5 |
| } |
| ] |
| } |
| ``` |
| |
| The proto definition is found |
| [here](https://gbmc.googlesource.com/gbmcweb/+/refs/heads/master/tlbmc/ad_hoc_fru_config.proto). |
| |
| > NOTE: This must go inside the ProbeV2 Json Object. |
| |
| ## Subfrus |
| |
| Some objects are not uniquely FRU-detectable like a regular chassis and are |
| logically separate from a given chassis, defined in their own EM configuration. |
| These objects are considered Subfrus. |
| |
| ### Assemblies |
| |
| Assemblies are subfrus that may have their own devpath, Manufacturer, Serial |
| Number, Model, etc. Assemblies represent a component that is logically separate |
| but are not I2C scannable and do not have an eeprom. |
| |
| An assembly is always defined with `"ResourceType": "RESOURCE_TYPE_ASSEMBLY"`, |
| this has parity with the `"Type": "Component"` field in existing EM configs. |
| |
| Assemblies will have PartLocationType of either `PART_LOCATION_TYPE_EMBEDDED` or |
| `PART_LOCATION_TYPE_SLOT`. This should follow what is in the existing EM config. |
| |
| * `PART_LOCATION_TYPE_EMBEDDED`: This represents an assembly that is |
| physically attached to the parent board and cannot be replaced on its own. |
| An example of this configuration is provided below: |
| |
| ``` |
| { |
| "Exposes": [ |
| { |
| "Name": "Foo Sensor Controller 0 Port", |
| "Type": "UpstreamConnection", |
| "User": "tlbmc" |
| }, |
| { |
| "Name": "foo_0_variant_a_sensors", |
| "Address": "0x68", |
| "Bus": "64", |
| "Labels": [ |
| "vin1", |
| "iin1", |
| "pin1" |
| ], |
| "vin1_Name": "foo_0_a_vin", |
| "iin1_Name": "foo_0_a_iin", |
| "pin1_Name": "foo_0_a_pin", |
| "Type": "XDPE1A2G5B", |
| "TlbmcOwned": true, |
| "SkipDbusRead": true |
| } |
| ], |
| "Name": "Foo Sensor Controller 0 Var A", |
| "ProbeV2": { |
| "IpmiFru": { |
| "PRODUCT_PRODUCT_NAME": "^Foo_Parent_Variant_A$" |
| } |
| }, |
| "ResourceType": "RESOURCE_TYPE_ASSEMBLY", |
| "PartLocationType": "PART_LOCATION_TYPE_EMBEDDED", |
| "Type": "Component", |
| "xyz.openbmc_project.Inventory.Connector.Embedded": {}, |
| "xyz.openbmc_project.Inventory.Decorator.Asset": { |
| "Manufacturer": "Foo_Manufacturer", |
| "Model": "Foo_Controller_Model" |
| } |
| }, |
| ``` |
| |
| * `PART_LOCATION_TYPE_SLOT`: This represents a component that is attached to |
| the parent chassis through a slot and can be individually replaced, although |
| it is not FRU detectable. |
| An example of this configuration is provided below: |
| |
| ``` |
| { |
| "Exposes": [ |
| { |
| "Name": "Bar Port", |
| "Type": "UpstreamConnection", |
| "User": "tlbmc" |
| }, |
| { |
| "Address": "0x42", |
| "Bus": "21", |
| "Name": "bar_sensor1", |
| "Name1": "bar_sensor2", |
| "Name2": "bar_sensor3", |
| "Labels": [ |
| "sensor1", |
| "sensor2", |
| "sensor3" |
| ], |
| "Type": "TMP75", |
| "TlbmcOwned": true, |
| "SkipDbusRead": true |
| } |
| ], |
| "Name": "Bar Assembly", |
| "ProbeV2": "TRUE", |
| "ResourceType": "RESOURCE_TYPE_ASSEMBLY", |
| "PartLocationType": "PART_LOCATION_TYPE_SLOT", |
| "Type": "Component", |
| "xyz.openbmc_project.Inventory.Connector.Slot": {}, |
| "Asset": { |
| "Model": "Bar_Assembly_Model" |
| } |
| } |
| |
| ``` |
| |
| Assemblies may have their own sensors defined. These will be propagated to the |
| parent chassis object since Assemblies cannot directly own sensors. This allows |
| for sensors that will only be present on certain board variants to be defined in |
| a separate config without having to duplicate the whole parent board |
| configuration. |
| |
| Note: tlBMC does not currently own the Assembly redfish path. The sensor |
| propagation described above is the only feature supported by tlBMC. Querying |
| assemblies at `/redfish/v1/Chassis/{ChassisId}/Assembly` will be routed through |
| non-tlBMC Bmcweb. |
| |
| An example definition: |
| |
| ``` |
| { |
| "Exposes": [ |
| { |
| "Name": "Assembly A Port", |
| "Type": "UpstreamConnection", |
| "User": "tlbmc" |
| }, |
| { |
| "Name": "variant_a_psu_sensor", |
| "Address": "0x68", |
| "Bus": "64", |
| "Labels": [ |
| "vin1", |
| "pin1", |
| "vout1", |
| "iout1" |
| ], |
| "vin1_Name": "foo_a_vin", |
| "pin1_Name": "foo_a_pin", |
| "vout1_Name": "foo_a_vout", |
| "iout1_Name": "foo_a_iout", |
| "Type": "ADM1266", |
| "TlbmcOwned": true, |
| "SkipDbusRead": true |
| } |
| ], |
| "Name": "Foo Assembly A", |
| "ProbeV2": { |
| "IpmiFru": { |
| "PRODUCT_PRODUCT_NAME": "^FooVariant$" |
| } |
| }, |
| "ResourceType": "RESOURCE_TYPE_ASSEMBLY", |
| "PartLocationType": "PART_LOCATION_TYPE_EMBEDDED", |
| "Type": "Component", |
| "xyz.openbmc_project.Inventory.Connector.Embedded": {}, |
| ... |
| }, |
| ``` |
| |
| The sensors defined here in the `variant_a_psu_sensor` config would be |
| propagated to the parent board, `Foo` which would be expected to have a |
| `PortDownstream` connection with the name `Assembly A Port`. Note again that |
| tlBMC does not own Assemblies, but due to `"Type": "Component",` being in the |
| config, `Foo Assembly A` can be queried under |
| `/redfish/v1/Chassis/Foo/Assembly`. |
| |
| ## Sensor |
| |
| Note: For any sensor configuration to be parsed by tlBMC, must add the property |
| `"TlbmcOwned": true` to the sensor EM config. This **must** be added per sensor. |
| tlBMC will error if a sensor is marked as TlbmcOwned but is not a supported |
| type. |
| |
| ### HWMon Temp Sensors |
| |
| There are no specific changes that need to be made to HWMon Temp Sensor configs |
| to be compatible with tlBMC. |
| |
| Not all sensor types are currently supported by tlBMC, see |
| kSupportedHwmonTempSensorTypes in tlbmc/configs/entity_config_json_impl.cc. |
| |
| ### PSU Sensors |
| |
| There are no specific changes that need to be made to PSU Sensor configs to be |
| compatible with tlBMC. |
| |
| Not all sensor types are currently supported by tlBMC, see |
| kSupportedPsuSensorTypes in tlbmc/configs/entity_config_json_impl.cc. |
| |
| ### SkipDbusRead Property |
| |
| For Hwmon Temp Sensors and PSU Sensors, if the dbus interface of the sensor is |
| not being consumed by other processes such as PID loop, we can stop polling over |
| dbus to significantly reduce CPU usage on BMC. By adding the property |
| `"SkipDbusRead": true` in the sensor configuration, the dbus-sensor daemon will |
| stop polling the sensor. tlBMC backend will still supply the reading as expected |
| when queried through redfish. |
| |
| ### Fan Controller |
| |
| All I2C fans need a corresponding fan controller defined in the EM config that |
| share the bus/address combination as the fans. Without this field, no I2C fans |
| will be populated. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Address": "0x2c", |
| "Bus": "34", |
| "Name": "FAN_CONTROLLER", |
| "Type": "MAX31790", |
| "TlbmcOwned": true |
| }, |
| ... |
| ], |
| ``` |
| |
| ### I2C Fans (Tach/PWM) |
| |
| For all I2C configs, there must be a JSON `Connector` field that contains at |
| least `Pwm` (the index of the fan) and `PwmName` (name of the PWM Fan sensor). |
| Some configs may already have this present. No other changes are required to |
| make I2C fans compatible with tlBMC. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Address": "0x2c", |
| "BindConnector": "fan1_tach_connector", |
| "Bus": "32", |
| "Connector": { |
| "Pwm": 1, |
| "PwmName": "fan1_pwm" |
| }, |
| "EntityId": "0x1A", |
| "EntityInstance": "0x01", |
| "Index": 1, |
| "Name": "fan1_tach", |
| ... |
| } |
| ... |
| ] |
| ``` |
| |
| ### Related Item |
| |
| `RelatedItem` is a field unique to tlBMC, since RelatedItem can no longer be |
| detected through dbus calls, they must be defined in the static configuration. |
| The `RelatedItem` of any sensor must be defined as a `RelatedItem` JSON object |
| within the sensor config. It contains the fields `Id` (the redfish Id of the |
| related item) and `Type` (the `ResourceType` of the related item). By default, |
| the related item will be the parent chassis object. This field only needs to be |
| overridden in the case that the related item should be a different object e.g. a |
| fan. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| ... |
| "Name": "fan1_tach", |
| "RelatedItem": { |
| "Id": "Fan1", |
| "Type": "RESOURCE_TYPE_FAN", |
| "User": "tlbmc" |
| }, |
| ... |
| } |
| ... |
| ] |
| ``` |
| |
| ## Cables |
| |
| ### Cable Downstream/Upstream Connections |
| |
| Cables are defined in separate EM config files from their associated Chassis, |
| and are associated through `CableDownstreamConnection` or |
| `CableUpstreamConnection` type ports in their `Exposes` field. All Cable configs |
| should have their `ProbeV2` field set to `TRUE` (since Cables cannot be detected |
| over I2C) and their `ResourceType` set to `RESOURCE_TYPE_CABLE`. The associated |
| upstream or downstream Chassis must have a port with the opposite connection |
| type and the same `Name` to form a connection. Also defined in the exposed port |
| is `CableId`. This field must match the `Name` field in the Cable config. |
| |
| Note: There is a difference between the `Name` field in the port config (name of |
| the port) and `CableId`, which should match the `Name` of a Cable config JSON |
| |
| An example Cable config: |
| |
| ``` |
| { |
| "Exposes": [ |
| { |
| "Name": "Foo Cable Downstream", |
| "CableId": "FooCable", |
| "Type": "CableUpstreamConnection", |
| "User": "tlbmc" |
| }, |
| { |
| "Name": "Foo Cable Upstream", |
| "CableId": "FooCable", |
| "Type": "CableDownstreamConnection", |
| "User": "tlbmc" |
| } |
| ], |
| "Name": "FooCable", |
| "Probe": "xyz.openbmc_project.Network.EthernetInterface({'InterfaceName': '^foo$', 'LinkUp': true})", |
| "ProbeV2": "TRUE", |
| "ResourceType": "RESOURCE_TYPE_CABLE", |
| "Type": "Cable" |
| } |
| ``` |
| |
| The upstream chassis exposes a CableDownstreamConnection: |
| |
| ``` |
| "Exposes": { |
| ... |
| { |
| "Name": "Foo Cable Downstream", |
| "CableId": "FooCable", |
| "Type": "CableDownstreamConnection", |
| "User": "tlbmc" |
| }, |
| ... |
| }, |
| ``` |
| |
| and the downstream chassis exposes a CableUpstreamConnection: |
| |
| ``` |
| "Exposes": { |
| ... |
| { |
| "Name": "Foo Cable Upstream", |
| "CableId": "FooCable", |
| "Type": "CableUpstreamConnection", |
| "User": "tlbmc" |
| }, |
| ... |
| }, |
| ``` |