summaryrefslogtreecommitdiff
path: root/ManageabilityPkg/Library/BaseManageabilityTransportHelperLib/BaseManageabilityTransportHelper.c
blob: 0fb5a33a5e2401b557962aaec17ec5f6bd34798d (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
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
/** @file
  Manageability Transport Helper Library

  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/ManageabilityTransportHelperLib.h>

//
// BaseManageabilityTransportHelper is used by PEI, DXE and SMM.
// Make sure the global variables added here should be unchangeable.
//
MANAGEABILITY_SPECIFICATION_NAME  ManageabilitySpecNameTable[] = {
  { &gManageabilityTransportKcsGuid,      L"KCS"       },
  { &gManageabilityTransportSmbusI2cGuid, L"SMBUS I2C" },
  { &gManageabilityTransportPciVdmGuid,   L"PCI VDM"   },
  { &gManageabilityTransportSerialGuid,   L"SERIAL"    },
  { &gManageabilityTransportMctpGuid,     L"MCTP"      },
  { &gManageabilityProtocolIpmiGuid,      L"IPMI"      },
  { &gManageabilityProtocolMctpGuid,      L"MCTP"      },
  { &gManageabilityProtocolPldmGuid,      L"PLDM"      }
};

UINT16  mManageabilitySpecNum = sizeof (ManageabilitySpecNameTable)/ sizeof (MANAGEABILITY_SPECIFICATION_NAME);

/**
  Helper function returns the human readable name of Manageability specification.

  @param[in]  SpecificationGuid         The Manageability specification GUID

  @retval      !NULL  Human readable name is returned;
  @retval       NULL  No string found, the given Manageability specification is
                      not supported.
**/
CHAR16 *
EFIAPI
HelperManageabilitySpecName (
  IN EFI_GUID  *SpecificationGuid
  )
{
  UINT16                            Index;
  MANAGEABILITY_SPECIFICATION_NAME  *ThisSpec;

  if (mManageabilitySpecNum == 0) {
    return NULL;
  }

  if ((SpecificationGuid == NULL) || IsZeroGuid (SpecificationGuid)) {
    DEBUG ((DEBUG_ERROR, "%a: Improper input GUIDs, could be NULL or zero GUID.\n", __func__));
    return NULL;
  }

  ThisSpec = ManageabilitySpecNameTable;
  for (Index = 0; Index < mManageabilitySpecNum; Index++) {
    if (CompareGuid (
          SpecificationGuid,
          ThisSpec->SpecificationGuid
          ))
    {
      return ThisSpec->SpecificationName;
    }

    ThisSpec++;
  }

  return NULL;
}

/**
  Helper function to check if the Manageability specification is supported
  by transport interface or not.

  @param[in]  TransportGuid                         GUID of the transport interface.
  @param[in]  SupportedManageabilityProtocolArray   The Manageability protocols supported
                                                    by the transport interface.
  @param[in]  NumberOfSupportedProtocolInArray      Number of protocols in the array.
  @param[in]  ManageabilityProtocolToCheck          The Manageability specification to check.

  @retval      EFI_SUCCESS            Token is created successfully.
  @retval      EFI_INVALID_PARAMETER  Either NumberOfSupportedProtocolInArray = 0 or
                                      SupportedManageabilityProtocolArray = NULL.
  @retval      EFI_UNSUPPORTED        Out of resource to create a new transport session.
               Otherwise              Other errors.
**/
EFI_STATUS
EFIAPI
HelperManageabilityCheckSupportedSpec (
  IN  EFI_GUID  *TransportGuid,
  IN  EFI_GUID  **SupportedManageabilityProtocolArray,
  IN  UINT8     NumberOfSupportedProtocolInArray,
  IN  EFI_GUID  *ManageabilityProtocolToCheck
  )
{
  UINT16    Index;
  EFI_GUID  **ThisSpecGuid;

  if ((NumberOfSupportedProtocolInArray == 0) || (SupportedManageabilityProtocolArray == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if ((TransportGuid == NULL) ||
      IsZeroGuid (TransportGuid) ||
      (ManageabilityProtocolToCheck == NULL) ||
      IsZeroGuid (ManageabilityProtocolToCheck)
      )
  {
    DEBUG ((DEBUG_ERROR, "%a: Improper input GUIDs, could be NULL or zero GUID.\n", __func__));
    return EFI_INVALID_PARAMETER;
  }

  ThisSpecGuid = SupportedManageabilityProtocolArray;
  for (Index = 0; Index < NumberOfSupportedProtocolInArray; Index++) {
    if (CompareGuid (
          *ThisSpecGuid,
          ManageabilityProtocolToCheck
          ))
    {
      DEBUG ((
        DEBUG_MANAGEABILITY_INFO,
        "%a: Transport interface %s supports %s manageability specification.\n",
        __func__,
        HelperManageabilitySpecName (TransportGuid),
        HelperManageabilitySpecName (ManageabilityProtocolToCheck)
        ));
      return EFI_SUCCESS;
    }

    ThisSpecGuid++;
  }

  DEBUG ((
    DEBUG_ERROR,
    "%a: Transport interface %s doesn't support %s manageability specification.\n",
    __func__,
    HelperManageabilitySpecName (TransportGuid),
    HelperManageabilitySpecName (ManageabilityProtocolToCheck)
    ));
  return EFI_UNSUPPORTED;
}

/**
  Helper function to acquire the Manageability transport token.

  @param[in]  ManageabilityProtocolSpec   The Manageability protocol specification.
  @param[out] TransportToken              Pointer to receive Manageability transport
                                          token.

  @retval      EFI_SUCCESS            Token is created successfully.
  @retval      EFI_OUT_OF_RESOURCES   Out of resource to create a new transport session.
  @retval      EFI_UNSUPPORTED        Token is created successfully.
  @retval      EFI_INVALID_PARAMETER  Input parameter is not valid.
               Otherwise              Other errors.
**/
EFI_STATUS
EFIAPI
HelperAcquireManageabilityTransport (
  IN  EFI_GUID                       *ManageabilityProtocolSpec,
  OUT MANAGEABILITY_TRANSPORT_TOKEN  **TransportToken
  )
{
  EFI_STATUS  Status;
  CHAR16      *ManageabilityProtocolName;
  CHAR16      *ManageabilityTransportName;

  DEBUG ((DEBUG_MANAGEABILITY_INFO, "%a: Entry\n", __func__));
  if ((TransportToken == NULL) || (ManageabilityProtocolSpec == NULL)) {
    DEBUG ((DEBUG_ERROR, "%a: One of the required input parameters is NULL.\n", __func__));
    return EFI_INVALID_PARAMETER;
  }

  *TransportToken           = NULL;
  ManageabilityProtocolName = HelperManageabilitySpecName (ManageabilityProtocolSpec);
  if (ManageabilityProtocolName == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Unsupported Manageability Protocol Specification.\n", __func__));
    return EFI_UNSUPPORTED;
  }

  DEBUG ((DEBUG_MANAGEABILITY_INFO, "  Manageability protocol %s is going to acquire transport interface token...\n", ManageabilityProtocolName));

  Status = AcquireTransportSession (ManageabilityProtocolSpec, TransportToken);
  if (Status == EFI_UNSUPPORTED) {
    DEBUG ((DEBUG_ERROR, "%a: No supported transport interface for %s packet.\n", __func__, ManageabilityProtocolName));
    return Status;
  }

  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "%a: Fail to acquire Manageability transport token for %s (%r).\n",
      __func__,
      ManageabilityProtocolName,
      Status
      ));
    return Status;
  }

  ManageabilityTransportName = HelperManageabilitySpecName ((*TransportToken)->Transport->ManageabilityTransportSpecification);
  if (ManageabilityTransportName == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Unsupported Manageability Transport Interface Specification\n", __func__));
    return EFI_UNSUPPORTED;
  }

  DEBUG ((DEBUG_MANAGEABILITY_INFO, "%a: This is the transfer session for %s over %s\n", __func__, ManageabilityProtocolName, ManageabilityTransportName));
  return Status;
}

/**
  Helper function to initial the transport interface.

  @param[in]  TransportToken              Transport token.
  @param[in]  HardwareInfo                Optional hardware information of transport interface.
  @param[out] TransportAdditionalStatus   Transport additional status.

  @retval      EFI_SUCCESS            Transport interface is initiated successfully.
  @retval      EFI_DEVICE_ERROR       The transport interface has problems
  @retval      EFI_INVALID_PARAMETER  INput parameter is not valid.
               Otherwise              Other errors.
**/
EFI_STATUS
EFIAPI
HelperInitManageabilityTransport (
  IN  MANAGEABILITY_TRANSPORT_TOKEN                 *TransportToken,
  IN  MANAGEABILITY_TRANSPORT_HARDWARE_INFORMATION  HardwareInfo OPTIONAL,
  OUT MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS     *TransportAdditionalStatus OPTIONAL
  )
{
  EFI_STATUS  Status;

  if (TransportToken == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: TransportToken is invalid.\n", __func__));
    return EFI_INVALID_PARAMETER;
  }

  // Initial transport interface.
  Status = TransportToken->Transport->Function.Version1_0->TransportInit (TransportToken, HardwareInfo);
  if ((Status != EFI_SUCCESS) && (Status != EFI_ALREADY_STARTED)) {
    if (Status == EFI_DEVICE_ERROR) {
      // Try to reset the transport and initialize it again.
      Status = TransportToken->Transport->Function.Version1_0->TransportReset (
                                                                 TransportToken,
                                                                 TransportAdditionalStatus
                                                                 );
      if (EFI_ERROR (Status)) {
        if (Status == EFI_UNSUPPORTED) {
          DEBUG ((DEBUG_ERROR, "%a: Transport interface doesn't have reset capability.\n", __func__));
        } else {
          DEBUG ((DEBUG_ERROR, "%a: Fail to reset transport interface (%r).\n", __func__, Status));
        }

        Status = EFI_DEVICE_ERROR;
      } else {
        Status = TransportToken->Transport->Function.Version1_0->TransportInit (TransportToken, HardwareInfo);
        if (EFI_ERROR (Status)) {
          DEBUG ((DEBUG_ERROR, "%a: Transport interface is not able to use after the reset (%r).\n", __func__, Status));
        }
      }
    } else {
      DEBUG ((DEBUG_ERROR, "%a: Transport interface is not able to use (%r).\n", __func__, Status));
    }
  }

  return Status;
}

/**
  This function generates CRC8 with given polynomial.

  @param[in]  Polynomial       Polynomial in 8-bit.
  @param[in]  CrcInitialValue  CRC initial value.
  @param[in]  BufferStart      Pointer to buffer starts the CRC calculation.
  @param[in]  BufferSize       Size of buffer.

  @retval  UINT8 CRC value.
**/
UINT8
EFIAPI
HelperManageabilityGenerateCrc8 (
  IN UINT8   Polynomial,
  IN UINT8   CrcInitialValue,
  IN UINT8   *BufferStart,
  IN UINT32  BufferSize
  )
{
  UINT8   BitIndex;
  UINT32  BufferIndex;

  BufferIndex = 0;
  while (BufferIndex < BufferSize) {
    CrcInitialValue = CrcInitialValue ^ *(BufferStart + BufferIndex);
    BufferIndex++;

    for (BitIndex = 0; BitIndex < 8; BitIndex++) {
      if ((CrcInitialValue & 0x80) != 0) {
        CrcInitialValue = (CrcInitialValue << 1) ^ Polynomial;
      } else {
        CrcInitialValue <<= 1;
      }
    }
  }

  return CrcInitialValue;
}

/**
  This function splits payload into multiple packages according to
  the given transport interface Maximum Transfer Unit (MTU).


  @param[in]  PreambleSize         The additional data size precedes
                                   each package.
  @param[in]  PostambleSize        The additional data size succeeds
                                   each package.
  @param[in]  Payload              Pointer to payload.
  @param[in]  PayloadSize          Payload size in byte.
  @param[in]  MaximumTransferUnit  MTU of transport interface.
  @param[out] MultiplePackages     Pointer to receive
                                   MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES
                                   structure. Caller has to free the memory
                                   allocated for MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES.

  @retval   EFI_SUCCESS          MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES structure
                                 is returned successfully.
  @retval   EFI_OUT_OF_RESOURCE  Not enough resource to create
                                 MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES structure.
**/
EFI_STATUS
EFIAPI
HelperManageabilitySplitPayload (
  IN UINT16                                      PreambleSize,
  IN UINT16                                      PostambleSize,
  IN UINT8                                       *Payload,
  IN UINT32                                      PayloadSize,
  IN UINT32                                      MaximumTransferUnit,
  OUT MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES  **MultiplePackages
  )
{
  UINT16                                     NumberOfPackages;
  UINT16                                     IndexOfPackage;
  UINT32                                     PackagePayloadSize;
  UINT32                                     TotalPayloadRemaining;
  MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES  *ThisMultiplePackages;
  MANAGEABILITY_TRANSMISSION_PACKAGE_ATTR    *ThisPackage;

  if ((INT16)(MaximumTransferUnit - PreambleSize - PostambleSize) < 0) {
    DEBUG ((
      DEBUG_ERROR,
      "%a: (Preamble 0x%x + PostambleSize 0x%x) is greater than MaximumTransferUnit 0x%x.\n",
      __func__,
      PreambleSize,
      PostambleSize,
      MaximumTransferUnit
      ));
    return EFI_INVALID_PARAMETER;
  }

  PackagePayloadSize   = MaximumTransferUnit -PreambleSize - PostambleSize;
  NumberOfPackages     = (UINT16)((PayloadSize + (PackagePayloadSize - 1)) / PackagePayloadSize);
  ThisMultiplePackages = (MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES *)AllocateZeroPool (
                                                                        sizeof (MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES) +
                                                                        sizeof (MANAGEABILITY_TRANSMISSION_PACKAGE_ATTR) * NumberOfPackages
                                                                        );
  if (ThisMultiplePackages == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Not enough memory for MANAGEABILITY_TRANSMISSION_MULTI_PACKAGES\n", __func__));
    return EFI_OUT_OF_RESOURCES;
  }

  ThisMultiplePackages->NumberOfPackages = NumberOfPackages;
  ThisPackage                            = (MANAGEABILITY_TRANSMISSION_PACKAGE_ATTR *)(ThisMultiplePackages + 1);
  TotalPayloadRemaining                  = PayloadSize;
  for (IndexOfPackage = 0; IndexOfPackage < NumberOfPackages; IndexOfPackage++) {
    ThisPackage->PayloadPointer = Payload + (IndexOfPackage * PackagePayloadSize);
    ThisPackage->PayloadSize    = MIN (TotalPayloadRemaining, PackagePayloadSize);
    TotalPayloadRemaining      -= ThisPackage->PayloadSize;
    ThisPackage++;
  }

  if (TotalPayloadRemaining != 0) {
    DEBUG ((DEBUG_ERROR, "%a: Error processing multiple packages (TotalPayloadRemaining != 0)\n", __func__));
    FreePool (ThisMultiplePackages);
    return EFI_INVALID_PARAMETER;
  }

  *MultiplePackages = ThisMultiplePackages;
  return EFI_SUCCESS;
}

/**
  Print out manageability transmit payload to the debug output device.

  @param[in]  Payload      Payload to print.
  @param[in]  PayloadSize  Payload size.

**/
VOID
EFIAPI
HelperManageabilityPayLoadDebugPrint (
  IN  VOID    *Payload,
  IN  UINT32  PayloadSize
  )
{
  UINTN  Block;
  UINTN  BlockSize;
  UINTN  RowSize;
  UINTN  RemainingBytes;
  UINTN  BytesPrinted;

  RemainingBytes = PayloadSize;
  BlockSize      = 256;
  RowSize        = 16;

  for (Block = 0, BytesPrinted = 0; RemainingBytes > 0;) {
    UINTN  BlockBase;
    UINTN  Row;
    UINTN  RowsToPrint;
    UINTN  Column;

    Block     = BytesPrinted / BlockSize;
    BlockBase = Block * BlockSize;

    if (BytesPrinted % BlockSize == 0) {
      DEBUG ((DEBUG_MANAGEABILITY_INFO, "======== Manageability Payload %04xH - %04xH =========\n", BlockBase, BlockBase + MIN (RemainingBytes, BlockSize) - 1));
      DEBUG ((DEBUG_MANAGEABILITY_INFO, "       "));
      for (Column = 0; Column < RowSize; Column++) {
        DEBUG ((DEBUG_MANAGEABILITY_INFO, "%02x ", Column));
      }

      DEBUG ((DEBUG_MANAGEABILITY_INFO, "\n       -----------------------------------------------\n"));
    }

    if (RemainingBytes >= BlockSize) {
      RowsToPrint = BlockSize / RowSize;
    } else {
      RowsToPrint = RemainingBytes / RowSize + 1;
    }

    for (Row = 0; Row < RowsToPrint; Row++) {
      DEBUG ((DEBUG_MANAGEABILITY_INFO, "%04x | ", BlockBase + Row * RowSize));
      for (Column = 0; Column < MIN (RemainingBytes, RowSize); Column++) {
        DEBUG ((DEBUG_MANAGEABILITY_INFO, "%02x ", *((UINT8 *)Payload + BlockBase + Row * RowSize + Column)));
      }

      RemainingBytes -= Column;
      BytesPrinted   += Column;

      DEBUG ((DEBUG_MANAGEABILITY_INFO, "\n"));
    }

    DEBUG ((DEBUG_MANAGEABILITY_INFO, "\n"));
  }
}

/**
  Prints a debug message and manageability payload to the debug output device.

  @param[in]  Payload      Payload to print.
  @param[in]  PayloadSize  Payload size.
  @param[in]  Format       The format string for the debug message to print.
  @param[in]  ...          The variable argument list whose contents are accessed
                           based on the format string specified by Format.

**/
VOID
EFIAPI
HelperManageabilityDebugPrint (
  IN  VOID         *Payload,
  IN  UINT32       PayloadSize,
  IN  CONST CHAR8  *Format,
  ...
  )
{
  VA_LIST  Marker;

  VA_START (Marker, Format);
  DEBUG ((DEBUG_MANAGEABILITY_INFO, "Manageability Transmission: "));
  DebugVPrint ((UINTN)DEBUG_MANAGEABILITY_INFO, Format, Marker);
  HelperManageabilityPayLoadDebugPrint (Payload, PayloadSize);
  VA_END (Marker);
}