| /** @file | |
| RedfishHttpDxe produces EdkIIRedfishHttpProtocol | |
| for EDK2 Redfish Feature driver to do HTTP operations. | |
| Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "RedfishHttpDxe.h" | |
| #include "RedfishHttpData.h" | |
| #include "RedfishHttpOperation.h" | |
| REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate = NULL; | |
| /** | |
| Debug output the cache list. | |
| @param[in] Msg Debug message string. | |
| @param[in] ErrorLevel Output error level. | |
| @param[in] CacheList Target list to dump. | |
| @retval EFI_SUCCESS Debug dump finished. | |
| @retval EFI_INVALID_PARAMETER HttpCacheList is NULL. | |
| **/ | |
| EFI_STATUS | |
| DebugPrintHttpCacheList ( | |
| IN CONST CHAR8 *Msg, | |
| IN UINTN ErrorLevel, | |
| IN REDFISH_HTTP_CACHE_LIST *CacheList | |
| ) | |
| { | |
| LIST_ENTRY *List; | |
| REDFISH_HTTP_CACHE_DATA *Data; | |
| UINTN Index; | |
| if (CacheList == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (!IS_EMPTY_STRING (Msg)) { | |
| DEBUG ((ErrorLevel, "%a\n", Msg)); | |
| } | |
| if (IsListEmpty (&CacheList->Head)) { | |
| DEBUG ((ErrorLevel, "list is empty\n")); | |
| return EFI_NOT_FOUND; | |
| } | |
| DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count, CacheList->Capacity)); | |
| Data = NULL; | |
| Index = 0; | |
| List = GetFirstNode (&CacheList->Head); | |
| while (!IsNull (&CacheList->Head, List)) { | |
| Data = REDFISH_HTTP_CACHE_FROM_LIST (List); | |
| DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data->HitCount)); | |
| List = GetNextNode (&CacheList->Head, List); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check HTTP status code to see if we like to retry HTTP request or not. | |
| @param[in] StatusCode HTTP status code. | |
| @retval BOOLEAN Return true when we like to retry request. | |
| Return false when we don't want to retry request. | |
| **/ | |
| BOOLEAN | |
| RedfishRetryRequired ( | |
| IN EFI_HTTP_STATUS_CODE *StatusCode | |
| ) | |
| { | |
| if (StatusCode == NULL) { | |
| return TRUE; | |
| } | |
| if ((*StatusCode == HTTP_STATUS_500_INTERNAL_SERVER_ERROR) || | |
| (*StatusCode == HTTP_STATUS_UNSUPPORTED_STATUS)) | |
| { | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| This function follows below sections in Redfish specification to | |
| check HTTP status code and see if this is success response or not. | |
| 7.5.2 Modification success responses | |
| 7.11 POST (action) | |
| @param[in] Method HTTP method of this status code. | |
| @param[in] StatusCode HTTP status code. | |
| @retval BOOLEAN Return true when this is success response. | |
| Return false when this is not success response. | |
| **/ | |
| BOOLEAN | |
| RedfishSuccessResponse ( | |
| IN EFI_HTTP_METHOD Method, | |
| IN EFI_HTTP_STATUS_CODE *StatusCode | |
| ) | |
| { | |
| BOOLEAN SuccessResponse; | |
| if (StatusCode == NULL) { | |
| return TRUE; | |
| } | |
| SuccessResponse = FALSE; | |
| switch (Method) { | |
| case HttpMethodPost: | |
| if ((*StatusCode == HTTP_STATUS_200_OK) || | |
| (*StatusCode == HTTP_STATUS_201_CREATED) || | |
| (*StatusCode == HTTP_STATUS_202_ACCEPTED) || | |
| (*StatusCode == HTTP_STATUS_204_NO_CONTENT)) | |
| { | |
| SuccessResponse = TRUE; | |
| } | |
| break; | |
| case HttpMethodPatch: | |
| case HttpMethodPut: | |
| case HttpMethodDelete: | |
| if ((*StatusCode == HTTP_STATUS_200_OK) || | |
| (*StatusCode == HTTP_STATUS_202_ACCEPTED) || | |
| (*StatusCode == HTTP_STATUS_204_NO_CONTENT)) | |
| { | |
| SuccessResponse = TRUE; | |
| } | |
| break; | |
| default: | |
| // | |
| // Return true for unsupported method to prevent false alarm. | |
| // | |
| SuccessResponse = TRUE; | |
| break; | |
| } | |
| return SuccessResponse; | |
| } | |
| /** | |
| Convert Unicode string to ASCII string. It's call responsibility to release returned buffer. | |
| @param[in] UnicodeStr Unicode string to convert. | |
| @retval CHAR8 * ASCII string returned. | |
| @retval NULL Errors occur. | |
| **/ | |
| CHAR8 * | |
| StringUnicodeToAscii ( | |
| IN EFI_STRING UnicodeStr | |
| ) | |
| { | |
| CHAR8 *AsciiStr; | |
| UINTN AsciiStrSize; | |
| EFI_STATUS Status; | |
| if (IS_EMPTY_STRING (UnicodeStr)) { | |
| return NULL; | |
| } | |
| AsciiStrSize = StrLen (UnicodeStr) + 1; | |
| AsciiStr = AllocateZeroPool (AsciiStrSize); | |
| if (AsciiStr == NULL) { | |
| return NULL; | |
| } | |
| Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status)); | |
| FreePool (AsciiStr); | |
| return NULL; | |
| } | |
| return AsciiStr; | |
| } | |
| /** | |
| Return HTTP method in ASCII string. Caller does not need | |
| to free returned string buffer. | |
| @param[in] Method HTTP method. | |
| @retval CHAR8 * Method in string. | |
| **/ | |
| CHAR8 * | |
| HttpMethodToString ( | |
| IN EFI_HTTP_METHOD Method | |
| ) | |
| { | |
| switch (Method) { | |
| case HttpMethodGet: | |
| return HTTP_METHOD_GET; | |
| break; | |
| case HttpMethodPost: | |
| return HTTP_METHOD_POST; | |
| break; | |
| case HttpMethodPatch: | |
| return HTTP_METHOD_PATCH; | |
| break; | |
| case HttpMethodPut: | |
| return HTTP_METHOD_PUT; | |
| break; | |
| case HttpMethodDelete: | |
| return HTTP_METHOD_DELETE; | |
| break; | |
| default: | |
| break; | |
| } | |
| return "Unknown"; | |
| } | |
| /** | |
| Report HTTP communication error via report status code. | |
| @param[in] Method HTTP method. | |
| @param[in] Uri The URI which has failure. | |
| @param[in] HttpStatusCode HTTP status code. | |
| **/ | |
| VOID | |
| ReportHttpError ( | |
| IN EFI_HTTP_METHOD Method, | |
| IN EFI_STRING Uri, | |
| IN EFI_HTTP_STATUS_CODE *HttpStatusCode OPTIONAL | |
| ) | |
| { | |
| CHAR8 ErrorMsg[REDFISH_ERROR_MSG_MAX]; | |
| if (IS_EMPTY_STRING (Uri)) { | |
| DEBUG ((DEBUG_ERROR, "%a: no URI to report error status\n", __func__)); | |
| return; | |
| } | |
| // | |
| // Report failure of URI and HTTP status code. | |
| // | |
| AsciiSPrint (ErrorMsg, sizeof (ErrorMsg), REDFISH_HTTP_ERROR_REPORT, HttpMethodToString (Method), (HttpStatusCode == NULL ? HTTP_STATUS_UNSUPPORTED_STATUS : *HttpStatusCode), Uri); | |
| DEBUG ((DEBUG_ERROR, "%a\n", ErrorMsg)); | |
| // | |
| // Report this failure via status code and BMC has chance to capture the error. | |
| // | |
| REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( | |
| EFI_ERROR_CODE | EFI_ERROR_MAJOR, | |
| EFI_COMPUTING_UNIT_MANAGEABILITY | EFI_MANAGEABILITY_EC_REDFISH_COMMUNICATION_ERROR, | |
| ErrorMsg, | |
| AsciiStrSize (ErrorMsg) | |
| ); | |
| } | |
| /** | |
| This function create Redfish service. It's caller's responsibility to free returned | |
| Redfish service by calling FreeService (). | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] RedfishConfigServiceInfo Redfish config service information. | |
| @retval REDFISH_SERVICE Redfish service is created. | |
| @retval NULL Errors occur. | |
| **/ | |
| REDFISH_SERVICE | |
| EFIAPI | |
| RedfishCreateRedfishService ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| REDFISH_SERVICE_PRIVATE *NewService; | |
| CHAR8 *AsciiLocation; | |
| CHAR8 *Host; | |
| CHAR8 *BasicAuthString; | |
| UINTN BasicAuthStrSize; | |
| CHAR8 *EncodedAuthString; | |
| UINTN EncodedAuthStrSize; | |
| EDKII_REDFISH_AUTH_METHOD AuthMethod; | |
| CHAR8 *Username; | |
| CHAR8 *Password; | |
| UINTN UsernameSize; | |
| UINTN PasswordSize; | |
| EFI_REST_EX_PROTOCOL *RestEx; | |
| if ((This == NULL) || (RedfishConfigServiceInfo == NULL)) { | |
| return NULL; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: service location: %s\n", __func__, RedfishConfigServiceInfo->RedfishServiceLocation)); | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| BasicAuthString = NULL; | |
| EncodedAuthString = NULL; | |
| Username = NULL; | |
| Password = NULL; | |
| NewService = NULL; | |
| AsciiLocation = NULL; | |
| Host = NULL; | |
| BasicAuthStrSize = 0; | |
| EncodedAuthStrSize = 0; | |
| UsernameSize = 0; | |
| PasswordSize = 0; | |
| // | |
| // Build host and host name from service location | |
| // | |
| if (!IS_EMPTY_STRING (RedfishConfigServiceInfo->RedfishServiceLocation)) { | |
| AsciiLocation = StringUnicodeToAscii (RedfishConfigServiceInfo->RedfishServiceLocation); | |
| if (AsciiLocation == NULL) { | |
| goto ON_RELEASE; | |
| } | |
| Host = AllocateZeroPool (REDFISH_HOST_NAME_MAX); | |
| if (AsciiLocation == NULL) { | |
| goto ON_RELEASE; | |
| } | |
| if (RedfishConfigServiceInfo->RedfishServiceUseHttps) { | |
| AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "https://%a", AsciiLocation); | |
| } else { | |
| AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "http://%a", AsciiLocation); | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Host: %a\n", __func__, Host)); | |
| } | |
| // | |
| // Find Rest Ex protocol | |
| // | |
| if (RedfishConfigServiceInfo->RedfishServiceRestExHandle != NULL) { | |
| Status = gBS->HandleProtocol ( | |
| RedfishConfigServiceInfo->RedfishServiceRestExHandle, | |
| &gEfiRestExProtocolGuid, | |
| (VOID **)&RestEx | |
| ); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "%a: Rest Ex protocol is not available\n", __func__)); | |
| goto ON_RELEASE; | |
| } | |
| // | |
| // Get credential | |
| // | |
| if (Private->CredentialProtocol == NULL) { | |
| // | |
| // No credential available on this system. | |
| // | |
| DEBUG ((DEBUG_WARN, "%a: no credential protocol available\n", __func__)); | |
| } else { | |
| Status = Private->CredentialProtocol->GetAuthInfo ( | |
| Private->CredentialProtocol, | |
| &AuthMethod, | |
| &Username, | |
| &Password | |
| ); | |
| if (EFI_ERROR (Status) || ((AuthMethod != AuthMethodNone) && (IS_EMPTY_STRING (Username) || IS_EMPTY_STRING (Password)))) { | |
| DEBUG ((DEBUG_ERROR, "%a: cannot get authentication information: %r\n", __func__, Status)); | |
| goto ON_RELEASE; | |
| } else if (AuthMethod != AuthMethodNone) { | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Auth method: 0x%x username: %a password: %a\n", __func__, AuthMethod, Username, Password)); | |
| // | |
| // Perform base64 encoding (RFC 7617) | |
| // | |
| UsernameSize = AsciiStrSize (Username); | |
| PasswordSize = AsciiStrSize (Password); | |
| BasicAuthStrSize = UsernameSize + PasswordSize; // one byte taken from null-terminator for ':' | |
| BasicAuthString = AllocateZeroPool (BasicAuthStrSize); | |
| if (BasicAuthString == NULL) { | |
| goto ON_RELEASE; | |
| } | |
| AsciiSPrint ( | |
| BasicAuthString, | |
| BasicAuthStrSize, | |
| "%a:%a", | |
| Username, | |
| Password | |
| ); | |
| Status = Base64Encode ( | |
| (CONST UINT8 *)BasicAuthString, | |
| AsciiStrLen (BasicAuthString), | |
| EncodedAuthString, | |
| &EncodedAuthStrSize | |
| ); | |
| if ((Status == EFI_BUFFER_TOO_SMALL) && (EncodedAuthStrSize > 0)) { | |
| EncodedAuthString = AllocateZeroPool (EncodedAuthStrSize); | |
| if (EncodedAuthString == NULL) { | |
| goto ON_RELEASE; | |
| } | |
| Status = Base64Encode ( | |
| (CONST UINT8 *)BasicAuthString, | |
| AsciiStrLen (BasicAuthString), | |
| EncodedAuthString, | |
| &EncodedAuthStrSize | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__, Status)); | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Basic authorization: %a\n", __func__, EncodedAuthString)); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__, Status)); | |
| goto ON_RELEASE; | |
| } | |
| } | |
| } | |
| NewService = CreateRedfishService (Host, AsciiLocation, EncodedAuthString, NULL, RestEx); | |
| if (NewService == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a: CreateRedfishService\n", __func__)); | |
| goto ON_RELEASE; | |
| } | |
| if (Private->CredentialProtocol != NULL) { | |
| Status = Private->CredentialProtocol->RegisterRedfishService (Private->CredentialProtocol, NewService); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: Failed to register Redfish service - %r\n", __func__, Status)); | |
| } | |
| } | |
| ON_RELEASE: | |
| if (BasicAuthString != NULL) { | |
| ZeroMem (BasicAuthString, BasicAuthStrSize); | |
| FreePool (BasicAuthString); | |
| } | |
| if (EncodedAuthString != NULL) { | |
| ZeroMem (BasicAuthString, EncodedAuthStrSize); | |
| FreePool (EncodedAuthString); | |
| } | |
| if (Username != NULL) { | |
| ZeroMem (Username, UsernameSize); | |
| FreePool (Username); | |
| } | |
| if (Password != NULL) { | |
| ZeroMem (Password, PasswordSize); | |
| FreePool (Password); | |
| } | |
| if (AsciiLocation != NULL) { | |
| FreePool (AsciiLocation); | |
| } | |
| if (Host != NULL) { | |
| FreePool (Host); | |
| } | |
| return NewService; | |
| } | |
| /** | |
| This function free resources in Redfish service. RedfishService is no longer available | |
| after this function returns successfully. | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] RedfishService Pointer to Redfish service to be released. | |
| @retval EFI_SUCCESS Resource is released successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishFreeRedfishService ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_SERVICE RedfishService | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| REDFISH_SERVICE_PRIVATE *Service; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| if ((This == NULL) || (RedfishService == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| Service = (REDFISH_SERVICE_PRIVATE *)RedfishService; | |
| if (Service->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) { | |
| DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); | |
| } | |
| if (Private->CredentialProtocol != NULL) { | |
| Status = Private->CredentialProtocol->UnregisterRedfishService (Private->CredentialProtocol, RedfishService); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: Failed to unregister Redfish service - %r\n", __func__, Status)); | |
| } else { | |
| if (Service->RestEx != NULL) { | |
| Status = Service->RestEx->Configure (Service->RestEx, NULL); | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: release RestEx instance: %r\n", __func__, Status)); | |
| } | |
| } | |
| } | |
| return ReleaseRedfishService (Service); | |
| } | |
| /** | |
| This function returns JSON value in given RedfishPayload. Returned JSON value | |
| is a reference to the JSON value in RedfishPayload. Any modification to returned | |
| JSON value will change JSON value in RedfishPayload. | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] RedfishPayload Pointer to Redfish payload. | |
| @retval EDKII_JSON_VALUE JSON value is returned. | |
| @retval NULL Errors occur. | |
| **/ | |
| EDKII_JSON_VALUE | |
| EFIAPI | |
| RedfishJsonInRedfishPayload ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_PAYLOAD RedfishPayload | |
| ) | |
| { | |
| REDFISH_PAYLOAD_PRIVATE *Payload; | |
| if ((This == NULL) || (RedfishPayload == NULL)) { | |
| return NULL; | |
| } | |
| Payload = (REDFISH_PAYLOAD_PRIVATE *)RedfishPayload; | |
| if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) { | |
| DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); | |
| } | |
| return Payload->JsonValue; | |
| } | |
| /** | |
| Perform HTTP GET to Get redfish resource from given resource URI with | |
| cache mechanism supported. It's caller's responsibility to free Response | |
| by calling FreeResponse (). | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Service Redfish service instance to perform HTTP GET. | |
| @param[in] Uri Target resource URI. | |
| @param[in] Request Additional request context. This is optional. | |
| @param[out] Response HTTP response from redfish service. | |
| @param[in] UseCache If it is TRUE, this function will search for | |
| cache first. If it is FALSE, this function | |
| will query Redfish URI directly. | |
| @retval EFI_SUCCESS Resource is returned successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishGetResource ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_SERVICE Service, | |
| IN EFI_STRING Uri, | |
| IN REDFISH_REQUEST *Request OPTIONAL, | |
| OUT REDFISH_RESPONSE *Response, | |
| IN BOOLEAN UseCache | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| REDFISH_HTTP_CACHE_DATA *CacheData; | |
| UINTN RetryCount; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Get URI: %s cache: %a\n", __func__, Uri, (UseCache ? "true" : "false"))); | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| CacheData = NULL; | |
| RetryCount = 0; | |
| ZeroMem (Response, sizeof (REDFISH_RESPONSE)); | |
| if (Private->CacheDisabled) { | |
| UseCache = FALSE; | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache is disabled by PCD!\n", __func__)); | |
| } | |
| // | |
| // Search for cache list. | |
| // | |
| if (UseCache) { | |
| CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri); | |
| if (CacheData != NULL) { | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: cache hit! %s\n", __func__, Uri)); | |
| // | |
| // Copy cached response to caller's buffer. | |
| // | |
| Status = CopyRedfishResponse (CacheData->Response, Response); | |
| CacheData->HitCount += 1; | |
| return Status; | |
| } | |
| } | |
| // | |
| // Get resource from redfish service. | |
| // | |
| do { | |
| RetryCount += 1; | |
| Status = HttpSendReceive ( | |
| Service, | |
| Uri, | |
| HttpMethodGet, | |
| Request, | |
| Response | |
| ); | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status)); | |
| if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryGet)) { | |
| break; | |
| } | |
| // | |
| // Retry when BMC is not ready. | |
| // | |
| if ((Response->StatusCode != NULL)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| if (!RedfishRetryRequired (Response->StatusCode)) { | |
| break; | |
| } | |
| // | |
| // Release response for next round of request. | |
| // | |
| This->FreeResponse (This, Response); | |
| } | |
| DEBUG ((DEBUG_WARN, "%a: RedfishGetByUriEx failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryGet)); | |
| if (Private->RetrySetting.RetryWait > 0) { | |
| gBS->Stall (Private->RetrySetting.RetryWait); | |
| } | |
| } while (TRUE); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| // | |
| // Report status code for Redfish failure | |
| // | |
| ReportHttpError (HttpMethodGet, Uri, Response->StatusCode); | |
| DEBUG ((DEBUG_ERROR, "%a: get %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryGet, Status)); | |
| goto ON_RELEASE; | |
| } | |
| if (!Private->CacheDisabled) { | |
| // | |
| // Keep response in cache list | |
| // | |
| Status = AddHttpCacheData (&Private->CacheList, Uri, Response); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri, Status)); | |
| goto ON_RELEASE; | |
| } | |
| DEBUG_CODE ( | |
| DebugPrintHttpCacheList (__func__, REDFISH_HTTP_CACHE_DEBUG_DUMP, &Private->CacheList); | |
| ); | |
| } | |
| ON_RELEASE: | |
| return Status; | |
| } | |
| /** | |
| This function free resources in Request. Request is no longer available | |
| after this function returns successfully. | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Request HTTP request to be released. | |
| @retval EFI_SUCCESS Resource is released successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishFreeRequest ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_REQUEST *Request | |
| ) | |
| { | |
| if ((This == NULL) || (Request == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__)); | |
| return ReleaseRedfishRequest (Request); | |
| } | |
| /** | |
| This function free resources in given Response. | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Response HTTP response to be released. | |
| @retval EFI_SUCCESS Resource is released successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishFreeResponse ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_RESPONSE *Response | |
| ) | |
| { | |
| if ((This == NULL) || (Response == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__)); | |
| return ReleaseRedfishResponse (Response); | |
| } | |
| /** | |
| This function expire the cached response of given URI. | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Uri Target response of URI. | |
| @retval EFI_SUCCESS Target response is expired successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishExpireResponse ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN EFI_STRING Uri | |
| ) | |
| { | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| REDFISH_HTTP_CACHE_DATA *CacheData; | |
| if ((This == NULL) || IS_EMPTY_STRING (Uri)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: expire URI: %s\n", __func__, Uri)); | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri); | |
| if (CacheData == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| return DeleteHttpCacheData (&Private->CacheList, CacheData); | |
| } | |
| /** | |
| Perform HTTP PATCH to send redfish resource to given resource URI. | |
| It's caller's responsibility to free Response by calling FreeResponse (). | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Service Redfish service instance to perform HTTP PATCH. | |
| @param[in] Uri Target resource URI. | |
| @param[in] Content Data to patch. | |
| @param[in] ContentSize Size of the Content to be send to Redfish service. | |
| This is optional. When ContentSize is 0, ContentSize | |
| is the size of Content. | |
| @param[in] ContentType Type of the Content to be send to Redfish service. | |
| This is optional. When ContentType is NULL, content | |
| type HTTP_CONTENT_TYPE_APP_JSON will be used. | |
| @param[out] Response HTTP response from redfish service. | |
| @retval EFI_SUCCESS Resource is returned successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishPatchResource ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_SERVICE Service, | |
| IN EFI_STRING Uri, | |
| IN CHAR8 *Content, | |
| IN UINTN ContentSize OPTIONAL, | |
| IN CHAR8 *ContentType OPTIONAL, | |
| OUT REDFISH_RESPONSE *Response | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN RetryCount; | |
| REDFISH_REQUEST Request; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Patch URI: %s\n", __func__, Uri)); | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| RetryCount = 0; | |
| ZeroMem (Response, sizeof (REDFISH_RESPONSE)); | |
| ZeroMem (&Request, sizeof (REDFISH_REQUEST)); | |
| Request.Content = Content; | |
| Request.ContentLength = ContentSize; | |
| Request.ContentType = ContentType; | |
| // | |
| // Patch resource to redfish service. | |
| // | |
| do { | |
| RetryCount += 1; | |
| Status = HttpSendReceive ( | |
| Service, | |
| Uri, | |
| HttpMethodPatch, | |
| &Request, | |
| Response | |
| ); | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status)); | |
| if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPatch)) { | |
| break; | |
| } | |
| // | |
| // Retry when BMC is not ready. | |
| // | |
| if ((Response->StatusCode != NULL)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| if (!RedfishRetryRequired (Response->StatusCode)) { | |
| break; | |
| } | |
| // | |
| // Release response for next round of request. | |
| // | |
| This->FreeResponse (This, Response); | |
| } | |
| DEBUG ((DEBUG_WARN, "%a: RedfishPatchToUriEx failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPatch)); | |
| if (Private->RetrySetting.RetryWait > 0) { | |
| gBS->Stall (Private->RetrySetting.RetryWait); | |
| } | |
| } while (TRUE); | |
| // | |
| // Redfish resource is updated. Automatically expire the cached response | |
| // so application can directly get resource from Redfish service again. | |
| // | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri)); | |
| RedfishExpireResponse (This, Uri); | |
| if (EFI_ERROR (Status) || !RedfishSuccessResponse (HttpMethodPatch, Response->StatusCode)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| // | |
| // Report status code for Redfish failure | |
| // | |
| ReportHttpError (HttpMethodPatch, Uri, Response->StatusCode); | |
| DEBUG ((DEBUG_ERROR, "%a: patch %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPatch, Status)); | |
| goto ON_RELEASE; | |
| } | |
| ON_RELEASE: | |
| return Status; | |
| } | |
| /** | |
| Perform HTTP PUT to send redfish resource to given resource URI. | |
| It's caller's responsibility to free Response by calling FreeResponse (). | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Service Redfish service instance to perform HTTP PUT. | |
| @param[in] Uri Target resource URI. | |
| @param[in] Content Data to put. | |
| @param[in] ContentSize Size of the Content to be send to Redfish service. | |
| This is optional. When ContentSize is 0, ContentSize | |
| is the size of Content. | |
| @param[in] ContentType Type of the Content to be send to Redfish service. | |
| This is optional. When ContentType is NULL, content | |
| type HTTP_CONTENT_TYPE_APP_JSON will be used. | |
| @param[out] Response HTTP response from redfish service. | |
| @retval EFI_SUCCESS Resource is returned successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishPutResource ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_SERVICE Service, | |
| IN EFI_STRING Uri, | |
| IN CHAR8 *Content, | |
| IN UINTN ContentSize OPTIONAL, | |
| IN CHAR8 *ContentType OPTIONAL, | |
| OUT REDFISH_RESPONSE *Response | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN RetryCount; | |
| REDFISH_REQUEST Request; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Put URI: %s\n", __func__, Uri)); | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| RetryCount = 0; | |
| ZeroMem (Response, sizeof (REDFISH_RESPONSE)); | |
| ZeroMem (&Request, sizeof (REDFISH_REQUEST)); | |
| Request.Content = Content; | |
| Request.ContentLength = ContentSize; | |
| Request.ContentType = ContentType; | |
| // | |
| // Patch resource to redfish service. | |
| // | |
| do { | |
| RetryCount += 1; | |
| Status = HttpSendReceive ( | |
| Service, | |
| Uri, | |
| HttpMethodPut, | |
| &Request, | |
| Response | |
| ); | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status)); | |
| if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPut)) { | |
| break; | |
| } | |
| // | |
| // Retry when BMC is not ready. | |
| // | |
| if ((Response->StatusCode != NULL)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| if (!RedfishRetryRequired (Response->StatusCode)) { | |
| break; | |
| } | |
| // | |
| // Release response for next round of request. | |
| // | |
| This->FreeResponse (This, Response); | |
| } | |
| DEBUG ((DEBUG_WARN, "%a: RedfishPutToUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPut)); | |
| if (Private->RetrySetting.RetryWait > 0) { | |
| gBS->Stall (Private->RetrySetting.RetryWait); | |
| } | |
| } while (TRUE); | |
| // | |
| // Redfish resource is updated. Automatically expire the cached response | |
| // so application can directly get resource from Redfish service again. | |
| // | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri)); | |
| RedfishExpireResponse (This, Uri); | |
| if (EFI_ERROR (Status) || !RedfishSuccessResponse (HttpMethodPut, Response->StatusCode)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| // | |
| // Report status code for Redfish failure | |
| // | |
| ReportHttpError (HttpMethodPut, Uri, Response->StatusCode); | |
| DEBUG ((DEBUG_ERROR, "%a: put %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPut, Status)); | |
| goto ON_RELEASE; | |
| } | |
| ON_RELEASE: | |
| return Status; | |
| } | |
| /** | |
| Perform HTTP POST to send redfish resource to given resource URI. | |
| It's caller's responsibility to free Response by calling FreeResponse (). | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Service Redfish service instance to perform HTTP POST. | |
| @param[in] Uri Target resource URI. | |
| @param[in] Content Data to post. | |
| @param[in] ContentSize Size of the Content to be send to Redfish service. | |
| This is optional. When ContentSize is 0, ContentSize | |
| is the size of Content. | |
| @param[in] ContentType Type of the Content to be send to Redfish service. | |
| This is optional. When ContentType is NULL, content | |
| type HTTP_CONTENT_TYPE_APP_JSON will be used. | |
| @param[out] Response HTTP response from redfish service. | |
| @retval EFI_SUCCESS Resource is returned successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishPostResource ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_SERVICE Service, | |
| IN EFI_STRING Uri, | |
| IN CHAR8 *Content, | |
| IN UINTN ContentSize OPTIONAL, | |
| IN CHAR8 *ContentType OPTIONAL, | |
| OUT REDFISH_RESPONSE *Response | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN RetryCount; | |
| REDFISH_REQUEST Request; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Post URI: %s\n", __func__, Uri)); | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| RetryCount = 0; | |
| ZeroMem (Response, sizeof (REDFISH_RESPONSE)); | |
| ZeroMem (&Request, sizeof (REDFISH_REQUEST)); | |
| Request.Content = Content; | |
| Request.ContentLength = ContentSize; | |
| Request.ContentType = ContentType; | |
| // | |
| // Patch resource to redfish service. | |
| // | |
| do { | |
| RetryCount += 1; | |
| Status = HttpSendReceive ( | |
| Service, | |
| Uri, | |
| HttpMethodPost, | |
| &Request, | |
| Response | |
| ); | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status)); | |
| if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPost)) { | |
| break; | |
| } | |
| // | |
| // Retry when BMC is not ready. | |
| // | |
| if ((Response->StatusCode != NULL)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| if (!RedfishRetryRequired (Response->StatusCode)) { | |
| break; | |
| } | |
| // | |
| // Release response for next round of request. | |
| // | |
| This->FreeResponse (This, Response); | |
| } | |
| DEBUG ((DEBUG_WARN, "%a: RedfishPostToUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPost)); | |
| if (Private->RetrySetting.RetryWait > 0) { | |
| gBS->Stall (Private->RetrySetting.RetryWait); | |
| } | |
| } while (TRUE); | |
| // | |
| // Redfish resource is updated. Automatically expire the cached response | |
| // so application can directly get resource from Redfish service again. | |
| // | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri)); | |
| RedfishExpireResponse (This, Uri); | |
| if (EFI_ERROR (Status) || !RedfishSuccessResponse (HttpMethodPost, Response->StatusCode)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| // | |
| // Report status code for Redfish failure | |
| // | |
| ReportHttpError (HttpMethodPost, Uri, Response->StatusCode); | |
| DEBUG ((DEBUG_ERROR, "%a: post %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPost, Status)); | |
| goto ON_RELEASE; | |
| } | |
| ON_RELEASE: | |
| return Status; | |
| } | |
| /** | |
| Perform HTTP DELETE to delete redfish resource on given resource URI. | |
| It's caller's responsibility to free Response by calling FreeResponse (). | |
| @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance. | |
| @param[in] Service Redfish service instance to perform HTTP DELETE. | |
| @param[in] Uri Target resource URI. | |
| @param[in] Content JSON represented properties to be deleted. This is | |
| optional. | |
| @param[in] ContentSize Size of the Content to be send to Redfish service. | |
| This is optional. When ContentSize is 0, ContentSize | |
| is the size of Content if Content is not NULL. | |
| @param[in] ContentType Type of the Content to be send to Redfish service. | |
| This is optional. When Content is not NULL and | |
| ContentType is NULL, content type HTTP_CONTENT_TYPE_APP_JSON | |
| will be used. | |
| @param[out] Response HTTP response from redfish service. | |
| @retval EFI_SUCCESS Resource is returned successfully. | |
| @retval Others Errors occur. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishDeleteResource ( | |
| IN EDKII_REDFISH_HTTP_PROTOCOL *This, | |
| IN REDFISH_SERVICE Service, | |
| IN EFI_STRING Uri, | |
| IN CHAR8 *Content OPTIONAL, | |
| IN UINTN ContentSize OPTIONAL, | |
| IN CHAR8 *ContentType OPTIONAL, | |
| OUT REDFISH_RESPONSE *Response | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN RetryCount; | |
| REDFISH_REQUEST Request; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Delete URI: %s\n", __func__, Uri)); | |
| Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This); | |
| RetryCount = 0; | |
| ZeroMem (Response, sizeof (REDFISH_RESPONSE)); | |
| ZeroMem (&Request, sizeof (REDFISH_REQUEST)); | |
| Request.Content = Content; | |
| Request.ContentLength = ContentSize; | |
| Request.ContentType = ContentType; | |
| // | |
| // Patch resource to redfish service. | |
| // | |
| do { | |
| RetryCount += 1; | |
| Status = HttpSendReceive ( | |
| Service, | |
| Uri, | |
| HttpMethodDelete, | |
| &Request, | |
| Response | |
| ); | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status)); | |
| if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryDelete)) { | |
| break; | |
| } | |
| // | |
| // Retry when BMC is not ready. | |
| // | |
| if ((Response->StatusCode != NULL)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| if (!RedfishRetryRequired (Response->StatusCode)) { | |
| break; | |
| } | |
| // | |
| // Release response for next round of request. | |
| // | |
| This->FreeResponse (This, Response); | |
| } | |
| DEBUG ((DEBUG_WARN, "%a: RedfishDeleteByUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryDelete)); | |
| if (Private->RetrySetting.RetryWait > 0) { | |
| gBS->Stall (Private->RetrySetting.RetryWait); | |
| } | |
| } while (TRUE); | |
| // | |
| // Redfish resource is updated. Automatically expire the cached response | |
| // so application can directly get resource from Redfish service again. | |
| // | |
| DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri)); | |
| RedfishExpireResponse (This, Uri); | |
| if (EFI_ERROR (Status) || !RedfishSuccessResponse (HttpMethodDelete, Response->StatusCode)) { | |
| DEBUG_CODE ( | |
| DumpRedfishResponse (NULL, DEBUG_ERROR, Response); | |
| ); | |
| // | |
| // Report status code for Redfish failure | |
| // | |
| ReportHttpError (HttpMethodDelete, Uri, Response->StatusCode); | |
| DEBUG ((DEBUG_ERROR, "%a: delete %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryDelete, Status)); | |
| goto ON_RELEASE; | |
| } | |
| ON_RELEASE: | |
| return Status; | |
| } | |
| EDKII_REDFISH_HTTP_PROTOCOL mEdkIIRedfishHttpProtocol = { | |
| EDKII_REDFISH_HTTP_PROTOCOL_REVISION, | |
| RedfishCreateRedfishService, | |
| RedfishFreeRedfishService, | |
| RedfishJsonInRedfishPayload, | |
| RedfishGetResource, | |
| RedfishPatchResource, | |
| RedfishPutResource, | |
| RedfishPostResource, | |
| RedfishDeleteResource, | |
| RedfishFreeRequest, | |
| RedfishFreeResponse, | |
| RedfishExpireResponse | |
| }; | |
| /** | |
| Unloads an image. | |
| @param[in] ImageHandle Handle that identifies the image to be unloaded. | |
| @retval EFI_SUCCESS The image has been unloaded. | |
| @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishHttpDriverUnload ( | |
| IN EFI_HANDLE ImageHandle | |
| ) | |
| { | |
| if (mRedfishHttpCachePrivate == NULL) { | |
| return EFI_SUCCESS; | |
| } | |
| if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) { | |
| ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList); | |
| } | |
| gBS->UninstallMultipleProtocolInterfaces ( | |
| ImageHandle, | |
| &gEdkIIRedfishHttpProtocolGuid, | |
| &mRedfishHttpCachePrivate->Protocol, | |
| NULL | |
| ); | |
| FreePool (mRedfishHttpCachePrivate); | |
| mRedfishHttpCachePrivate = NULL; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This is a EDKII_REDFISH_CREDENTIAL_PROTOCOL notification event handler. | |
| @param[in] Event Event whose notification function is being invoked. | |
| @param[in] Context Pointer to the notification function's context. | |
| **/ | |
| VOID | |
| EFIAPI | |
| CredentialProtocolInstalled ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| REDFISH_HTTP_CACHE_PRIVATE *Private; | |
| Private = (REDFISH_HTTP_CACHE_PRIVATE *)Context; | |
| if (Private->Signature != REDFISH_HTTP_DRIVER_SIGNATURE) { | |
| DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__)); | |
| return; | |
| } | |
| // | |
| // Locate HII credential protocol. | |
| // | |
| Status = gBS->LocateProtocol ( | |
| &gEdkIIRedfishCredential2ProtocolGuid, | |
| NULL, | |
| (VOID **)&Private->CredentialProtocol | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return; | |
| } | |
| gBS->CloseEvent (Event); | |
| } | |
| /** | |
| Main entry for this driver. | |
| @param[in] ImageHandle Image handle this driver. | |
| @param[in] SystemTable Pointer to SystemTable. | |
| @retval EFI_SUCCESS This function always complete successfully. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RedfishHttpEntryPoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VOID *Registration; | |
| if (mRedfishHttpCachePrivate != NULL) { | |
| return EFI_ALREADY_STARTED; | |
| } | |
| mRedfishHttpCachePrivate = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_PRIVATE)); | |
| if (mRedfishHttpCachePrivate == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // Initial cache list and protocol instance. | |
| // | |
| mRedfishHttpCachePrivate->Signature = REDFISH_HTTP_DRIVER_SIGNATURE; | |
| mRedfishHttpCachePrivate->ImageHandle = ImageHandle; | |
| CopyMem (&mRedfishHttpCachePrivate->Protocol, &mEdkIIRedfishHttpProtocol, sizeof (EDKII_REDFISH_HTTP_PROTOCOL)); | |
| mRedfishHttpCachePrivate->CacheList.Capacity = REDFISH_HTTP_CACHE_LIST_SIZE; | |
| mRedfishHttpCachePrivate->CacheList.Count = 0x00; | |
| mRedfishHttpCachePrivate->CacheDisabled = PcdGetBool (PcdHttpCacheDisabled); | |
| InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head); | |
| // | |
| // Get retry settings | |
| // | |
| mRedfishHttpCachePrivate->RetrySetting.MaximumRetryGet = PcdGet16 (PcdHttpGetRetry); | |
| mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPut = PcdGet16 (PcdHttpPutRetry); | |
| mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPatch = PcdGet16 (PcdHttpPatchRetry); | |
| mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPost = PcdGet16 (PcdHttpPostRetry); | |
| mRedfishHttpCachePrivate->RetrySetting.MaximumRetryDelete = PcdGet16 (PcdHttpDeleteRetry); | |
| mRedfishHttpCachePrivate->RetrySetting.RetryWait = PcdGet16 (PcdHttpRetryWaitInSecond) * 1000000U; | |
| // | |
| // Install the gEdkIIRedfishHttpProtocolGuid onto Handle. | |
| // | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &mRedfishHttpCachePrivate->ImageHandle, | |
| &gEdkIIRedfishHttpProtocolGuid, | |
| &mRedfishHttpCachePrivate->Protocol, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: cannot install Redfish http protocol: %r\n", __func__, Status)); | |
| RedfishHttpDriverUnload (ImageHandle); | |
| return Status; | |
| } | |
| // | |
| // Install protocol notification if credential protocol is installed. | |
| // | |
| mRedfishHttpCachePrivate->NotifyEvent = EfiCreateProtocolNotifyEvent ( | |
| &gEdkIIRedfishCredential2ProtocolGuid, | |
| TPL_CALLBACK, | |
| CredentialProtocolInstalled, | |
| mRedfishHttpCachePrivate, | |
| &Registration | |
| ); | |
| if (mRedfishHttpCachePrivate->NotifyEvent == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a: failed to create protocol notification for gEdkIIRedfishCredential2ProtocolGuid\n", __func__)); | |
| ASSERT (FALSE); | |
| RedfishHttpDriverUnload (ImageHandle); | |
| return Status; | |
| } | |
| return EFI_SUCCESS; | |
| } |