Chapter 20: BMC Integration
IPMI, Redfish, and BMC-UEFI communication for server platforms.
Overview
When to Use BMC Integration
Use BMC Integration when you need to:
- Report boot progress (POST codes) to remote management
- Enable Serial-over-LAN for remote console access
- Log sensor data and hardware events during boot
- Support Redfish-based configuration from management software
| Scenario | Protocol | UEFI Component |
|---|---|---|
| POST code reporting | IPMI (KCS/BT) | Status Code driver |
| Remote console | IPMI SOL | Serial redirection |
| Sensor reading | IPMI | Sensor driver |
| REST-based management | Redfish | REST_EX protocol |
| Firmware update via BMC | Redfish | UpdateService |
| Boot option from BMC | Redfish | BootOptionRegistry |
BMC Protocol Comparison:
| Factor | IPMI | Redfish |
|---|---|---|
| Interface | Low-level binary | RESTful JSON |
| Complexity | Simpler | More complex |
| Extensibility | OEM commands | Standard schemas |
| Security | Limited | TLS, authentication |
| Modern systems | Legacy | Preferred |
Who Implements BMC Integration:
| Role | BMC Tasks |
|---|---|
| Platform developer | IPMI driver, interface selection |
| Server OEM | BMC firmware, UEFI integration |
| Data center operator | Redfish clients, automation |
| Firmware engineer | POST codes, SOL, health monitoring |
Platform Considerations:
- Server platforms: Full BMC integration expected
- Client platforms: Usually no BMC (use EC instead)
- Embedded: May have lightweight management controller
- IPMI interface (KCS, BT, SSIF) determined by hardware
BMC Architecture
The Baseboard Management Controller (BMC) provides out-of-band management:
flowchart TB
subgraph Server Platform
subgraph UEFI Firmware
IPMI_DRV[IPMI Driver]
RF_DRV[Redfish Driver]
POST[POST Code Reporting]
end
subgraph Communication Interfaces
KCS[KCS Interface]
BT[BT Interface]
SSIF[SSIF/SMBus]
USB[USB/Network]
end
subgraph BMC
BMC_FW[BMC Firmware]
IPMI_SVC[IPMI Stack]
RF_SVC[Redfish Service]
SENSORS[Sensor Monitoring]
SOL[Serial Over LAN]
KVM[Remote KVM]
end
end
IPMI_DRV --> KCS
IPMI_DRV --> BT
RF_DRV --> USB
POST --> KCS
KCS --> BMC_FW
BT --> BMC_FW
USB --> RF_SVC
BMC_FW --> IPMI_SVC
BMC_FW --> SENSORS
IPMI_SVC --> SOL
IPMI_SVC --> KVM
style BMC fill:#e67e22,color:#fff
style IPMI_DRV fill:#3498db,color:#fff
style RF_SVC fill:#2ecc71,color:#fff
BMC Communication Methods
| Interface | Protocol | Speed | Use Case |
|---|---|---|---|
| KCS | IPMI | Slow | Standard IPMI, POST codes |
| BT | IPMI | Medium | Higher throughput IPMI |
| SSIF | SMBus | Slow | I2C-based systems |
| USB | Redfish/MCTP | Fast | High-bandwidth, modern |
| Network | Redfish | Fast | Out-of-band management |
Initialization
IPMI Protocol
#include <IndustryStandard/Ipmi.h>
#include <Protocol/IpmiProtocol.h>
IPMI_PROTOCOL *gIpmi;
EFI_STATUS
InitializeIpmi (
VOID
)
{
return gBS->LocateProtocol(
&gIpmiProtocolGuid,
NULL,
(VOID **)&gIpmi
);
}
//
// Send IPMI command
//
EFI_STATUS
SendIpmiCommand (
IN UINT8 NetFunction,
IN UINT8 Command,
IN UINT8 *RequestData,
IN UINT32 RequestSize,
OUT UINT8 *ResponseData,
OUT UINT32 *ResponseSize
)
{
if (gIpmi == NULL) {
return EFI_NOT_READY;
}
return gIpmi->IpmiSubmitCommand(
gIpmi,
NetFunction,
0, // Lun
Command,
RequestData,
RequestSize,
ResponseData,
ResponseSize
);
}
KCS Interface Direct Access
#include <IndustryStandard/IpmiKcs.h>
//
// KCS I/O ports (common addresses)
//
#define KCS_DATA_PORT 0xCA2
#define KCS_STATUS_PORT 0xCA3
//
// KCS Status bits
//
#define KCS_STATUS_OBF 0x01 // Output Buffer Full
#define KCS_STATUS_IBF 0x02 // Input Buffer Full
#define KCS_STATUS_STATE 0xC0 // State machine bits
//
// KCS States
//
#define KCS_STATE_IDLE 0x00
#define KCS_STATE_READ 0x40
#define KCS_STATE_WRITE 0x80
#define KCS_STATE_ERROR 0xC0
EFI_STATUS
KcsWaitInputBufferEmpty (
IN UINT16 StatusPort
)
{
UINTN Timeout = 100000; // 100ms
UINT8 Status;
while (Timeout > 0) {
Status = IoRead8(StatusPort);
if ((Status & KCS_STATUS_IBF) == 0) {
return EFI_SUCCESS;
}
gBS->Stall(10);
Timeout -= 10;
}
return EFI_TIMEOUT;
}
EFI_STATUS
KcsSendCommand (
IN UINT16 DataPort,
IN UINT16 StatusPort,
IN UINT8 NetFn,
IN UINT8 Cmd,
IN UINT8 *Data,
IN UINTN DataSize
)
{
EFI_STATUS Status;
UINTN Index;
//
// Wait for IBF clear
//
Status = KcsWaitInputBufferEmpty(StatusPort);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Write WRITE_START command
//
IoWrite8(StatusPort, KCS_WRITE_START);
Status = KcsWaitInputBufferEmpty(StatusPort);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Write NetFn/LUN
//
IoWrite8(DataPort, (NetFn << 2));
//
// Write command
//
Status = KcsWaitInputBufferEmpty(StatusPort);
if (EFI_ERROR(Status)) {
return Status;
}
IoWrite8(DataPort, Cmd);
//
// Write data
//
for (Index = 0; Index < DataSize; Index++) {
Status = KcsWaitInputBufferEmpty(StatusPort);
if (EFI_ERROR(Status)) {
return Status;
}
if (Index == DataSize - 1) {
//
// Last byte - send WRITE_END
//
IoWrite8(StatusPort, KCS_WRITE_END);
}
IoWrite8(DataPort, Data[Index]);
}
return EFI_SUCCESS;
}
Configuration
IPMI Commands
//
// Common IPMI Network Functions
//
#define IPMI_NETFN_CHASSIS 0x00
#define IPMI_NETFN_BRIDGE 0x02
#define IPMI_NETFN_SENSOR 0x04
#define IPMI_NETFN_APP 0x06
#define IPMI_NETFN_FIRMWARE 0x08
#define IPMI_NETFN_STORAGE 0x0A
#define IPMI_NETFN_TRANSPORT 0x0C
//
// Get Device ID
//
EFI_STATUS
IpmiGetDeviceId (
OUT IPMI_GET_DEVICE_ID_RESPONSE *DeviceId
)
{
UINT32 ResponseSize = sizeof(IPMI_GET_DEVICE_ID_RESPONSE);
return SendIpmiCommand(
IPMI_NETFN_APP,
IPMI_APP_GET_DEVICE_ID,
NULL,
0,
(UINT8 *)DeviceId,
&ResponseSize
);
}
//
// Chassis Control (Power On/Off/Cycle/Reset)
//
EFI_STATUS
IpmiChassisControl (
IN UINT8 Control // 0=Off, 1=On, 2=Cycle, 3=Reset
)
{
UINT8 Response[2];
UINT32 ResponseSize = sizeof(Response);
return SendIpmiCommand(
IPMI_NETFN_CHASSIS,
IPMI_CHASSIS_CONTROL,
&Control,
1,
Response,
&ResponseSize
);
}
//
// Get Sensor Reading
//
EFI_STATUS
IpmiGetSensorReading (
IN UINT8 SensorNumber,
OUT UINT8 *Reading,
OUT UINT8 *Status
)
{
IPMI_GET_SENSOR_READING_RESPONSE Response;
UINT32 ResponseSize = sizeof(Response);
EFI_STATUS EfiStatus;
EfiStatus = SendIpmiCommand(
IPMI_NETFN_SENSOR,
IPMI_SENSOR_GET_SENSOR_READING,
&SensorNumber,
1,
(UINT8 *)&Response,
&ResponseSize
);
if (!EFI_ERROR(EfiStatus)) {
*Reading = Response.SensorReading;
*Status = Response.SensorStatus;
}
return EfiStatus;
}
POST Code Reporting
//
// Send POST code to BMC
//
EFI_STATUS
ReportPostCode (
IN UINT32 PostCode
)
{
UINT8 Data[2];
UINT8 Response[2];
UINT32 ResponseSize = sizeof(Response);
//
// OEM command format varies by vendor
// This is a simplified example
//
Data[0] = (UINT8)(PostCode & 0xFF);
Data[1] = (UINT8)((PostCode >> 8) & 0xFF);
return SendIpmiCommand(
IPMI_NETFN_OEM,
0x01, // OEM POST code command
Data,
2,
Response,
&ResponseSize
);
}
//
// Report progress during boot
//
VOID
ReportBootProgress (
IN EFI_STATUS_CODE_TYPE Type,
IN EFI_STATUS_CODE_VALUE Value
)
{
UINT32 PostCode;
//
// Convert status code to POST code
//
PostCode = (Value & 0xFFFF);
ReportPostCode(PostCode);
}
Redfish Integration
Redfish Host Interface
#include <Protocol/RedfishDiscover.h>
#include <Protocol/RestEx.h>
//
// SMBIOS Type 42 - Redfish Host Interface
//
// Defines how UEFI communicates with BMC Redfish service
//
EFI_REST_EX_PROTOCOL *gRestEx;
EFI_STATUS
InitializeRedfish (
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiRestExProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status) || HandleCount == 0) {
return EFI_NOT_FOUND;
}
Status = gBS->HandleProtocol(
HandleBuffer[0],
&gEfiRestExProtocolGuid,
(VOID **)&gRestEx
);
gBS->FreePool(HandleBuffer);
return Status;
}
Redfish REST Operations
//
// Perform Redfish GET request
//
EFI_STATUS
RedfishGet (
IN CHAR8 *Uri,
OUT CHAR8 **Response,
OUT UINTN *ResponseSize
)
{
EFI_STATUS Status;
EFI_HTTP_MESSAGE Request;
EFI_HTTP_MESSAGE ResponseMsg;
EFI_REST_EX_TOKEN Token;
if (gRestEx == NULL) {
return EFI_NOT_READY;
}
ZeroMem(&Request, sizeof(Request));
ZeroMem(&ResponseMsg, sizeof(ResponseMsg));
ZeroMem(&Token, sizeof(Token));
//
// Build request
//
Request.Data.Request = AllocateZeroPool(sizeof(EFI_HTTP_REQUEST_DATA));
Request.Data.Request->Method = HttpMethodGet;
Request.Data.Request->Url = AllocatePool(AsciiStrLen(Uri) + 1);
AsciiStrCpyS(Request.Data.Request->Url, AsciiStrLen(Uri) + 1, Uri);
//
// Send request
//
Token.Event = NULL;
Token.Status = EFI_NOT_READY;
Token.Message = &ResponseMsg;
Status = gRestEx->SendReceive(gRestEx, &Request, &Token);
if (!EFI_ERROR(Status) && !EFI_ERROR(Token.Status)) {
if (ResponseMsg.Body != NULL && ResponseMsg.BodyLength > 0) {
*Response = AllocatePool(ResponseMsg.BodyLength + 1);
CopyMem(*Response, ResponseMsg.Body, ResponseMsg.BodyLength);
(*Response)[ResponseMsg.BodyLength] = '\0';
*ResponseSize = ResponseMsg.BodyLength;
}
}
//
// Cleanup
//
if (Request.Data.Request->Url != NULL) {
FreePool(Request.Data.Request->Url);
}
FreePool(Request.Data.Request);
return Status;
}
//
// Example: Get system inventory
//
EFI_STATUS
GetSystemInventory (
VOID
)
{
EFI_STATUS Status;
CHAR8 *Response;
UINTN ResponseSize;
Status = RedfishGet("/redfish/v1/Systems/1", &Response, &ResponseSize);
if (!EFI_ERROR(Status)) {
//
// Parse JSON response
// Contains: Manufacturer, Model, SerialNumber, etc.
//
DEBUG((DEBUG_INFO, "System Info:\n%a\n", Response));
FreePool(Response);
}
return Status;
}
Porting Guide
Platform BMC Configuration
#
# Platform DSC file - BMC configuration
#
[PcdsFixedAtBuild]
# KCS interface address
gEfiIpmiPkgTokenSpaceGuid.PcdIpmiKcsIoBaseAddress|0xCA2
# BT interface address
gEfiIpmiPkgTokenSpaceGuid.PcdIpmiBtIoBaseAddress|0xE4
# Interface type: 1=KCS, 2=BT, 3=SSIF
gEfiIpmiPkgTokenSpaceGuid.PcdIpmiInterfaceType|1
[Components]
# IPMI infrastructure
IpmiFeaturePkg/GenericIpmi/Dxe/GenericIpmi.inf
IpmiFeaturePkg/BmcAcpi/BmcAcpi.inf
# Redfish support
RedfishPkg/RedfishDiscoverDxe/RedfishDiscoverDxe.inf
RedfishPkg/RedfishRestExDxe/RedfishRestExDxe.inf
RedfishPkg/RedfishCredentialDxe/RedfishCredentialDxe.inf
SMBIOS Type 42 (Redfish Host Interface)
//
// SMBIOS Type 42 tells OS/UEFI how to reach Redfish service
//
#pragma pack(1)
typedef struct {
SMBIOS_STRUCTURE Hdr;
UINT8 InterfaceType;
UINT8 InterfaceTypeSpecificDataLength;
// Interface-specific data follows
// For USB: Device descriptor info
// For Network: IP configuration
} SMBIOS_TABLE_TYPE42;
#pragma pack()
EFI_STATUS
CreateRedfishHostInterfaceSmbios (
VOID
)
{
EFI_STATUS Status;
EFI_SMBIOS_PROTOCOL *Smbios;
SMBIOS_TABLE_TYPE42 *Type42;
EFI_SMBIOS_HANDLE Handle;
Status = gBS->LocateProtocol(
&gEfiSmbiosProtocolGuid,
NULL,
(VOID **)&Smbios
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Build Type 42 structure
// Actual implementation depends on interface type
//
Handle = SMBIOS_HANDLE_PI_RESERVED;
Status = Smbios->Add(Smbios, NULL, &Handle, (EFI_SMBIOS_TABLE_HEADER *)Type42);
return Status;
}
BMC Use Cases
Serial Over LAN (SOL)
//
// Configure SOL for remote console
//
EFI_STATUS
ConfigureSerialOverLan (
IN BOOLEAN Enable
)
{
UINT8 Request[4];
UINT8 Response[2];
UINT32 ResponseSize = sizeof(Response);
//
// Set SOL configuration parameters
//
Request[0] = 0x01; // Channel (usually 1)
Request[1] = 0x00; // Parameter selector: Set In Progress
Request[2] = Enable ? 0x01 : 0x00;
return SendIpmiCommand(
IPMI_NETFN_TRANSPORT,
IPMI_TRANSPORT_SET_SOL_CONFIG_PARAM,
Request,
3,
Response,
&ResponseSize
);
}
Sensor Monitoring
//
// Read all SDR (Sensor Data Records)
//
EFI_STATUS
EnumerateSensors (
VOID
)
{
UINT16 ReservationId;
UINT16 RecordId = 0;
UINT8 Response[64];
UINT32 ResponseSize;
//
// Get SDR repository reservation
//
ResponseSize = sizeof(ReservationId);
SendIpmiCommand(
IPMI_NETFN_STORAGE,
IPMI_STORAGE_RESERVE_SDR_REPOSITORY,
NULL, 0,
(UINT8 *)&ReservationId,
&ResponseSize
);
Print(L"Sensors:\n");
//
// Iterate through SDR records
//
while (RecordId != 0xFFFF) {
UINT8 Request[6];
IPMI_SDR_RECORD_HEADER *SdrHeader;
Request[0] = ReservationId & 0xFF;
Request[1] = (ReservationId >> 8) & 0xFF;
Request[2] = RecordId & 0xFF;
Request[3] = (RecordId >> 8) & 0xFF;
Request[4] = 0; // Offset
Request[5] = 64; // Bytes to read
ResponseSize = sizeof(Response);
SendIpmiCommand(
IPMI_NETFN_STORAGE,
IPMI_STORAGE_GET_SDR,
Request, 6,
Response,
&ResponseSize
);
//
// Parse SDR record
//
RecordId = (Response[1] << 8) | Response[0];
SdrHeader = (IPMI_SDR_RECORD_HEADER *)&Response[2];
if (SdrHeader->RecordType == 0x01) { // Full Sensor Record
IPMI_SDR_RECORD_STRUCT_1 *FullSensor =
(IPMI_SDR_RECORD_STRUCT_1 *)SdrHeader;
Print(L" Sensor %d: %a\n",
FullSensor->SensorNumber,
FullSensor->IdString);
}
}
return EFI_SUCCESS;
}
Example: BMC Info Display
/** @file
BMC Information Display
**/
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/IpmiProtocol.h>
#include <IndustryStandard/Ipmi.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
IPMI_PROTOCOL *Ipmi;
IPMI_GET_DEVICE_ID_RESPONSE DeviceId;
UINT32 ResponseSize;
Print(L"=== BMC Information ===\n\n");
//
// Locate IPMI protocol
//
Status = gBS->LocateProtocol(&gIpmiProtocolGuid, NULL, (VOID **)&Ipmi);
if (EFI_ERROR(Status)) {
Print(L"IPMI not available: %r\n", Status);
Print(L"(BMC may not be present on this system)\n");
goto Exit;
}
//
// Get Device ID
//
ResponseSize = sizeof(DeviceId);
Status = Ipmi->IpmiSubmitCommand(
Ipmi,
IPMI_NETFN_APP,
0,
IPMI_APP_GET_DEVICE_ID,
NULL, 0,
(UINT8 *)&DeviceId,
&ResponseSize
);
if (!EFI_ERROR(Status)) {
Print(L"BMC Device ID:\n");
Print(L" Device ID: 0x%02x\n", DeviceId.DeviceId);
Print(L" Device Revision: 0x%02x\n", DeviceId.DeviceRevision);
Print(L" Firmware Rev: %d.%d\n",
DeviceId.FirmwareMajorRevision,
DeviceId.FirmwareMinorRevision);
Print(L" IPMI Version: %d.%d\n",
(DeviceId.IpmiVersion >> 4) & 0x0F,
DeviceId.IpmiVersion & 0x0F);
Print(L" Manufacturer ID: 0x%06x\n",
DeviceId.ManufacturerId[0] |
(DeviceId.ManufacturerId[1] << 8) |
(DeviceId.ManufacturerId[2] << 16));
}
//
// Get Self Test Results
//
{
UINT8 Response[4];
ResponseSize = sizeof(Response);
Status = Ipmi->IpmiSubmitCommand(
Ipmi,
IPMI_NETFN_APP,
0,
IPMI_APP_GET_SELFTEST_RESULTS,
NULL, 0,
Response,
&ResponseSize
);
if (!EFI_ERROR(Status)) {
Print(L"\nSelf Test Results:\n");
Print(L" Result: 0x%02x ", Response[0]);
switch (Response[0]) {
case 0x55:
Print(L"(No error)\n");
break;
case 0x56:
Print(L"(Self test not implemented)\n");
break;
case 0x57:
Print(L"(Corrupted device)\n");
break;
case 0x58:
Print(L"(Fatal hardware error)\n");
break;
default:
Print(L"(Device-specific error)\n");
}
}
}
Exit:
Print(L"\nPress any key to exit...\n");
{
EFI_INPUT_KEY Key;
UINTN Index;
gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index);
gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
}
return EFI_SUCCESS;
}
Specification Reference
- IPMI 2.0 Specification: Intelligent Platform Management Interface
- Redfish Specification: DMTF Redfish API
- SMBIOS Specification: Type 42 - Management Controller Host Interface
Summary
- BMC provides out-of-band server management
- IPMI is the traditional low-level interface (KCS, BT)
- Redfish is the modern REST API approach
- POST codes report boot progress to BMC
- SOL enables remote console access
- Sensors provide hardware health monitoring
Next Steps
- Chapter 21: RAS Features - Reliability features
- Chapter 22: eSPI Interface - Enhanced SPI
Server-Specific: BMC integration is primarily for server platforms. Desktop/laptop systems typically don’t have BMC.