/** @file HII Config Access protocol implementation of RamDiskDxe driver. Copyright (c) 2016, Intel Corporation. All rights reserved.
(C) Copyright 2016-2018 Hewlett Packard Enterprise Development LP
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "RamDiskImpl.h" CHAR16 mRamDiskStorageName[] = L"RAM_DISK_CONFIGURATION"; RAM_DISK_CONFIG_PRIVATE_DATA mRamDiskConfigPrivateDataTemplate = { RAM_DISK_CONFIG_PRIVATE_DATA_SIGNATURE, { EFI_PAGE_SIZE, RAM_DISK_BOOT_SERVICE_DATA_MEMORY }, { RamDiskExtractConfig, RamDiskRouteConfig, RamDiskCallback } }; HII_VENDOR_DEVICE_PATH mRamDiskHiiVendorDevicePath = { { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { (UINT8)(sizeof (VENDOR_DEVICE_PATH)), (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8) } }, RAM_DISK_FORM_SET_GUID }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { (UINT8)(END_DEVICE_PATH_LENGTH), (UINT8)((END_DEVICE_PATH_LENGTH) >> 8) } } }; /** This function publish the RAM disk configuration Form. @param[in, out] ConfigPrivateData Points to RAM disk configuration private data. @retval EFI_SUCCESS HII Form is installed successfully. @retval EFI_OUT_OF_RESOURCES Not enough resource for HII Form installation. @retval Others Other errors as indicated. **/ EFI_STATUS InstallRamDiskConfigForm ( IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivateData ) { EFI_STATUS Status; EFI_HII_HANDLE HiiHandle; EFI_HANDLE DriverHandle; EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; DriverHandle = NULL; ConfigAccess = &ConfigPrivateData->ConfigAccess; Status = gBS->InstallMultipleProtocolInterfaces ( &DriverHandle, &gEfiDevicePathProtocolGuid, &mRamDiskHiiVendorDevicePath, &gEfiHiiConfigAccessProtocolGuid, ConfigAccess, NULL ); if (EFI_ERROR (Status)) { return Status; } ConfigPrivateData->DriverHandle = DriverHandle; // // Publish the HII package list // HiiHandle = HiiAddPackages ( &gRamDiskFormSetGuid, DriverHandle, RamDiskDxeStrings, RamDiskHiiBin, NULL ); if (HiiHandle == NULL) { gBS->UninstallMultipleProtocolInterfaces ( DriverHandle, &gEfiDevicePathProtocolGuid, &mRamDiskHiiVendorDevicePath, &gEfiHiiConfigAccessProtocolGuid, ConfigAccess, NULL ); return EFI_OUT_OF_RESOURCES; } ConfigPrivateData->HiiHandle = HiiHandle; return EFI_SUCCESS; } /** This function removes RAM disk configuration Form. @param[in, out] ConfigPrivateData Points to RAM disk configuration private data. **/ VOID UninstallRamDiskConfigForm ( IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivateData ) { // // Uninstall HII package list // if (ConfigPrivateData->HiiHandle != NULL) { HiiRemovePackages (ConfigPrivateData->HiiHandle); ConfigPrivateData->HiiHandle = NULL; } // // Uninstall HII Config Access Protocol // if (ConfigPrivateData->DriverHandle != NULL) { gBS->UninstallMultipleProtocolInterfaces ( ConfigPrivateData->DriverHandle, &gEfiDevicePathProtocolGuid, &mRamDiskHiiVendorDevicePath, &gEfiHiiConfigAccessProtocolGuid, &ConfigPrivateData->ConfigAccess, NULL ); ConfigPrivateData->DriverHandle = NULL; } FreePool (ConfigPrivateData); } /** Unregister all registered RAM disks. **/ VOID UnregisterAllRamDisks ( VOID ) { LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; RAM_DISK_PRIVATE_DATA *PrivateData; if (!IsListEmpty (&RegisteredRamDisks)) { BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &RegisteredRamDisks) { PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); gBS->UninstallMultipleProtocolInterfaces ( PrivateData->Handle, &gEfiBlockIoProtocolGuid, &PrivateData->BlockIo, &gEfiBlockIo2ProtocolGuid, &PrivateData->BlockIo2, &gEfiDevicePathProtocolGuid, (EFI_DEVICE_PATH_PROTOCOL *)PrivateData->DevicePath, NULL ); RemoveEntryList (&PrivateData->ThisInstance); if (RamDiskCreateHii == PrivateData->CreateMethod) { // // If a RAM disk is created within HII, then the RamDiskDxe driver // driver is responsible for freeing the allocated memory for the // RAM disk. // FreePool ((VOID *)(UINTN)PrivateData->StartingAddr); } FreePool (PrivateData->DevicePath); FreePool (PrivateData); } } } /** This function allows a caller to extract the current configuration for one or more named elements from the target driver. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Request A null-terminated Unicode string in format. @param[out] Progress On return, points to a character in the Request string. Points to the string's null terminator if request was successful. Points to the most recent '&' before the first failing name/value pair (or the beginning of the string if the failure is in the first name/value pair) if the request was not successful. @param[out] Results A null-terminated Unicode string in format which has all values filled in for the names in the Request string. String to be allocated by the called function. @retval EFI_SUCCESS The Results is filled with the requested values. @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. **/ EFI_STATUS EFIAPI RamDiskExtractConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Request, OUT EFI_STRING *Progress, OUT EFI_STRING *Results ) { if ((Progress == NULL) || (Results == NULL)) { return EFI_INVALID_PARAMETER; } *Progress = Request; return EFI_NOT_FOUND; } /** This function processes the results of changes in configuration. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Configuration A null-terminated Unicode string in format. @param[out] Progress A pointer to a string filled in with the offset of the most recent '&' before the first failing name/value pair (or the beginning of the string if the failure is in the first name/value pair) or the terminating NULL if all was successful. @retval EFI_SUCCESS The Results is processed successfully. @retval EFI_INVALID_PARAMETER Configuration is NULL. @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. **/ EFI_STATUS EFIAPI RamDiskRouteConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Configuration, OUT EFI_STRING *Progress ) { if ((Configuration == NULL) || (Progress == NULL)) { return EFI_INVALID_PARAMETER; } *Progress = Configuration; return EFI_NOT_FOUND; } /** Allocate memory and register the RAM disk created within RamDiskDxe driver HII. @param[in] Size If creating raw, size of the RAM disk to create. If creating from file, zero. @param[in] FileHandle If creating raw, NULL. If creating from file, the file handle. @param[in] MemoryType Type of memory to be used to create RAM Disk. @retval EFI_SUCCESS RAM disk is created and registered. @retval EFI_OUT_OF_RESOURCES Not enough storage is available to match the size required. **/ EFI_STATUS HiiCreateRamDisk ( IN UINT64 Size, IN EFI_FILE_HANDLE FileHandle, IN UINT8 MemoryType ) { EFI_STATUS Status; UINTN BufferSize; UINT64 *StartingAddr; EFI_INPUT_KEY Key; EFI_DEVICE_PATH_PROTOCOL *DevicePath; RAM_DISK_PRIVATE_DATA *PrivateData; EFI_FILE_INFO *FileInformation; FileInformation = NULL; StartingAddr = NULL; if (FileHandle != NULL) { // // Create from file. // FileInformation = FileInfo (FileHandle); if (NULL == FileInformation) { do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", L"Not enough memory to get the file information!", L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); return EFI_OUT_OF_RESOURCES; } // // Update the size of RAM disk according to the file size. // Size = FileInformation->FileSize; } if (Size > (UINTN)-1) { do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", L"The given RAM disk size is too large!", L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); return EFI_OUT_OF_RESOURCES; } if (MemoryType == RAM_DISK_BOOT_SERVICE_DATA_MEMORY) { Status = gBS->AllocatePool ( EfiBootServicesData, (UINTN)Size, (VOID **)&StartingAddr ); } else if (MemoryType == RAM_DISK_RESERVED_MEMORY) { Status = gBS->AllocatePool ( EfiReservedMemoryType, (UINTN)Size, (VOID **)&StartingAddr ); } else { Status = EFI_INVALID_PARAMETER; } if ((StartingAddr == NULL) || EFI_ERROR (Status)) { do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", L"Not enough memory to create the RAM disk!", L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); return EFI_OUT_OF_RESOURCES; } if (FileHandle != NULL) { // // Copy the file content to the RAM disk. // BufferSize = (UINTN)Size; FileHandle->Read ( FileHandle, &BufferSize, (VOID *)(UINTN)StartingAddr ); if (BufferSize != FileInformation->FileSize) { do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", L"File content read error!", L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); return EFI_DEVICE_ERROR; } } // // Register the newly created RAM disk. // Status = RamDiskRegister ( ((UINT64)(UINTN)StartingAddr), Size, &gEfiVirtualDiskGuid, NULL, &DevicePath ); if (EFI_ERROR (Status)) { do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", L"Fail to register the newly created RAM disk!", L"Press ENTER to continue ...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); return Status; } // // If RAM disk is created within HII, memory should be freed when the // RAM disk is unregisterd. // PrivateData = RAM_DISK_PRIVATE_FROM_THIS (RegisteredRamDisks.BackLink); PrivateData->CreateMethod = RamDiskCreateHii; return EFI_SUCCESS; } /** This function updates the registered RAM disks list on the main form. @param[in, out] ConfigPrivate Private data for configurating hii data for RAM disks. **/ VOID UpdateMainForm ( IN OUT RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivate ) { VOID *StartOpCodeHandle; VOID *EndOpCodeHandle; EFI_IFR_GUID_LABEL *StartLabel; EFI_IFR_GUID_LABEL *EndLabel; LIST_ENTRY *Entry; UINTN Index; RAM_DISK_PRIVATE_DATA *PrivateData; CHAR16 *String; CHAR16 RamDiskStr[128]; EFI_STRING_ID StringId; // // Init OpCode Handle // StartOpCodeHandle = HiiAllocateOpCodeHandle (); ASSERT (StartOpCodeHandle != NULL); EndOpCodeHandle = HiiAllocateOpCodeHandle (); ASSERT (EndOpCodeHandle != NULL); // // Create Hii Extend Label OpCode as the start opcode // StartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL) ); StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; StartLabel->Number = MAIN_LABEL_LIST_START; // // Create Hii Extend Label OpCode as the end opcode // EndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL) ); EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; EndLabel->Number = MAIN_LABEL_LIST_END; Index = 0; BASE_LIST_FOR_EACH (Entry, &RegisteredRamDisks) { PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); PrivateData->CheckBoxId = (EFI_QUESTION_ID) (MAIN_CHECKBOX_QUESTION_ID_START + Index); // // CheckBox is unchecked by default. // PrivateData->CheckBoxChecked = FALSE; String = RamDiskStr; UnicodeSPrint ( String, sizeof (RamDiskStr), L" RAM Disk %d: [0x%lx, 0x%lx]\n", Index, PrivateData->StartingAddr, PrivateData->StartingAddr + PrivateData->Size - 1 ); StringId = HiiSetString (ConfigPrivate->HiiHandle, 0, RamDiskStr, NULL); ASSERT (StringId != 0); HiiCreateCheckBoxOpCode ( StartOpCodeHandle, PrivateData->CheckBoxId, 0, 0, StringId, STRING_TOKEN (STR_RAM_DISK_LIST_HELP), EFI_IFR_FLAG_CALLBACK, 0, NULL ); Index++; } HiiUpdateForm ( ConfigPrivate->HiiHandle, &gRamDiskFormSetGuid, MAIN_FORM_ID, StartOpCodeHandle, EndOpCodeHandle ); HiiFreeOpCodeHandle (StartOpCodeHandle); HiiFreeOpCodeHandle (EndOpCodeHandle); } /** This function processes the results of changes in configuration. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Action Specifies the type of action taken by the browser. @param[in] QuestionId A unique value which is sent to the original exporting driver so that it can identify the type of data to expect. @param[in] Type The type of value for the question. @param[in] Value A pointer to the data being sent to the original exporting driver. @param[out] ActionRequest On return, points to the action requested by the callback function. @retval EFI_SUCCESS The callback successfully handled the action. @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. @retval EFI_DEVICE_ERROR The variable could not be saved. @retval EFI_UNSUPPORTED The specified Action is not supported by the callback. **/ EFI_STATUS EFIAPI RamDiskCallback ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN EFI_BROWSER_ACTION Action, IN EFI_QUESTION_ID QuestionId, IN UINT8 Type, IN EFI_IFR_TYPE_VALUE *Value, OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest ) { EFI_STATUS Status; RAM_DISK_PRIVATE_DATA *PrivateData; RAM_DISK_CONFIG_PRIVATE_DATA *ConfigPrivate; EFI_DEVICE_PATH_PROTOCOL *FileDevPath; EFI_FILE_HANDLE FileHandle; LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; if ((This == NULL) || (Value == NULL) || (ActionRequest == NULL)) { return EFI_INVALID_PARAMETER; } ConfigPrivate = RAM_DISK_CONFIG_PRIVATE_FROM_THIS (This); if (Action == EFI_BROWSER_ACTION_RETRIEVE) { Status = EFI_UNSUPPORTED; if (QuestionId == CREATE_RAW_SIZE_QUESTION_ID) { Value->u64 = EFI_PAGE_SIZE; ConfigPrivate->ConfigStore.Size = EFI_PAGE_SIZE; Status = EFI_SUCCESS; } else if (QuestionId == CREATE_RAW_MEMORY_TYPE_QUESTION_ID) { Value->u8 = RAM_DISK_BOOT_SERVICE_DATA_MEMORY; ConfigPrivate->ConfigStore.MemType = RAM_DISK_BOOT_SERVICE_DATA_MEMORY; Status = EFI_SUCCESS; } return Status; } if ((Action != EFI_BROWSER_ACTION_CHANGED) && (Action != EFI_BROWSER_ACTION_CHANGING) && (Action != EFI_BROWSER_ACTION_FORM_OPEN)) { return EFI_UNSUPPORTED; } // // Update the RAM disk list show at the main form first. // if (Action == EFI_BROWSER_ACTION_FORM_OPEN) { Status = EFI_UNSUPPORTED; if (QuestionId == MAIN_GOTO_FILE_EXPLORER_ID) { UpdateMainForm (ConfigPrivate); Status = EFI_SUCCESS; } return Status; } Status = EFI_SUCCESS; if (Action == EFI_BROWSER_ACTION_CHANGING) { switch (QuestionId) { case MAIN_GOTO_FILE_EXPLORER_ID: Status = ChooseFile (NULL, NULL, NULL, &FileDevPath); if (EFI_ERROR (Status)) { break; } if (FileDevPath != NULL) { // // Open the file. // Status = EfiOpenFileByDevicePath ( &FileDevPath, &FileHandle, EFI_FILE_MODE_READ, 0 ); if (EFI_ERROR (Status)) { break; } // // Create from file, RAM disk size is zero. It will be updated // according to the file size. // Status = HiiCreateRamDisk ( 0, FileHandle, ConfigPrivate->ConfigStore.MemType ); if (EFI_ERROR (Status)) { break; } // // Refresh the registered RAM disks list. // UpdateMainForm (ConfigPrivate); } break; default: break; } } else if (Action == EFI_BROWSER_ACTION_CHANGED) { switch (QuestionId) { case MAIN_REMOVE_RD_QUESTION_ID: // // Remove the selected RAM disks // BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &RegisteredRamDisks) { PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); if (PrivateData->CheckBoxChecked) { RamDiskUnregister ( (EFI_DEVICE_PATH_PROTOCOL *)PrivateData->DevicePath ); } } UpdateMainForm (ConfigPrivate); *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; break; case CREATE_RAW_SIZE_QUESTION_ID: ConfigPrivate->ConfigStore.Size = Value->u64; break; case CREATE_RAW_MEMORY_TYPE_QUESTION_ID: ConfigPrivate->ConfigStore.MemType = Value->u8; break; case CREATE_RAW_SUBMIT_QUESTION_ID: // // Create raw, FileHandle is NULL. // Status = HiiCreateRamDisk ( ConfigPrivate->ConfigStore.Size, NULL, ConfigPrivate->ConfigStore.MemType ); if (EFI_ERROR (Status)) { break; } // // Refresh the registered RAM disks list. // UpdateMainForm (ConfigPrivate); *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; break; case CREATE_RAW_DISCARD_QUESTION_ID: *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; break; default: // // QuestionIds for checkboxes // if ((QuestionId >= MAIN_CHECKBOX_QUESTION_ID_START) && (QuestionId < CREATE_RAW_RAM_DISK_FORM_ID)) { BASE_LIST_FOR_EACH (Entry, &RegisteredRamDisks) { PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry); if (PrivateData->CheckBoxId == QuestionId) { PrivateData->CheckBoxChecked = (BOOLEAN)(Value->u8 != 0); } } } break; } } return EFI_SUCCESS; }