summaryrefslogtreecommitdiff
path: root/ManageabilityPkg/Universal/PldmProtocol/Common/PldmProtocolCommon.c
blob: 2c9cc9b3c3a395540fbd7d021398d63f5953aa84 (plain)
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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
/** @file

  IPMI Manageability Protocol common file.

  Copyright (C) 2023-2026 Advanced Micro Devices, Inc. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/ManageabilityTransportLib.h>
#include <Library/ManageabilityTransportHelperLib.h>
#include <Library/ManageabilityTransportMctpLib.h>
#include <IndustryStandard/Mctp.h>
#include <IndustryStandard/Pldm.h>
#include <IndustryStandard/PldmSmbiosTransfer.h>
#include "PldmProtocolCommon.h"

extern CHAR16  *mTransportName;
extern UINT8   mPldmRequestInstanceId;

/**
  This functions setup the final header/body/trailer packets for
  the acquired transport interface.

  @param[in]         TransportToken     The transport interface.
  @param[in]         PldmType           PLDM message type.
  @param[in]         PldmCommand        PLDM command of this PLDM type.
  @param[in]         SourceId           PLDM source terminus ID.
  @param[in]         DestinationId      PLDM destination terminus ID.
  @param[out]        PacketHeader       The pointer to receive header of request.
  @param[out]        PacketHeaderSize   Packet header size in bytes.
  @param[in, out]    PacketBody         The request body.
                                        When IN, it is the caller's request body.
                                        When OUT and NULL, the request body is not
                                        changed.
                                        Whee out and non-NULL, the request body is
                                        changed to comfort the transport interface.
  @param[in, out]    PacketBodySize     The request body size.
                                        When IN and non-zero, it is the new data
                                        length of request body.
                                        When IN and zero, the request body is unchanged.
  @param[out]        PacketTrailer      The pointer to receive trailer of request.
  @param[out]        PacketTrailerSize  Packet trailer size in bytes.

  @retval EFI_SUCCESS            Request packet is returned.
  @retval EFI_UNSUPPORTED        Request packet is not returned because
                                 the unsupported transport interface.
**/
EFI_STATUS
SetupPldmRequestTransportPacket (
  IN   MANAGEABILITY_TRANSPORT_TOKEN    *TransportToken,
  IN   UINT8                            PldmType,
  IN   UINT8                            PldmCommand,
  IN   UINT8                            SourceId,
  IN   UINT8                            DestinationId,
  OUT  MANAGEABILITY_TRANSPORT_HEADER   *PacketHeader,
  OUT  UINT16                           *PacketHeaderSize,
  IN OUT UINT8                          **PacketBody,
  IN OUT UINT32                         *PacketBodySize,
  OUT  MANAGEABILITY_TRANSPORT_TRAILER  *PacketTrailer,
  OUT  UINT16                           *PacketTrailerSize
  )
{
  MANAGEABILITY_MCTP_TRANSPORT_HEADER  *MctpHeader;
  PLDM_REQUEST_HEADER                  *PldmRequestHeader;

  if ((PacketHeader == NULL) || (PacketHeaderSize == NULL) ||
      (PacketBody   == NULL) || (PacketBodySize == NULL) ||
      (PacketTrailer == NULL) || (PacketTrailerSize == NULL)
      )
  {
    DEBUG ((DEBUG_ERROR, "%a: One or more than one of the required parameters is NULL.\n", __func__));
    return EFI_INVALID_PARAMETER;
  }

  if (CompareGuid (&gManageabilityTransportMctpGuid, TransportToken->Transport->ManageabilityTransportSpecification)) {
    DEBUG ((DEBUG_MANAGEABILITY_INFO, "%a: Setup transport header for PLDM over MCTP.\n", __func__));

    // This is MCTP transport interface.
    MctpHeader = AllocateZeroPool (sizeof (MANAGEABILITY_MCTP_TRANSPORT_HEADER));
    if (MctpHeader == NULL) {
      DEBUG ((DEBUG_ERROR, "%a: Not enough memory for MANAGEABILITY_MCTP_TRANSPORT_HEADER.\n", __func__));
      return EFI_OUT_OF_RESOURCES;
    }

    MctpHeader->SourceEndpointId             = SourceId;
    MctpHeader->DestinationEndpointId        = DestinationId;
    MctpHeader->MessageHeader.IntegrityCheck = FALSE;
    MctpHeader->MessageHeader.MessageType    = MCTP_MESSAGE_TYPE_PLDM;
    *PacketHeader                            = (MANAGEABILITY_TRANSPORT_HEADER *)MctpHeader;
    *PacketHeaderSize                        = sizeof (MANAGEABILITY_TRANSPORT_HEADER);
    *PacketTrailer                           = NULL;
    *PacketTrailerSize                       = 0;
  } else {
    DEBUG ((DEBUG_ERROR, "%a: No implementation of building up packet.\n", __func__));
    ASSERT (FALSE);
  }

  //
  // Create header for the final request message.
  //
  PldmRequestHeader = (PLDM_REQUEST_HEADER *)AllocateZeroPool (sizeof (PLDM_REQUEST_HEADER) + *PacketBodySize);
  if (PldmRequestHeader == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Not enough memory for final PLDM request message.\n", __func__));
    return EFI_OUT_OF_RESOURCES;
  }

  PldmRequestHeader->RequestBit          = PLDM_MESSAGE_HEADER_IS_REQUEST;
  PldmRequestHeader->HeaderVersion       = PLDM_MESSAGE_HEADER_VERSION;
  PldmRequestHeader->PldmType            = PldmType;
  PldmRequestHeader->PldmTypeCommandCode = PldmCommand;
  PldmRequestHeader->InstanceId          = mPldmRequestInstanceId;
  if ((*PacketBody != NULL) && (*PacketBodySize != 0)) {
    CopyMem (
      (VOID *)((UINT8 *)PldmRequestHeader + sizeof (PLDM_REQUEST_HEADER)),
      (VOID *)*PacketBody,
      *PacketBodySize
      );
  }

  *PacketBody     = (UINT8 *)PldmRequestHeader;
  *PacketBodySize = sizeof (PLDM_REQUEST_HEADER) + *PacketBodySize;
  return EFI_SUCCESS;
}

/**
  Common code to submit PLDM commands

  @param[in]         TransportToken             Transport token.
  @param[in]         PldmType                   PLDM message type.
  @param[in]         PldmCommand                PLDM command of this PLDM type.
  @param[in]         PldmTerminusSourceId       PLDM source terminus ID.
  @param[in]         PldmTerminusDestinationId  PLDM destination terminus ID.
  @param[in]         RequestData                Command Request Data.
  @param[in]         RequestDataSize            Size of Command Request Data.
  @param[out]        ResponseData               Command Response Data. The completion code is the first byte of response data.
  @param[in, out]    ResponseDataSize           Size of Command Response Data.

  @retval EFI_SUCCESS            The command byte stream was successfully submit to the device and a response was successfully received.
  @retval EFI_NOT_FOUND          The command was not successfully sent to the device or a response was not successfully received from the device.
  @retval EFI_NOT_READY          Ipmi Device is not ready for Ipmi command access.
  @retval EFI_DEVICE_ERROR       Ipmi Device hardware error.
  @retval EFI_TIMEOUT            The command time out.
  @retval EFI_UNSUPPORTED        The command was not successfully sent to the device.
  @retval EFI_OUT_OF_RESOURCES   The resource allocation is out of resource or data size error.
**/
EFI_STATUS
CommonPldmSubmitCommand (
  IN     MANAGEABILITY_TRANSPORT_TOKEN  *TransportToken,
  IN     UINT8                          PldmType,
  IN     UINT8                          PldmCommand,
  IN     UINT8                          PldmTerminusSourceId,
  IN     UINT8                          PldmTerminusDestinationId,
  IN     UINT8                          *RequestData OPTIONAL,
  IN     UINT32                         RequestDataSize,
  OUT    UINT8                          *ResponseData OPTIONAL,
  IN OUT UINT32                         *ResponseDataSize
  )
{
  EFI_STATUS                                 Status;
  UINT8                                      *ThisRequestData;
  UINT32                                     ThisRequestDataSize;
  MANAGEABILITY_TRANSFER_TOKEN               TransferToken;
  MANAGEABILITY_TRANSPORT_HEADER             PldmTransportHeader;
  MANAGEABILITY_TRANSPORT_TRAILER            PldmTransportTrailer;
  MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS  TransportAdditionalStatus;
  UINT8                                      *FullPacketResponseData;
  UINT32                                     FullPacketResponseDataSize;
  PLDM_RESPONSE_HEADER                       *ResponseHeader;
  UINT16                                     HeaderSize;
  UINT16                                     TrailerSize;

  if (TransportToken == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: No transport token for PLDM\n", __func__));
    return EFI_UNSUPPORTED;
  }

  Status = TransportToken->Transport->Function.Version1_0->TransportStatus (
                                                             TransportToken,
                                                             &TransportAdditionalStatus
                                                             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Transport %s for PLDM has problem - (%r)\n", __func__, mTransportName, Status));
    return Status;
  }

  ThisRequestData = RequestData;            // Save the original request data because the request data maybe modified
                                            // in SetupIpmiRequestTransportPacket() according to transport interface.
  ThisRequestDataSize = RequestDataSize;    // Save the original request data size because the request data size maybe modified
                                            //  in SetupIpmiRequestTransportPacket() according to transport interface.
  PldmTransportHeader  = NULL;
  PldmTransportTrailer = NULL;
  Status               = SetupPldmRequestTransportPacket (
                           TransportToken,
                           PldmType,
                           PldmCommand,
                           PldmTerminusSourceId,
                           PldmTerminusDestinationId,
                           &PldmTransportHeader,
                           &HeaderSize,
                           &ThisRequestData,
                           &ThisRequestDataSize,
                           &PldmTransportTrailer,
                           &TrailerSize
                           );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Fail to build packets - (%r)\n", __func__, Status));
    return Status;
  }

  ZeroMem (&TransferToken, sizeof (MANAGEABILITY_TRANSFER_TOKEN));
  TransferToken.TransmitHeader      = PldmTransportHeader;
  TransferToken.TransmitHeaderSize  = HeaderSize;
  TransferToken.TransmitTrailer     = PldmTransportTrailer;
  TransferToken.TransmitTrailerSize = TrailerSize;

  // Transmit packet.
  if ((ThisRequestData == NULL) || (ThisRequestDataSize == 0)) {
    // Transmit parameter were not changed by SetupIpmiRequestTransportPacket().
    TransferToken.TransmitPackage.TransmitPayload    = RequestData;
    TransferToken.TransmitPackage.TransmitSizeInByte = ThisRequestDataSize;
  } else {
    // Transmit parameter were changed by SetupIpmiRequestTransportPacket().
    TransferToken.TransmitPackage.TransmitPayload    = ThisRequestData;
    TransferToken.TransmitPackage.TransmitSizeInByte = ThisRequestDataSize;
  }

  TransferToken.TransmitPackage.TransmitTimeoutInMillisecond = MANAGEABILITY_TRANSPORT_NO_TIMEOUT;

  // Set receive packet.
  if ((ResponseData == NULL) && (*ResponseDataSize == 0)) {
    FullPacketResponseDataSize = sizeof (PLDM_RESPONSE_HEADER);
  } else {
    FullPacketResponseDataSize = *ResponseDataSize + sizeof (PLDM_RESPONSE_HEADER);
  }

  FullPacketResponseData = (UINT8 *)AllocateZeroPool (FullPacketResponseDataSize);
  if (FullPacketResponseData == NULL) {
    DEBUG ((DEBUG_ERROR, "  Not enough memory for FullPacketResponseDataSize.\n"));
    Status = EFI_OUT_OF_RESOURCES;
    goto ErrorExit;
  }

  // Print out PLDM packet.
  DEBUG ((
    DEBUG_MANAGEABILITY_INFO,
    "%a: Send PLDM type: 0x%x, Command: 0x%x: Request size: 0x%x, Response size: 0x%x\n",
    __func__,
    PldmType,
    PldmCommand,
    TransferToken.TransmitPackage.TransmitSizeInByte,
    FullPacketResponseDataSize
    ));

  HelperManageabilityDebugPrint (
    (VOID *)TransferToken.TransmitPackage.TransmitPayload,
    TransferToken.TransmitPackage.TransmitSizeInByte,
    "PLDM full request payload.\n"
    );

  TransferToken.ReceivePackage.ReceiveBuffer                = FullPacketResponseData;
  TransferToken.ReceivePackage.ReceiveSizeInByte            = FullPacketResponseDataSize;
  TransferToken.ReceivePackage.TransmitTimeoutInMillisecond = MANAGEABILITY_TRANSPORT_NO_TIMEOUT;
  TransportToken->Transport->Function.Version1_0->TransportTransmitReceive (
                                                    TransportToken,
                                                    &TransferToken
                                                    );
  //
  // Check the response size.
  //
  if (TransferToken.ReceivePackage.ReceiveSizeInByte < sizeof (PLDM_RESPONSE_HEADER)) {
    DEBUG ((
      DEBUG_MANAGEABILITY_INFO,
      "Invalid response header size of PLDM Type %d Command %d, Returned size: %d Expected size: %d\n",
      PldmType,
      PldmCommand,
      TransferToken.ReceivePackage.ReceiveSizeInByte,
      FullPacketResponseDataSize
      ));
    HelperManageabilityDebugPrint ((VOID *)FullPacketResponseData, TransferToken.ReceivePackage.ReceiveSizeInByte, "Failed response payload\n");
    Status = EFI_DEVICE_ERROR;
    goto ErrorExit;
  }

  //
  // Check the integrity of response. data.
  //
  ResponseHeader = (PLDM_RESPONSE_HEADER *)FullPacketResponseData;
  if ((ResponseHeader->PldmHeader.DatagramBit != (!PLDM_MESSAGE_HEADER_IS_DATAGRAM)) ||
      (ResponseHeader->PldmHeader.RequestBit != PLDM_MESSAGE_HEADER_IS_RESPONSE) ||
      (ResponseHeader->PldmHeader.InstanceId != mPldmRequestInstanceId) ||
      (ResponseHeader->PldmHeader.PldmType != PldmType) ||
      (ResponseHeader->PldmHeader.PldmTypeCommandCode != PldmCommand) ||
      (ResponseHeader->PldmCompletionCode != PLDM_COMPLETION_CODE_SUCCESS))
  {
    DEBUG ((DEBUG_ERROR, "PLDM integrity check of response data is failed.\n"));
    DEBUG ((DEBUG_ERROR, "    Datagram     = %d (Expected value: %d)\n", ResponseHeader->PldmHeader.DatagramBit, (!PLDM_MESSAGE_HEADER_IS_DATAGRAM)));
    DEBUG ((DEBUG_ERROR, "    Request bit  = %d (Expected value: %d)\n", ResponseHeader->PldmHeader.RequestBit, PLDM_MESSAGE_HEADER_IS_RESPONSE));
    DEBUG ((DEBUG_ERROR, "    Instance ID  = %d (Expected value: %d)\n", ResponseHeader->PldmHeader.InstanceId, mPldmRequestInstanceId));
    DEBUG ((DEBUG_ERROR, "    Pldm Type    = %d (Expected value: %d)\n", ResponseHeader->PldmHeader.PldmType, PldmType));
    DEBUG ((DEBUG_ERROR, "    Pldm Command = %d (Expected value: %d)\n", ResponseHeader->PldmHeader.PldmTypeCommandCode, PldmCommand));
    DEBUG ((DEBUG_ERROR, "    Pldm Completion Code = 0x%x\n", ResponseHeader->PldmCompletionCode));

    HelperManageabilityDebugPrint ((VOID *)FullPacketResponseData, TransferToken.ReceivePackage.ReceiveSizeInByte, "Failed response payload\n");
    Status = EFI_DEVICE_ERROR;
    goto ErrorExit;
  }

  //
  // Check the response size
  //
  if (TransferToken.ReceivePackage.ReceiveSizeInByte > FullPacketResponseDataSize) {
    DEBUG ((
      DEBUG_ERROR,
      "The response size is incorrect: Response size %d (Expected %d), Completion code %d.\n",
      TransferToken.ReceivePackage.ReceiveSizeInByte,
      FullPacketResponseDataSize,
      ResponseHeader->PldmCompletionCode
      ));

    HelperManageabilityDebugPrint ((VOID *)FullPacketResponseData, TransferToken.ReceivePackage.ReceiveSizeInByte, "Failed response payload\n");
    Status = EFI_DEVICE_ERROR;
    goto ErrorExit;
  }

  if (*ResponseDataSize < GET_PLDM_MESSAGE_PAYLOAD_SIZE (TransferToken.ReceivePackage.ReceiveSizeInByte)) {
    DEBUG ((DEBUG_ERROR, "  The size of response is not matched to RequestDataSize assigned by caller.\n"));
    DEBUG ((
      DEBUG_ERROR,
      "Caller expects %d, the response size minus PLDM_RESPONSE_HEADER size is %d, Completion Code %d.\n",
      *ResponseDataSize,
      GET_PLDM_MESSAGE_PAYLOAD_SIZE (TransferToken.ReceivePackage.ReceiveSizeInByte),
      ResponseHeader->PldmCompletionCode
      ));
    HelperManageabilityDebugPrint ((VOID *)FullPacketResponseData, GET_PLDM_MESSAGE_PAYLOAD_SIZE (TransferToken.ReceivePackage.ReceiveSizeInByte), "Failed response payload\n");
    Status = EFI_DEVICE_ERROR;
    goto ErrorExit;
  }

  // Print out PLDM full responses payload.
  HelperManageabilityDebugPrint ((VOID *)FullPacketResponseData, FullPacketResponseDataSize, "PLDM full response payload\n");

  // Copy response data (without header) to caller's buffer.
  if ((ResponseData != NULL) && (*ResponseDataSize != 0)) {
    *ResponseDataSize = GET_PLDM_MESSAGE_PAYLOAD_SIZE (TransferToken.ReceivePackage.ReceiveSizeInByte);
    CopyMem (
      (VOID *)ResponseData,
      GET_PLDM_MESSAGE_PAYLOAD_PTR (FullPacketResponseData),
      *ResponseDataSize
      );
  }

  // Return transfer status.
  //
  Status = TransferToken.TransferStatus;
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Failed to send PLDM command over %s\n", __func__, mTransportName));
  }

ErrorExit:
  if (PldmTransportHeader != NULL) {
    FreePool ((VOID *)PldmTransportHeader);
  }

  if (PldmTransportTrailer != NULL) {
    FreePool ((VOID *)PldmTransportTrailer);
  }

  if (ThisRequestData != NULL) {
    FreePool ((VOID *)ThisRequestData);
  }

  if (FullPacketResponseData != NULL) {
    FreePool ((VOID *)FullPacketResponseData);
  }

  //
  // Update PLDM message instance ID.
  mPldmRequestInstanceId++;
  mPldmRequestInstanceId &= PLDM_MESSAGE_HEADER_INSTANCE_ID_MASK;
  return Status;
}