hc
2024-03-25 edb30157bad0c0001c32b854271ace01d3b9a16a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/** @file
  IPMI Transport common layer driver
 
  @copyright
  Copyright 1999 - 2021 Intel Corporation. <BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
 
#include "IpmiBmc.h"
 
EFI_STATUS
UpdateErrorStatus (
  IN UINT8                      BmcError,
  IPMI_BMC_INSTANCE_DATA    *IpmiInstance
  )
/*++
 
Routine Description:
 
  Check if the completion code is a Soft Error and increment the count.  The count
  is not updated if the BMC is in Force Update Mode.
 
Arguments:
 
  BmcError      - Completion code to check
  IpmiInstance  - BMC instance data
 
Returns:
 
  EFI_SUCCESS   - Status
 
--*/
{
  UINT8   Errors[] = COMPLETION_CODES;
  UINT16  CodeCount;
  UINT8   i;
 
  CodeCount = sizeof (Errors) / sizeof (Errors[0]);
  for (i = 0; i < CodeCount; i++) {
    if (BmcError == Errors[i]) {
      //
      // Don't change Bmc Status flag if the BMC is in Force Update Mode.
      //
      if (IpmiInstance->BmcStatus != BMC_UPDATE_IN_PROGRESS) {
        IpmiInstance->BmcStatus = BMC_SOFTFAIL;
      }
 
      IpmiInstance->SoftErrorCount++;
      break;
    }
  }
 
  return EFI_SUCCESS;
}
 
EFI_STATUS
EFIAPI
IpmiSendCommandToBmc (
  IN      IPMI_TRANSPORT            *This,
  IN      UINT8                         NetFunction,
  IN      UINT8                         Lun,
  IN      UINT8                         Command,
  IN      UINT8                         *CommandData,
  IN      UINT8                         CommandDataSize,
  IN OUT  UINT8                         *ResponseData,
  IN OUT  UINT8                         *ResponseDataSize,
  IN      VOID                          *Context
  )
/*++
 
Routine Description:
 
  Send IPMI command to BMC
 
Arguments:
 
  This              - Pointer to IPMI protocol instance
  NetFunction       - Net Function of command to send
  Lun               - LUN of command to send
  Command           - IPMI command to send
  CommandData       - Pointer to command data buffer, if needed
  CommandDataSize   - Size of command data buffer
  ResponseData      - Pointer to response data buffer
  ResponseDataSize  - Pointer to response data buffer size
  Context           - Context
 
Returns:
 
  EFI_INVALID_PARAMETER - One of the input values is bad
  EFI_DEVICE_ERROR      - IPMI command failed
  EFI_BUFFER_TOO_SMALL  - Response buffer is too small
  EFI_UNSUPPORTED       - Command is not supported by BMC
  EFI_SUCCESS           - Command completed successfully
 
--*/
{
  IPMI_BMC_INSTANCE_DATA  *IpmiInstance;
  UINT8                   DataSize;
  EFI_STATUS              Status;
  IPMI_COMMAND            *IpmiCommand;
  IPMI_RESPONSE           *IpmiResponse;
  UINT8                   RetryCnt = IPMI_SEND_COMMAND_MAX_RETRY;
  UINT8                   Index;
 
  IpmiInstance = INSTANCE_FROM_SM_IPMI_BMC_THIS (This);
 
  while (RetryCnt--) {
    //
    // The TempData buffer is used for both sending command data and receiving
    // response data.  Since the command format is different from the response
    // format, the buffer is cast to both structure definitions.
    //
    IpmiCommand  = (IPMI_COMMAND*)  IpmiInstance->TempData;
    IpmiResponse = (IPMI_RESPONSE*) IpmiInstance->TempData;
 
    //
    // Send IPMI command to BMC
    //
    IpmiCommand->Lun = Lun;
    IpmiCommand->NetFunction = NetFunction;
    IpmiCommand->Command = Command;
 
    //
    // Ensure that the buffer is valid before attempting to copy the command data
    // buffer into the IpmiCommand structure.
    //
    if (CommandDataSize > 0) {
      if (CommandData == NULL) {
        return EFI_INVALID_PARAMETER;
      }
 
      CopyMem (
        IpmiCommand->CommandData,
        CommandData,
        CommandDataSize
        );
    }
 
    Status = SendDataToBmcPort (
               IpmiInstance->KcsTimeoutPeriod,
               IpmiInstance->IpmiIoBase,
               Context,
               (UINT8 *) IpmiCommand,
               (CommandDataSize + IPMI_COMMAND_HEADER_SIZE)
               );
 
    if (Status != EFI_SUCCESS) {
      IpmiInstance->BmcStatus = BMC_SOFTFAIL;
      IpmiInstance->SoftErrorCount++;
      return Status;
    }
 
    //
    // Get Response to IPMI Command from BMC.
    // Subtract 1 from DataSize so memory past the end of the buffer can't be written
    //
    DataSize = MAX_TEMP_DATA - 1;
    Status = ReceiveBmcDataFromPort (
               IpmiInstance->KcsTimeoutPeriod,
               IpmiInstance->IpmiIoBase,
               Context,
               (UINT8 *) IpmiResponse,
               &DataSize
               );
 
    if (Status != EFI_SUCCESS) {
      IpmiInstance->BmcStatus = BMC_SOFTFAIL;
      IpmiInstance->SoftErrorCount++;
      return Status;
    }
 
    //
    // If we got this far without any error codes, but the DataSize less than IPMI_RESPONSE_HEADER_SIZE, then the
    // command response failed, so do not continue.
    //
    if (DataSize < IPMI_RESPONSE_HEADER_SIZE) {
      return EFI_DEVICE_ERROR;
    }
 
    if ((IpmiResponse->CompletionCode != COMP_CODE_NORMAL) &&
        (IpmiInstance->BmcStatus == BMC_UPDATE_IN_PROGRESS)) {
      //
      // If the completion code is not normal and the BMC is in Force Update
      // mode, then update the error status and return EFI_UNSUPPORTED.
      //
      UpdateErrorStatus (
        IpmiResponse->CompletionCode,
        IpmiInstance
        );
      return EFI_UNSUPPORTED;
    } else if (IpmiResponse->CompletionCode != COMP_CODE_NORMAL) {
      //
      // Otherwise if the BMC is in normal mode, but the completion code
      // is not normal, then update the error status and return device error.
      //
      UpdateErrorStatus (
        IpmiResponse->CompletionCode,
        IpmiInstance
        );
      //
      // Intel Server System Integrated Baseboard Management Controller (BMC) Firmware v0.62
      // D4h C Insufficient privilege, in KCS channel this indicates KCS Policy Control Mode is Deny All.
      // In authenticated channels this indicates invalid authentication/privilege.
      //
      if (IpmiResponse->CompletionCode == COMP_INSUFFICIENT_PRIVILEGE) {
        return EFI_SECURITY_VIOLATION;
      } else {
        return EFI_DEVICE_ERROR;
      }
    }
 
    //
    // Verify the response data buffer passed in is big enough.
    //
    if ((DataSize - IPMI_RESPONSE_HEADER_SIZE) > *ResponseDataSize) {
      //
      //Verify the response data matched with the cmd sent.
      //
      if ((IpmiResponse->NetFunction != (NetFunction | 0x1)) || (IpmiResponse->Command != Command)) {
        if (0 == RetryCnt) {
          return EFI_DEVICE_ERROR;
        } else {
          continue;
        }
      }
      return EFI_BUFFER_TOO_SMALL;
    }
 
    break;
  }
  //
  // Copy data over to the response data buffer.
  //
  *ResponseDataSize = DataSize - IPMI_RESPONSE_HEADER_SIZE;
  CopyMem (
    ResponseData,
    IpmiResponse->ResponseData,
    *ResponseDataSize
    );
 
  //
  // Add completion code in response data to meet the requirement of IPMI spec 2.0
  //
  *ResponseDataSize += 1; // Add one byte for Completion Code
  for (Index = 1; Index < *ResponseDataSize; Index++) {
    ResponseData [*ResponseDataSize - Index] = ResponseData [*ResponseDataSize - (Index + 1)];
  }
  ResponseData [0] = IpmiResponse->CompletionCode;
 
  IpmiInstance->BmcStatus = BMC_OK;
  return EFI_SUCCESS;
}
 
 
EFI_STATUS
EFIAPI
IpmiBmcStatus (
  IN  IPMI_TRANSPORT              *This,
  OUT BMC_STATUS                  *BmcStatus,
  OUT SM_COM_ADDRESS              *ComAddress,
  IN  VOID                            *Context
  )
/*++
 
Routine Description:
 
  Updates the BMC status and returns the Com Address
 
Arguments:
 
  This        - Pointer to IPMI protocol instance
  BmcStatus   - BMC status
  ComAddress  - Com Address
  Context     - Context
 
Returns:
 
  EFI_SUCCESS - Success
 
--*/
{
  IPMI_BMC_INSTANCE_DATA  *IpmiInstance;
 
  IpmiInstance = INSTANCE_FROM_SM_IPMI_BMC_THIS (This);
 
  if ((IpmiInstance->BmcStatus == BMC_SOFTFAIL) && (IpmiInstance->SoftErrorCount >= MAX_SOFT_COUNT)) {
    IpmiInstance->BmcStatus = BMC_HARDFAIL;
  }
 
  *BmcStatus = IpmiInstance->BmcStatus;
  ComAddress->ChannelType = SmBmc;
  ComAddress->Address.BmcAddress.LunAddress = 0x0;
  ComAddress->Address.BmcAddress.SlaveAddress = IpmiInstance->SlaveAddress;
  ComAddress->Address.BmcAddress.ChannelAddress = 0x0;
 
  return EFI_SUCCESS;
}