# Customize IPMI commands in OpenBMC

## In this learning path

- [Introduction](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/)
- [What are OpenBMC and UEFI?](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/1_introduction_openbmc/)
- [Set up the development environment for OpenBMC and UEFI](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/2_openbmc_setup/)
- [Run OpenBMC and host UEFI simulation on RD-V3 FVP](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/3_openbmc_simulate/)
- [Monitor and control the host CPU using OpenBMC SOL and web UI](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/4_openbmc_communicate/)
- [Customize IPMI commands in OpenBMC](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/5_openbmc_ipmi/)
- [Next Steps](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/_next-steps/)

With the host console accessible through OpenBMC, you can extend platform management by implementing a custom IPMI command handler.

The Intelligent Platform Management Interface ([IPMI](https://en.wikipedia.org/wiki/Intelligent_Platform_Management_Interface)) is a standardized protocol for out-of-band server management, even when the operating system is not running. In OpenBMC, IPMI support is built-in and can be extended with custom handlers through the D-Bus and IPMI infrastructure.

In this section, you implement a custom IPMI command handler that returns a simple string response. You write the handler in C++, package it with a BitBake recipe, build it into the OpenBMC image, and test it with `ipmitool` in the FVP simulation environment.

## Step 1: Create a BitBake recipe

Create a new file named `phosphor-ipmi-example.bb` in the same folder.

```
touch ~/openbmc/meta-evb/meta-evb-arm/meta-evb-fvp-base/recipes-phosphor/ipmi/phosphor-ipmi-example.bb
```

Paste the following content into the file:

```
SUMMARY = "Custom IPMI commands"
LICENSE = "CLOSED"
PR = "r1"
SRC_URI = "file://fvp-ipmi.cpp"
S = "${UNPACKDIR}"

DEPENDS += "phosphor-ipmi-host sdbusplus systemd"
TARGET_CXXFLAGS += " -std=c++23"
TARGET_LDFLAGS += " -lsystemd -lsdbusplus"

do_compile() {
    ${CXX} ${TARGET_CXXFLAGS} -fPIC -shared -o libmyipmi.so ${UNPACKDIR}/fvp-ipmi.cpp -I${STAGING_INCDIR} -L${STAGING_LIBDIR} ${TARGET_LDFLAGS}
}

do_install() {
    install -d ${D}${libdir}/ipmid-providers
    install -m 0644 libmyipmi.so ${D}${libdir}/ipmid-providers/
}

FILES:${PN} += "${libdir}/ipmid-providers/libmyipmi.so"
```

## Step 2: Create a custom IPMI handler

Create a folder `phosphor-ipmi-example` at the same path, and add a new file called `fvp-ipmi.cpp`:

```
mkdir ~/openbmc/meta-evb/meta-evb-arm/meta-evb-fvp-base/recipes-phosphor/ipmi/phosphor-ipmi-example
touch ~/openbmc/meta-evb/meta-evb-arm/meta-evb-fvp-base/recipes-phosphor/ipmi/phosphor-ipmi-example/fvp-ipmi.cpp
```

Add the following contents to `fvp-ipmi.cpp`:

```
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <string>

// Example handler: return a string
ipmi::RspType<std::string> myIpmiCommand() {
    std::string reply = "Hello from OpenBMC IPMI!";
    return ipmi::responseSuccess(reply);
}

void register_my_ipmi() __attribute__((constructor));
void register_my_ipmi() {
    ipmi::registerHandler(
        ipmi::prioOemBase,
        0x30,                   // NetFn code
        0x20,                   // command code
        ipmi::Privilege::Admin,
        myIpmiCommand
    );
}
```

This function registers a custom IPMI handler with NetFn `0x30` and command `0x20`. When invoked, it returns the ASCII string `"Hello from OpenBMC IPMI!"`. At runtime, the string is encoded as hexadecimal bytes and returned in the IPMI response. You will observe this with `ipmitool raw` and by decoding the output.

## Step 3: Add to build configuration

To test the IPMI command, add `ipmitool` and `phosphor-ipmi-example` to your build configuration. Edit `fvp.conf` at:

```
~/openbmc/meta-evb/meta-evb-arm/meta-evb-fvp-base/conf/machine/fvp.conf
```

Append the following packages:

```
IMAGE_INSTALL:append = "\
    phosphor-ipmi-example     ipmitool "
```

Rebuild the OpenBMC image with your IPMI handler included:

```
cd ~/openbmc
source setup fvp
bitbake obmc-phosphor-image
```

After the build completes, the generated image contains both `ipmitool` and `phosphor-ipmi-example`.

For details about the final image configuration, inspect the generated FVP configuration file at:

```
~/openbmc/build/fvp/tmp/deploy/images/fvp/obmc-phosphor-image-fvp.fvpconf
```

## Step 4: Verify the IPMI command in simulation

After launching the FVP simulation and logging into the OpenBMC console, run the following command to invoke your custom IPMI handler:

```
ipmitool raw 0x30 0x20
```

This command invokes your custom IPMI handler registered under:

- NetFn: `0x30` (OEM function)
- Command: `0x20`

You should see a response similar to:

```
__output__root@fvp:~# ipmitool raw 0x30 0x20
__output__18 48 65 6c 6c 6f 20 66 72 6f 6d 20 4f 70 65 6e
__output__42 4d 43 20 49 50 4d 49 21
```

This response is a sequence of hexadecimal bytes returned by the BMC:

- The first byte indicates the length of the payload — in this case `0x18`, 24 bytes.
- The remaining 24 bytes are the data payload, encoded as ASCII.

![OpenBMC IPMI command output showing hexadecimal response alt-text](https://learn.arm.com/learning-paths/servers-and-cloud-computing/openbmc-rdv3/openbmc_ipmi.jpg)

To decode the message, copy the payload portion (excluding the first byte) and run:

```
echo "48 65 6c 6c 6f 20 66 72 6f 6d 20 4f 70 65 6e 42 4d 43 20 49 50 4d 49 21" | tr -d ' ' | xxd -r -p
```

The output is:

```
__output__Hello from OpenBMC IPMI!
```

This confirms that the custom string returned by your `myIpmiCommand()` function has been correctly encoded and transmitted using IPMI:

```
std::string reply = "Hello from OpenBMC IPMI!";
return ipmi::responseSuccess(reply);
```

The response from `ipmitool raw` confirms that your custom IPMI handler was:

- Successfully compiled and included in the OpenBMC image
- Properly registered to respond to NetFn `0x30` and command `0x20`
- Executed correctly in the simulated environment via IPMI raw access
- Returning the intended payload, encoded as ASCII and received in hexadecimal format

By decoding the hexadecimal payload into ASCII, you verified the full path from handler registration to command execution and payload delivery.

You have now implemented and tested a custom IPMI command in OpenBMC using pre-silicon simulation. This sets the foundation for adding OEM commands or platform-specific extensions to your BMC firmware. You can expand this pattern to support argument parsing, custom data formats, or system-level control to prototype features such as sensor telemetry, power domain control, or boot policy configuration.
