/** @file
BmcLanConfig.c - BMC LAN Configuration UEFI Driver
This DXE driver initializes BMC LAN channel settings at boot time.
It enumerates all available LAN channels on the BMC and configures
their IP address, subnet mask, default gateway IP, and gateway MAC address
by sending IPMI commands via the IPMI transport protocol.
The configuration parameters (IP addresses, subnet masks, gateway MAC)
are read from a "ServerSetup" HII configuration data structure embedded
in the firmware's NVRAM variables.
Copyright (c) Lenovo. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Protocol/IpmiTransportProtocol.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
//
// ---------------------------------------------------------------------------
// IPMI LAN Configuration Parameter Selectors
// (IPMI Specification v2.0, Section 18, LAN Configuration Parameters)
// ---------------------------------------------------------------------------
//
#define IPMI_LAN_PARAM_IP_ADDRESS 3 // Station IP address (4 bytes)
#define IPMI_LAN_PARAM_SUBNET_MASK 6 // Subnet mask (4 bytes)
#define IPMI_LAN_PARAM_GATEWAY_IP 12 // Default gateway IP (4 bytes)
#define IPMI_LAN_PARAM_GATEWAY_MAC 13 // Default gateway MAC (6 bytes)
#define IPMI_LAN_NETFN 0x0C // IPMI LAN network function
#define IPMI_APP_NETFN 0x06 // IPMI Application network function
#define IPMI_CC_SUCCESS 0x00
#define IPMI_LUN_APP 0 // Application LUN
#define IPMI_LUN_BMC 1 // BMC LUN
#define IPMI_LUN_OEM 2 // OEM LUN
#define IPMI_RETRY_MAX 10
#define IPMI_RETRY_DELAY_MS 1000
//
// ---------------------------------------------------------------------------
// Global protocol pointers (stored in .data section, runtime BSS)
// ---------------------------------------------------------------------------
//
// The IPMI transport protocol interface, located by LocateProtocol()
// using the GUID at unk_1F20 ({0x36232936, ...}).
//
EFI_IPMI_TRANSPORT_PROTOCOL *mIpmiTransport = NULL; // qword_23F0
// The HOB list pointer, located from the system table configuration table
VOID *mHobList = NULL; // qword_1FA8
// The debug output protocol, located from HOB
VOID *mDebugProtocol = NULL; // (internal)
// The PCD protocol interface
VOID *mPcdProtocol = NULL; // qword_1FB0
//
// ---------------------------------------------------------------------------
// Forward declarations
// ---------------------------------------------------------------------------
//
EFI_STATUS
InitializeBmcLanConfig (
VOID
);
// ===========================================================================
// Internal helper functions (MdePkg library wrappers)
// ===========================================================================
/**
Reads a UINT64 from an unaligned pointer, asserting the pointer is non-NULL.
**/
UINT64
EFIAPI
ReadUnaligned64 (
CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(CONST UINT64 *)Buffer;
}
/**
Compares two GUID structures by comparing their first and second QWORDs.
**/
BOOLEAN
EFIAPI
IsGuidMatch (
CONST GUID *Guid1,
CONST GUID *Guid2
)
{
return ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2) &&
ReadUnaligned64 ((UINT8 *)Guid1 + 8) == ReadUnaligned64 ((UINT8 *)Guid2 + 8);
}
/**
Copies a memory buffer, handling overlapping source and destination.
**/
VOID *
EFIAPI
InternalCopyMem (
VOID *Destination,
CONST VOID *Source,
UINTN Length
)
{
UINT8 *Dst8;
UINT8 *Src8;
UINTN AlignedCount;
UINTN Remaining;
if (Source < Destination && (UINT8 *)Source + Length > (UINT8 *)Destination) {
//
// Overlap: Source starts before Destination and extends into it.
// Copy from end to beginning.
//
Src8 = (UINT8 *)Source + Length - 1;
Dst8 = (UINT8 *)Destination + Length - 1;
while (Length--) {
*Dst8-- = *Src8--;
}
} else {
//
// No overlap or Destination starts before Source.
// Copy from beginning, using qmemcpy for aligned 8-byte chunks.
//
AlignedCount = Length >> 3;
Remaining = Length & 7;
qmemcpy (Destination, Source, AlignedCount * 8);
Src8 = (UINT8 *)Source + AlignedCount * 8;
Dst8 = (UINT8 *)Destination + AlignedCount * 8;
while (Remaining--) {
*Dst8++ = *Src8++;
}
}
return Destination;
}
/**
Copies memory with bounds-check assertions.
@param[out] DestinationBuffer Pointer to the destination.
@param[in] SourceBuffer Pointer to the source.
@param[in] Length Number of bytes to copy.
@return DestinationBuffer.
**/
VOID *
EFIAPI
CopyMemWithAssert (
VOID *DestinationBuffer,
CONST VOID *SourceBuffer,
UINTN Length
)
{
UINTN Index;
Index = Length - 1;
ASSERT ((Length - 1) <= (MAX_ADDRESS - (UINTN)DestinationBuffer));
ASSERT ((Length - 1) <= (MAX_ADDRESS - (UINTN)SourceBuffer));
if (DestinationBuffer == SourceBuffer) {
return DestinationBuffer;
}
return InternalCopyMem (DestinationBuffer, SourceBuffer, Length);
}
// ===========================================================================
// HOB list and protocol locator functions
// ===========================================================================
/**
Locates the HOB (Hand-Off Block) list pointer from the UEFI system table
configuration table, using gEfiHobListGuid.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
GetHobList (
VOID
)
{
UINTN Index;
if (mHobList != NULL) {
return mHobList;
}
mHobList = NULL;
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
if (IsGuidMatch (&gEfiHobListGuid, gST->ConfigurationTable[Index].VendorGuid)) {
mHobList = gST->ConfigurationTable[Index].VendorTable;
break;
}
}
if (mHobList == NULL) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
}
ASSERT (mHobList != NULL);
return mHobList;
}
/**
Locates and returns the debug output protocol interface.
@return Pointer to the debug output protocol, or NULL if not found.
**/
VOID *
GetDebugOutputProtocol (
VOID
)
{
EFI_STATUS Status;
if (mDebugProtocol != NULL) {
return mDebugProtocol;
}
//
// Allocate pool for the HOB protocol lookup, using BootServices.
// If the HOB list size is 16 bytes or less (platform-specific check),
// use LocateProtocol for the debug output protocol instead.
//
if (gBS->CalculateEfiHobListSize (mHobList) <= sizeof (UINT64) + sizeof (UINT64)) {
Status = gBS->LocateProtocol (
&gEfiDebugSupportProtocolGuid,
NULL,
&mDebugProtocol
);
if (EFI_ERROR (Status)) {
mDebugProtocol = NULL;
}
}
return mDebugProtocol;
}
/**
Locates and returns the PCD protocol interface.
@return Pointer to the PCD protocol, or NULL if not found.
**/
VOID *
GetPcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (mPcdProtocol != NULL) {
return mPcdProtocol;
}
Status = gBS->LocateProtocol (
&gEfiPcdProtocolGuid,
NULL,
&mPcdProtocol
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
ASSERT (mPcdProtocol != NULL);
return mPcdProtocol;
}
// ===========================================================================
// IPMI Transport Protocol Command Functions
// ===========================================================================
/**
Sends an IPMI command to the BMC, retrying on transient failures.
The command uses the LAN network function (0x0C) and the given LUN.
If the BMC response indicates a "busy" condition (lower 2 bits != 0),
the function waits 1 second and retries, up to 10 retries.
@param[in] ChannelNumber The IPMB channel number.
@param[in] CommandData Pointer to the IPMI command data (after NetFn/LUN byte).
@param[in] DataSize Size of CommandData in bytes.
@param[in] Lun The logical unit number (typically 2 for OEM).
@retval EFI_SUCCESS The command completed and the BMC responded.
@retval EFI_TIMEOUT The BMC was busy after 10 retries.
@retval Others Error propagated from the IPMI transport protocol.
**/
EFI_STATUS
IpmiSendCommandWithRetry (
IN UINT8 ChannelNumber,
IN UINT8 *CommandData,
IN UINT8 DataSize,
IN UINT8 Lun
)
{
UINT8 Retries;
EFI_STATUS Status;
UINT8 ResponseBuffer[4];
UINTN ResponseSize;
Retries = IPMI_RETRY_MAX;
ResponseSize = sizeof (ResponseBuffer);
do {
Status = mIpmiTransport->SendCommand (
mIpmiTransport,
IPMI_LAN_NETFN,
0,
Lun,
CommandData,
DataSize,
ResponseBuffer,
&ResponseSize
);
if (EFI_ERROR (Status)) {
break;
}
if ((ResponseBuffer[1] & 0x03) != 0) {
//
// bits [1:0] of second byte = busy flag from BMC
//
gBS->Stall (IPMI_RETRY_DELAY_MS * 1000);
Retries--;
} else {
break;
}
} while (Retries > 0);
if (Retries == 0) {
return EFI_TIMEOUT;
}
return Status;
}
/**
Sets a LAN configuration parameter on the BMC.
The parameter is identified by a selector byte (e.g., 3 = Station IP,
6 = Subnet Mask, 12 = Gateway IP, 13 = Gateway MAC).
@param[in] ChannelNumber The LAN channel to configure.
@param[in] ParameterData Pointer to the parameter value bytes.
@param[in] ParameterSize Size of the parameter value in bytes.
@param[in] ParameterSel The IPMI LAN parameter selector.
@retval EFI_STATUS from the IPMI transport.
**/
EFI_STATUS
IpmiSetLanConfigParamBytes (
IN UINT8 ChannelNumber,
IN UINT8 *ParameterData,
IN UINT8 ParameterSize,
IN UINT8 ParameterSel
)
{
UINT8 RequestBuffer[20];
UINT8 ResponseData[1];
UINTN ResponseSize;
RequestBuffer[0] = ChannelNumber;
RequestBuffer[1] = ParameterSel;
if (ParameterSize > 0) {
CopyMemWithAssert (&RequestBuffer[2], ParameterData, ParameterSize);
}
//
// Set LAN Configuration Parameters command:
// NetFn: 0x0C (LAN)
// Cmd: 0x01 (Set LAN Configuration Parameters)
// LUN: 1 (BMC)
//
ResponseSize = 1;
return mIpmiTransport->SendCommand (
mIpmiTransport,
IPMI_LAN_NETFN,
0x01,
IPMI_LUN_BMC,
RequestBuffer,
2 + ParameterSize,
ResponseData,
&ResponseSize
);
}
/**
Retrieves a LAN configuration parameter from the BMC.
@param[in] ChannelNumber The LAN channel to query.
@param[out] ParameterValue Pointer to receive the parameter value.
@param[in] ParameterSelector The LAN parameter selector.
@param[in] SetSelector The set number (0 = current, 20 = set #0 revision).
@retval EFI_STATUS from the IPMI transport.
**/
EFI_STATUS
IpmiGetLanConfigParamBytes (
IN UINT8 ChannelNumber,
OUT UINT16 *ParameterValue,
IN UINT8 ParameterSelector,
IN UINT8 SetSelector
)
{
UINT8 RequestBuffer[3];
UINT8 ResponseBuffer[4];
UINTN ResponseSize;
EFI_STATUS Status;
RequestBuffer[0] = ChannelNumber;
RequestBuffer[1] = ParameterSelector;
RequestBuffer[2] = SetSelector;
ResponseSize = sizeof (ResponseBuffer);
Status = mIpmiTransport->SendCommand (
mIpmiTransport,
IPMI_LAN_NETFN,
0x02, // Get LAN Configuration Parameters
IPMI_LUN_OEM,
RequestBuffer,
sizeof (RequestBuffer),
ResponseBuffer,
&ResponseSize
);
if (!EFI_ERROR (Status)) {
//
// Response format: bit 7 = parameter revision flag
// If bit 7 set: value is byte[1] | ((byte[2] & 0x3F) << 8)
// If bit 7 clear: parameter is not supported / invalid
//
if (ResponseBuffer[0] & 0x80) {
*ParameterValue = ResponseBuffer[1] | ((ResponseBuffer[2] & 0x3F) << 8);
} else {
*ParameterValue = 0;
}
}
return Status;
}
/**
Queries the BMC for the current channel number.
@param[in] ChannelNumber IPMB channel to query.
@param[in] Lun Logical unit number.
@param[in] RetryCount (Unused in current implementation).
@param[in] RetryInterval (Unused in current implementation).
@return The channel number from the BMC, or 0 on error.
**/
UINT8
IpmiGetChannelNumber (
IN UINT8 ChannelNumber,
IN UINT16 Lun,
IN UINT8 RetryCount,
IN UINT8 RetryInterval
)
{
UINT8 RequestBuffer[2];
UINT8 ResponseBuffer[3];
UINTN ResponseSize;
RequestBuffer[0] = ChannelNumber;
if (Lun != 0) {
RequestBuffer[1] = (UINT8)Lun;
RequestBuffer[1] = (UINT8)(Lun >> 8) | 0x80;
} else {
RequestBuffer[1] = 0;
}
IpmiSendCommandWithRetry (ChannelNumber, RequestBuffer, sizeof (RequestBuffer), 0);
ResponseSize = sizeof (ResponseBuffer);
mIpmiTransport->SendCommand (
mIpmiTransport,
IPMI_LAN_NETFN,
0,
IPMI_LUN_BMC,
RequestBuffer,
sizeof (RequestBuffer),
ResponseBuffer,
&ResponseSize
);
return 0;
}
// ===========================================================================
// LAN Channel Discovery
// ===========================================================================
/**
Enumerates all available LAN channels on the BMC.
The function scans IPMB channels 1 through 11 and identifies those
with medium type 4 (e.g., dedicated LAN-over-USB / NC-SI / sideband).
It calls GetLanChannelNumber (application command) on each candidate
channel and checks the response.
@param[in] IpmiProtocol Pointer to the IPMI transport protocol.
@param[out] ChannelBuffer Buffer to receive discovered channel numbers.
@param[in,out] ChannelCount On input: max channels the buffer can hold.
On output: number of channels actually found.
@retval EFI_SUCCESS One or more channels were found.
@retval EFI_INVALID_PARAMETER IpmiProtocol or ChannelBuffer was NULL.
@retval EFI_NOT_FOUND No channels with medium type 4 were found.
**/
EFI_STATUS
GetLanChannelNumbers (
IN VOID *IpmiProtocol,
OUT UINT8 *ChannelBuffer,
IN OUT UINT8 *ChannelCount
)
{
UINT8 CandidateChannel;
UINT8 FoundCount;
UINT8 MaxChannels;
UINT8 MediumType;
UINT8 CompletionCode;
UINT8 ResponseByte;
UINTN ResponseSize;
EFI_STATUS Status;
FoundCount = 0;
CandidateChannel = 1;
if (IpmiProtocol == NULL || ChannelBuffer == NULL || *ChannelCount == 0) {
return EFI_INVALID_PARAMETER;
}
//
// Query the IPMI transport's capabilities for max channels and medium type.
// (The exact IPMI commands vary by platform.)
//
MaxChannels = ((EFI_IPMI_TRANSPORT_PROTOCOL *)IpmiProtocol)->GetMaxChannels (185);
MediumType = ((EFI_IPMI_TRANSPORT_PROTOCOL *)IpmiProtocol)->GetChannelMediumType (185);
if (MaxChannels == 0 || MediumType == 0) {
return EFI_NOT_FOUND;
}
//
// Scan candidate channels 1 through 11
//
do {
//
// Application command: Get Channel Number
// NetFn: 0x06 (Application), Cmd: 0x42 (Get Channel Number)
//
CompletionCode = IPMI_CC_SUCCESS;
ResponseSize = 1;
Status = ((EFI_IPMI_TRANSPORT_PROTOCOL *)IpmiProtocol)->SendCommand (
IpmiProtocol,
IPMI_APP_NETFN,
0x42,
66, // OEM-specific LUN/target
&CandidateChannel,
1,
&CompletionCode, // completion code
&ResponseByte
);
DEBUG ((
EFI_D_INFO,
" %a:%r ChannelNo: %d, CompletionCode: %x\n",
"GetLanChannelNumber",
Status,
CandidateChannel,
CompletionCode
));
if (!EFI_ERROR (Status) && CompletionCode == 0 && (ResponseByte & 0x7F) == 4) {
//
// Medium type bits [6:0] = 4 means this channel supports the
// dedicated LAN (NC-SI or USB-over-LAN) medium.
//
DEBUG ((EFI_D_INFO, "%a: Channel Medium Type: %x\n", "GetLanChannelNumber", ResponseByte & 0x7F));
if (FoundCount < MediumType) {
ChannelBuffer[FoundCount++] = CandidateChannel;
}
}
CandidateChannel++;
} while (CandidateChannel <= 11);
if (CandidateChannel > 11 && FoundCount == 0) {
return EFI_NOT_FOUND;
}
if (*ChannelCount > MaxChannels) {
*ChannelCount = (UINT8)MaxChannels;
}
if (*ChannelCount > 0) {
//
// The channel buffer already has the correct values from the scan,
// so this is effectively a length adjustment.
//
CopyMemWithAssert (ChannelBuffer, ChannelBuffer, *ChannelCount);
}
return EFI_SUCCESS;
}
// ===========================================================================
// ASCII String Parsers for IPMI Configuration
// ===========================================================================
/**
Parses an ASCII dotted-decimal IPv4 address string into 4 octets.
The string is in UTF-16 format (as stored in UEFI HII configuration).
Example input: L"192.168.1.100"
@param[out] Octets Buffer to receive the 4 IP octets.
@param[in] AddrString Pointer to the null-terminated UTF-16 string.
@retval EFI_SUCCESS Parsing succeeded.
@retval EFI_INVALID_PARAMETER The string was malformed or value out of range.
**/
EFI_STATUS
AsciiIpv4AddrToBytes (
OUT UINT8 *Octets,
IN UINT16 *AddrString
)
{
UINT8 SegmentIndex;
UINT8 Accumulator;
UINT8 DigitCount;
BOOLEAN HasDigits;
UINT16 CheckSum;
UINT8 Multiplier;
CHAR16 Ch;
INTN StringLen;
INTN Index;
HasDigits = FALSE;
SegmentIndex = 3; // start from last octet (parse backwards)
Accumulator = 0;
CheckSum = 0;
Multiplier = 1;
DigitCount = 0;
//
// Determine string length (cap at 15 chars to avoid pathological inputs)
//
StringLen = 0;
while (AddrString[StringLen] != 0) {
StringLen++;
}
if (StringLen > 15) {
StringLen = 15;
}
Index = StringLen - 1;
while (Index >= 0) {
Ch = AddrString[Index];
if (Ch >= L'0' && Ch <= L'9') {
HasDigits = TRUE;
//
// Validate value ranges: max 3 digits, max value 255
//
if (DigitCount > 2) {
goto ParseError;
}
if (DigitCount == 2) {
if ((Ch - L'0') > 2) {
goto ParseError;
}
if (Ch == L'2') {
if ((AddrString[Index + 1] - L'0') > 5) {
goto ParseError;
}
if (AddrString[Index + 1] == L'5' && AddrString[Index + 2] > L'5') {
goto ParseError;
}
}
}
Accumulator += Multiplier * (Ch - L'0');
Multiplier *= 10;
DigitCount++;
} else if (Ch == L'.') {
if (!HasDigits || AddrString[Index + 1] == L'.') {
goto ParseError;
}
Octets[SegmentIndex--] = Accumulator;
CheckSum += Accumulator;
if (SegmentIndex > 3) {
goto ParseError;
}
Accumulator = 0;
Multiplier = 1;
DigitCount = 0;
} else if (Ch == 0 && !HasDigits) {
//
// Skip leading null characters (padding)
//
goto NextChar;
} else {
goto ParseError;
}
NextChar:
Index--;
}
if (SegmentIndex == 0) {
Octets[0] = Accumulator;
CheckSum += Accumulator;
if (HasDigits && CheckSum != 0) {
return EFI_SUCCESS;
}
}
ParseError:
//
// Zero out output on parse failure
//
*(UINT32 *)Octets = 0;
return EFI_INVALID_PARAMETER;
}
/**
Parses an ASCII MAC address string into 6 bytes.
The string is in UTF-16 format. Accepted format uses dashes as
separators, e.g., L"00-11-22-33-44-55".
@param[out] MacBytes Buffer to receive the 6 MAC bytes.
@param[in] MacString Pointer to the null-terminated UTF-16 string.
@retval EFI_SUCCESS Parsing succeeded.
@retval EFI_INVALID_PARAMETER The string was malformed.
**/
EFI_STATUS
AsciiMacAddrToBytes (
OUT UINT8 *MacBytes,
IN UINT16 *MacString
)
{
UINT8 SegmentIndex;
UINT8 Accumulator;
UINT8 DigitCount;
BOOLEAN HasDigits;
UINT16 CheckSum;
UINT8 Multiplier;
CHAR16 Ch;
INTN Index;
HasDigits = FALSE;
SegmentIndex = 5; // start from last byte (parse backwards)
Accumulator = 0;
CheckSum = 0;
Multiplier = 1;
DigitCount = 0;
Index = 16;
while (TRUE) {
Ch = MacString[Index];
if (Ch >= L'0' && Ch <= L'9') {
Ch = Ch - L'0';
} else if (Ch >= L'A' && Ch <= L'F') {
Ch = Ch - L'A' + 10;
} else if (Ch >= L'a' && Ch <= L'f') {
Ch = Ch - L'a' + 10;
} else if (Ch == L'-') {
//
// Dash separator: commit the current byte
//
if (!HasDigits || MacString[Index + 1] == L'-') {
goto ParseError;
}
MacBytes[SegmentIndex] = Accumulator;
CheckSum += Accumulator;
if (SegmentIndex == 0) {
goto ParseError;
}
SegmentIndex--;
Accumulator = 0;
Multiplier = 1;
DigitCount = 0;
Index--;
continue;
} else if (Ch == 0) {
//
// End of string: commit final byte
//
if (HasDigits) {
MacBytes[SegmentIndex] = Accumulator;
CheckSum += Accumulator;
}
break;
} else {
goto ParseError;
}
//
// Process hex nibble
//
HasDigits = TRUE;
if (DigitCount > 1) {
goto ParseError;
}
Accumulator += Multiplier * Ch;
Multiplier *= 16;
DigitCount++;
Index--;
}
if (SegmentIndex == 0 && HasDigits && CheckSum != 0) {
return EFI_SUCCESS;
}
ParseError:
*(UINT32 *)MacBytes = 0;
*(UINT16 *)(MacBytes + 4) = 0;
return EFI_INVALID_PARAMETER;
}
// ===========================================================================
// IPMI LAN Configuration (Parameter Block Programming)
// ===========================================================================
/**
Programs a complete set of LAN parameters for a given channel.
The configuration data is organized as a 19-byte block per channel:
byte[0] = Channel number
byte[1..4] = Station IP address (4 bytes)
byte[5..8] = Subnet mask (4 bytes)
byte[9..12] = Gateway IP address (4 bytes)
byte[13..18] = Gateway MAC address (6 bytes)
@param[in] ChannelNumber The LAN channel to configure.
@param[in] ConfigBlock Pointer to the 19-byte configuration block.
@retval EFI_SUCCESS All parameters were programmed.
@retval Others Error from IPMI transport.
**/
EFI_STATUS
IpmiSetLanConfigParams (
IN UINT8 ChannelNumber,
IN UINT8 *ConfigBlock
)
{
//
// Parameter 3: Station IP Address (4 bytes at offset 1)
//
IpmiSendCommandWithRetry (
ChannelNumber,
ConfigBlock + 1,
4,
IPMI_LUN_BMC
);
IpmiSetLanConfigParamBytes (
ChannelNumber,
ConfigBlock + 1,
4,
IPMI_LAN_PARAM_IP_ADDRESS
);
//
// Parameter 6: Subnet Mask (4 bytes at offset 5)
//
IpmiSendCommandWithRetry (
ChannelNumber,
ConfigBlock + 5,
4,
IPMI_LUN_BMC
);
IpmiSetLanConfigParamBytes (
ChannelNumber,
ConfigBlock + 5,
4,
IPMI_LAN_PARAM_SUBNET_MASK
);
//
// Parameter 12: Default Gateway IP (4 bytes at offset 9)
//
IpmiSendCommandWithRetry (
ChannelNumber,
ConfigBlock + 9,
4,
IPMI_LUN_BMC
);
IpmiSetLanConfigParamBytes (
ChannelNumber,
ConfigBlock + 9,
4,
IPMI_LAN_PARAM_GATEWAY_IP
);
//
// Parameter 13: Default Gateway MAC (6 bytes at offset 13)
//
IpmiSendCommandWithRetry (
ChannelNumber,
ConfigBlock + 13,
6,
IPMI_LUN_BMC
);
IpmiSetLanConfigParamBytes (
ChannelNumber,
ConfigBlock + 13,
6,
IPMI_LAN_PARAM_GATEWAY_MAC
);
return EFI_SUCCESS;
}
// ===========================================================================
// Main Entry Point: InitializeBmcLanConfig
// ===========================================================================
/**
The main initialization function for the BMC LAN configuration.
This function:
1. Locates the IPMI Transport Protocol.
2. Calls GetLanChannelNumbers() to discover all available LAN channels.
3. For each discovered channel, calls IpmiSetLanConfigParams().
4. Prints the configured parameters as debug output.
@retval EFI_SUCCESS Configuration completed.
@retval EFI_NOT_FOUND IPMI transport protocol not found.
@retval EFI_INVALID_PARAMETER Channel discovery failed.
**/
EFI_STATUS
EFIAPI
InitializeBmcLanConfig (
VOID
)
{
EFI_STATUS Status;
UINT8 ChannelCount;
UINT8 ChannelNumberBuffer[16];
UINT8 Index;
//
// Locate the IPMI Transport Protocol
//
Status = gBS->LocateProtocol (
&gEfiIpmiTransportProtocolGuid,
NULL,
(VOID **)&mIpmiTransport
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Enumerate active LAN channels
//
ChannelCount = sizeof (ChannelNumberBuffer);
Status = GetLanChannelNumbers (
mIpmiTransport,
ChannelNumberBuffer,
&ChannelCount
);
DEBUG ((
EFI_D_INFO,
"%a ChannelNumberBuffer[0]: %x ChannelNumberBuffer[1]: %x ChannelCount = %x\n",
"InitializeBmcLanConfig",
ChannelNumberBuffer[0],
ChannelNumberBuffer[1],
ChannelCount
));
if (EFI_ERROR (Status)) {
return Status;
}
//
// Configure each discovered LAN channel
//
for (Index = 0; Index < ChannelCount; Index++) {
//
// The configuration data is referenced from the "ServerSetup"
// HII variable (string at 0x1940). Each channel has a 19-byte
// configuration block programmed via IpmiSetLanConfigParams().
//
IpmiSetLanConfigParams (
ChannelNumberBuffer[Index],
&mBmcLanConfigData[Index * 19]
);
//
// Debug dump of configured parameters
//
DEBUG ((EFI_D_INFO, "#########################\n"));
DEBUG ((EFI_D_INFO,
"### StationIp: %d %d %d %d\n",
mBmcLanConfigData[Index * 19 + 1],
mBmcLanConfigData[Index * 19 + 2],
mBmcLanConfigData[Index * 19 + 3],
mBmcLanConfigData[Index * 19 + 4]
));
DEBUG ((EFI_D_INFO,
"### SubnetMask: %d %d %d %d\n",
mBmcLanConfigData[Index * 19 + 5],
mBmcLanConfigData[Index * 19 + 6],
mBmcLanConfigData[Index * 19 + 7],
mBmcLanConfigData[Index * 19 + 8]
));
DEBUG ((EFI_D_INFO,
"### RouterIp: %d %d %d %d\n",
mBmcLanConfigData[Index * 19 + 9],
mBmcLanConfigData[Index * 19 + 10],
mBmcLanConfigData[Index * 19 + 11],
mBmcLanConfigData[Index * 19 + 12]
));
DEBUG ((EFI_D_INFO,
"### Router MAC: %x %x %x %x %x %x\n",
mBmcLanConfigData[Index * 19 + 13],
mBmcLanConfigData[Index * 19 + 14],
mBmcLanConfigData[Index * 19 + 15],
mBmcLanConfigData[Index * 19 + 16],
mBmcLanConfigData[Index * 19 + 17],
mBmcLanConfigData[Index * 19 + 18]
));
}
return EFI_SUCCESS;
}
//
// ---------------------------------------------------------------------------
// Standard UEFI Module Entry Point
// ---------------------------------------------------------------------------
//
// _ModuleEntryPoint (0x350)
// - Saves ImageHandle, SystemTable, BootServices, RuntimeServices
// as global pointers.
// - Calls GetHobList() to initialize the HOB list pointer for debug output.
// - Calls InitializeBmcLanConfig() to perform the actual configuration.
//
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Save global service table pointers with assertions
//
gImageHandle = ImageHandle;
ASSERT (gImageHandle != NULL);
gST = SystemTable;
ASSERT (gST != NULL);
gBS = SystemTable->BootServices;
ASSERT (gBS != NULL);
gRT = SystemTable->RuntimeServices;
ASSERT (gRT != NULL);
//
// Initialize HOB list for debug protocol access
//
GetHobList ();
//
// Perform BMC LAN configuration
//
return InitializeBmcLanConfig ();
}