Tutorial

How to port OpenBMC

With code modifications in the local system

By

Lei YU

This tutorial describes how to port OpenBMC to a new machine, including changes to OpenBMC layers, the Linux® kernel, and several related components such as hwmon sensor, LED, and inventory.

It also includes information about how to modify the code in the local system to build OpenBMC with local changes without changing the recipe.

Porting to a new machine

To port OpenBMC to a new machine, complete the following steps:

  1. Add the layer of the new machine in meta-openbmc.
  2. Make the kernel changes for the new machine such as configurations and dts.
  3. Add the workbook of the new machine.
  4. Make the required changes that are specific to the new machine, for example, to the hwmon sensor and LED.

Adding the machine layer

Let us take an example of adding a machine machine-name-2 in the manufacturer-1 repository.

  1. To create the layer, you can either reuse the existing repository meta-manufacturer-1, or create a new repository.
  2. Create a meta-machine-name-2 directory in the meta_manufacturer-1 repository for the machine.
  3. Update the configuration files with the machine name machine-name-2 instead of manufacturer-1.
  4. Create a conf directory in meta_manufacturer-1, following meta-ibm/conf.

After you complete these steps, the directory tree is similar to the following structure:

meta-manufacturer-1/
├── conf
│   └── layer.conf
└── meta-machine-name-2
    ├── conf
    │   ├── bblayers.conf.sample
    │   ├── conf-notes.txt
    │   ├── layer.conf
    │   ├── local.conf.sample
    │   └── machine
    │       └── machine-name-2.conf
    ├── recipes-kernel
    │   └── linux
    │       ├── linux-obmc
    │       │   └── machine-name-2.cfg  # Machine specific kernel configs
    │       └── linux-obmc_%.bbappend
    └── recipes-phosphor
        ...
        ├── images
        │   └── obmc-phosphor-image.bbappend  # Machine specfic apps/services to include
        └── workbook
            ├── machine-name-2-config
            │   └── Machine-name-2.py  # Machine specific workbook (see below)
            └── machine-name-2-config.bb

The above directory tree creates a new layer in the machine.

TEMPLATECONF=meta-manufacturer-1/meta-machine-name-2/conf . oe-init-build-env
bitbake obmc-phosphor-image

Making kernel changes

This section describes how you can make changes to the kernel to port the OpenBMC to a new machine. The device tree is in https://github.com/openbmc/linux/tree/dev-4.13/arch/arm/boot/dts. For examples, see aspeed-bmc-opp-romulus.dts or a similar machine. Complete the following steps to make kernel changes:

  1. Add the new machine device tree:

    • Describe the GPIOs, for example, LED, FSI, and gpio-keys. You can obtain this information from the schematic.
    • Describe the i2c buses and devices, which usually include various hwmon sensors.
    • Describe the other devices, such as uarts and mac.
    • Usually the flash layout does not need a change. Include the openbmc-flash-layout.dtsi file.
  2. Modify the Makefile to build the device tree.

Notes:

  • In dev-4.10, there is common and machine-specific initialization code in arch/arm/mach-aspeed/aspeed.c which is used to perform common initializations and specific settings in each machine.
  • With dev-4.13, most of the initializations are done with the upstreamed clock and reset driver.
  • If the machine requires specific settings such as uart routing, send an mail to the mailing list for discussion.

Adding the workbook

In legacy OpenBMC, the workbook describes the machine's services, sensors, and FRUs. It is a Python configuration and is used by other services in skeleton. In the latest OpenBMC release, phosphor-xxx services are used and thus, skeleton is deprecated. However, workbook is required to make the build.

An example is meta-quanta, that defines its own configuration in the OpenBMC tree, so that it does not rely on the skeleton repository.

For OpenPOWER systems, several configurations are still used in this configuration file. For example, in Romulus.py, the configuration details are as follows:

GPIO_CONFIG['BMC_POWER_UP'] = \
        {'gpio_pin': 'D1', 'direction': 'out'}
GPIO_CONFIG['SYS_PWROK_BUFF'] = \
        {'gpio_pin': 'D2', 'direction': 'in'}

GPIO_CONFIGS = {
    'power_config' : {
        'power_good_in' : 'SYS_PWROK_BUFF',
        'power_up_outs' : [
            ('BMC_POWER_UP', True),
        ],
        'reset_outs' : [
        ],
    },
}

The PowerUp and PowerOK GPIOs are needed for the build to power on the chassis and check the power state.

Making other miscellaneous changes

Different machines have different devices, such as hwmon sensors, LEDs, and fans.

OpenBMC is designed to be configurable. Hence, you can describe such devices in different configuration files in the machine layer.

Hwmon sensors

Hwmon sensors include sensors on board, such as temperature sensors, fans, and OCC sensors. The path and name in the configuration files must match the devices in the device tree.

Here, Romulus is taken as an example. The configuration files are in the meta-romulus/recipes-phosphor/sensors repository, which includes sensors on board and sensors of OCC, where on-board sensors are through i2c, and OCC sensors are through FSI.

  • w83773g@4c.conf defines the w83773 temperature sensor containing three temperatures:

    LABEL_temp1 = "outlet"
    ...
    LABEL_temp2 = "inlet_cpu"
    ...
    LABEL_temp3 = "inlet_io"
    

    This device is defined in its device tree as w83773g@4c. When the BMC starts, the udev rule starts phosphor-hwmon and it creates temperature sensors on the following DBus objects based on the sysfs attributes.

    /xyz/openbmc_project/sensors/temperature/outlet
    /xyz/openbmc_project/sensors/temperature/inlet_cpu
    /xyz/openbmc_project/sensors/temperature/inlet_io
    
  • pwm-tacho-controller@1e786000.conf defines the fans and the configuration is similar as above. The difference is that it creates fan_tach sensors.

  • occ-hwmon.1.conf defines the OCC hwmon sensor for the master CPU. This configuration is different and phosphor-hwmon is instructed to read the label instead of directly getting the index of the sensor, because CPU cores and DIMMs could be dynamic. For example, CPU cores could be disabled and DIMMs could be pulled out.

    MODE_temp1 = "label"
    MODE_temp2 = "label"
    ...
    MODE_temp31 = "label"
    MODE_temp32 = "label"
    LABEL_temp91 = "p0_core0_temp"
    LABEL_temp92 = "p0_core1_temp"
    ...
    LABEL_temp33 = "dimm6_temp"
    LABEL_temp34 = "dimm7_temp"
    LABEL_power2 = "p0_power"
    ...
    
    • MODE_temp* = "label" indicates that if tempX is seen, the label is read, which is the sensor ID.
    • LABEL_temp* = "xxx" indicates the sensor name for the corresponding sensor ID.

      For example, if the value of temp1_input is 37000 and the value of temp1_label is 91 in sysfs, phosphor-hwmon knows temp1_input is for sensor ID 91, which is p0_core0_temp. Hence, it creates /xyz/openbmc_project/sensors/temperature/p0_core0_temp with a sensor value of 37000.

    • The power sensors do not need to read the label because all powers are available on a system.

LEDs

Several parts are involved for LEDs.

  1. In kernel dts, LEDs are described. For example, romulus dts describes three LEDs, fault, identify, and power.

    leds {
        compatible = "gpio-leds";
    
        fault {
          gpios = <&gpio ASPEED_GPIO(N, 2) GPIO_ACTIVE_LOW>;
        };
    
        identify {
          gpios = <&gpio ASPEED_GPIO(N, 4) GPIO_ACTIVE_HIGH>;
        };
    
        power {
          gpios = <&gpio ASPEED_GPIO(R, 5) GPIO_ACTIVE_LOW>;
        };
      };
    
  2. In the machine layer, LEDs are configured through YAML files to describe how it functions. For example,

    bmc_booted:
        power:
            Action: 'Blink'
            DutyOn: 50
            Period: 1000
            Priority: 'On'
    power_on:
        power:
            Action: 'On'
            DutyOn: 50
            Period: 0
            Priority: 'On'
    

    It tells the LED manager to set the power LED to blink when BMC is ready and booted, and set it on when host is powered on.

  3. During run time, the LED manager automatically sets LEDs to on/off/blink based on the above YAML configuration file.

  4. LEDs can be accessed manually through /xyz/openbmc_project/led/. For example:

    • Get identify LED state:
      curl -b cjar -k https://$bmc/xyz/openbmc_project/led/physical/identify

    • Set identify LED to blink:
      jar -k -X PUT -H "Content-Type: application/json" -d '{"data": "xyz.openbmc_project.Led.Physical.Action.Blink" }' https://$bmc/xyz/openbmc_project/led/physical/identify/attr/State

    Note: This YAML configuration can be automatically generated by phosphor-mrw-tools from its MRW. See the example of Witherspoon.

Inventories and other sensors

Inventories, other sensors (for example, CPU/DIMM temperature), and FRUs are defined in the IPMIs YAML configuration files.

For example, meta-romulus/recipes-phosphor/ipmi

  • romulus-ipmi-inventory-map defines regular inventories, such as CPU, memory, and motherboard.
  • phosphor-ipmi-fru-properties defines extra properties of the inventories.
  • phosphor-ipmi-sensor-inventory defines the sensors from IPMI.
  • romulus-ipmi-inventory-sel defines inventories used for IPMI SEL.

inventory map and fru-properties are similar in different systems. Refer to this example and make one for your system.

For ipmi-sensor-inventory, the sensors from IPMI are different in different systems. Hence, you must define your own sensors. For example:

0x08:
  sensorType: 0x07
  path: /org/open_power/control/occ0
  ...
0x1e:
  sensorType: 0x0C
  path: /system/chassis/motherboard/dimm0
  ...
0x22:
  sensorType: 0x07
  path: /system/chassis/motherboard/cpu0/core0

The first value 0x08, 0x1e, and 0x22 are the sensor IDs of IPMI, which is defined in MRW. You should follow the system's MRW to define the above configuration.

Note : The YAML configuration files can be automatically generated by phosphor-mrw-tools from its MRW, see example of Witherspoon.

Fans

phosphor-fan-presence manages all the services about fan:

  • phosphor-fan-presence checks if a fan is present, creates the fan DBus objects in the inventory, and updates the Present property.
  • phosphor-fan-monitor checks if a fan is functional and updates the Functional property of the fan Dbus object.
  • phosphor-fan-control controls the fan speed by setting the fan speed target based on conditions, for example, temperatures.
  • phosphor-cooling-type checks if the system is air-cooled or water-cooled by setting properties of the /xyz/openbmc_project/inventory/system/chassis object.

All the above services are configurable, for example, by using the YAML configuration file. Hence, the machine specific configurations are written when porting OpenBMC to a new machine.

Taking Romulus as example, it is air-cooled and has three fans without GPIO presence detection.

Fan presence

Romulus has no GPIO detection for fan. Hence, it checks for the fan tachometer sensor:

- name: fan0
  path: /system/chassis/motherboard/fan0
  methods:
    - type: tach
      sensors:
        - fan0

The YAML configuration file indicates the following data:

  • It creates the /system/chassis/motherboard/fan0 object in inventory.
  • It checks that the fan0 tachometer sensor (/sensors/fan_tach/fan0) sets Present as the property on the fan0 object.

Fan monitor

Romulus fans uses pwm to control the fan speed, where pwm ranges from 0 to 255, and the fan speed ranges from 0 to about 7000. Hence, it needs a factor and offset to map the pwm to fan speed:

- inventory: /system/chassis/motherboard/fan0
    allowed_out_of_range_time: 30
    deviation: 15
    num_sensors_nonfunc_for_fan_nonfunc: 1
    sensors:
      - name: fan0
        has_target: true
        target_interface: xyz.openbmc_project.Control.FanPwm
        factor: 21
        offset: 1600

The YAML config indicates the following data:

  1. FanPwm is used as the target interface of the tachometer sensor;
  2. The expected fan speed is calculated with the formula, target * 21 + 1600
  3. The deviation is 15%. Hence, if the fan speed is out of the expected range for more than 30 seconds, fan0 shall be set as non-functional.

Fan control

The fan control service requires four YAML configuration files:

  • zone-condition defines the cooling zone conditions. Romulus is always air-cooled. Hence, this configuration involves defining a air_cooled_chassis condition based on the cooling type property.

    - name: air_cooled_chassis
      type: getProperty
      properties:
        - property: WaterCooled
          interface: xyz.openbmc_project.Inventory.Decorator.CoolingType
          path: /xyz/openbmc_project/inventory/system/chassis
          type: bool
          value: false
    
  • zone-config defines the cooling zones. Romulus has only one zone:

    zones:
     - zone: 0
       full_speed: 255
       default_floor: 195
       increase_delay: 5
       decrease_interval: 30
    

    It defines the zone full speed and default floor speed for the fans. In this example, the fan pwm is set to 255 if it is in full speed, and set to 195 if fans are in default floor speed.

  • fan-config defines which fans are controlled in which zone and which target interface shall be used, for example, the following YAML configuration defines that the fan0 is controlled in zone0 and it would use the FanPwm interface.

     - inventory: /system/chassis/motherboard/fan0
      cooling_zone: 0
      sensors:
        - fan0
      target_interface: xyz.openbmc_project.Control.FanPwm
      ...
    
  • events-config defines the various events and its handlers (for example, which fan targets would be set in which temperature). This configuration is described with examples in example event yaml. Romulus example:

     - name: set_air_cooled_speed_boundaries_based_on_ambient
        groups:
            - name: zone0_ambient
              interface: xyz.openbmc_project.Sensor.Value
              property:
                  name: Value
                  type: int64_t
        matches:
            - name: propertiesChanged
        actions:
            - name: set_floor_from_average_sensor_value
              map:
                  value:
                      - 27000: 85
                      - 32000: 112
                      - 37000: 126
                      - 40000: 141
    
                  type: std::map<int64_t, uint64_t>
            - name: set_ceiling_from_average_sensor_value
              map:
                  value:
                      - 25000: 175
                      - 27000: 255
                 type: std::map<int64_t, uint64_t>
    

The above YAML configuration defines the fan floor and ceiling speed in zone0_ambient's different temperatures. For example,

  • When the temperature is lower than 27-degree celsius, the floor speed (pwm) is set to 85.
  • When temperature is between 27 and 32-degree celsius, the floor speed (pwm) is set to 112.

With above configurations, phosphor-fan runs the fan presence/monitor/control logic as configured specifically for the machine.

Note : Romulus fans are simple. The following are the additional functions of Witherspoon fan configuration:

  • It checks the GPIO for fan presence.
  • It checks the GPIO to determine if the system is air or water cooled.
  • It has more sensors and more events in fan control.

GPIOs

This section mainly focuses on the GPIOs that are monitored in the device tree. For example,

  • A GPIO might represent a signal of host checkstop.
  • A GPIO might represent a button press.
  • A GPIO might represent if a device is attached or not.

They are categorized as phosphor-gpio-presence for checking presences of a device, and phosphor-gpio-monitor for monitoring a GPIO.

GPIOs in the device tree

All the GPIOs to be monitored are described in the device tree. For example,

gpio-keys {
   compatible = "gpio-keys";
   checkstop {
     label = "checkstop";
     gpios = <&gpio ASPEED_GPIO(J, 2) GPIO_ACTIVE_LOW>;
     linux,code = ;
   };
   id-button {
     label = "id-button";
     gpios = <&gpio ASPEED_GPIO(Q, 7) GPIO_ACTIVE_LOW>;
     linux,code = ;
   };
 };

The following code describes two GPIO keys, one for checkstop and the other for id-button, where the key code is calculated from aspeed-gpio.h:

#define ASPEED_GPIO_PORT_A 0
#define ASPEED_GPIO_PORT_B 1
...
#define ASPEED_GPIO_PORT_Y 24
#define ASPEED_GPIO_PORT_Z 25
#define ASPEED_GPIO_PORT_AA 26
...

#define ASPEED_GPIO(port, offset) \
  ((ASPEED_GPIO_PORT_##port * 8) + offset)

GPIO presence

Witherspoon and Zaius have examples for GPIO presence.

  • Witherspoon

    INVENTORY=/system/chassis/motherboard/powersupply0
    DEVPATH=/dev/input/by-path/platform-gpio-keys-event
    KEY=104
    NAME=powersupply0
    DRIVERS=/sys/bus/i2c/drivers/ibm-cffps,3-0069
    

    It checks GPIO key 104 for powersupply0 presence, creates the inventory object, and binds or unbinds the driver.

  • Zaius

    INVENTORY=/system/chassis/pcie_card_e2b
    DEVPATH=/dev/input/by-path/platform-gpio-keys-event
    KEY=39
    NAME=pcie_card_e2b
    

It checks GPIO key 39 for pcie_card_e2b presence and creates the inventory object.

GPIO monitor

Typical usage of the GPIO monitor is to monitor the checkstop event from host, or button presses.

  • checkstop monitor is a common service for OpenPOWER machines.

    DEVPATH=/dev/input/by-path/platform-gpio-keys-event
    KEY=74
    POLARITY=1
    TARGET=obmc-host-crash@0.target
    

    By default, it monitors GPIO key 74, and if it is triggered, it directs systemd to start obmc-host-crash@0.target. For systems using a different GPIO pin for checkstop, it simply overrides the default one by specifying its own configuration file in the meta-machine layer. For example, Zaius's checkstop config.

    Note: When the key is pressed, phospohr-gpio-monitor starts the target unit and exits.

  • id-button monitor is an example service on Romulus to monitor ID button press.

     DEVPATH=/dev/input/by-path/platform-gpio-keys-event
     KEY=135
     POLARITY=1
     TARGET=id-button-pressed.service
     EXTRA_ARGS=--continue
    

    It monitors GPIO key 135 for the button press and starts id-button-pressed.service, which handles the event by setting the identify LED group's Assert property.

    Note: It has an extra argument --continue that tells phosphor-gpio-monitor to not exit and continue running when the key is pressed.