diff options
author | Ruiyu Ni <ruiyu.ni@intel.com> | 2015-05-06 04:47:02 +0000 |
---|---|---|
committer | niruiyu <niruiyu@Edk2> | 2015-05-06 04:47:02 +0000 |
commit | 1d1122292572cbaf73d8e8d2d39d01a8a28da76a (patch) | |
tree | 701b29d2a1f1b178a77b45569793b85f595222ca /MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c | |
parent | 58183d391c1efd4488a64412be28ab9bfd25a7db (diff) | |
download | edk2-platforms-1d1122292572cbaf73d8e8d2d39d01a8a28da76a.tar.xz |
MdeModulePkg: Add UefiBootManagerLib
UefiBootManagerLib provides:
load option library functions;
hot key library functions;
boot library functions;
connect and disconnect library functions;
driver health library functions.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17327 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c')
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c new file mode 100644 index 0000000000..49b99957e0 --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c @@ -0,0 +1,748 @@ +/** @file + Library functions which contain all the code to connect console device. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +CHAR16 *mConVarName[] = { + L"ConIn", + L"ConOut", + L"ErrOut", + L"ConInDev", + L"ConOutDev", + L"ErrOutDev" +}; + +/** + Search out the video controller. + + @return PCI device path of the video controller. +**/ +EFI_HANDLE +BmGetVideoController ( + VOID + ) +{ + EFI_STATUS Status; + UINTN RootBridgeHandleCount; + EFI_HANDLE *RootBridgeHandleBuffer; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN RootBridgeIndex; + UINTN Index; + EFI_HANDLE VideoController; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + // + // Make all the PCI_IO protocols show up + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciRootBridgeIoProtocolGuid, + NULL, + &RootBridgeHandleCount, + &RootBridgeHandleBuffer + ); + if (EFI_ERROR (Status) || (RootBridgeHandleCount == 0)) { + return NULL; + } + + VideoController = NULL; + for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) { + gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE); + + // + // Start to check all the pci io to find the first video controller + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + continue; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo); + if (!EFI_ERROR (Status)) { + // + // Check for all video controller + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (!EFI_ERROR (Status) && IS_PCI_VGA (&Pci)) { + // TODO: use IS_PCI_DISPLAY?? + VideoController = HandleBuffer[Index]; + break; + } + } + } + FreePool (HandleBuffer); + + if (VideoController != NULL) { + break; + } + } + FreePool (RootBridgeHandleBuffer); + + return VideoController; +} + +/** + Query all the children of VideoController and return the device paths of all the + children that support GraphicsOutput protocol. + + @param VideoController PCI handle of video controller. + + @return Device paths of all the children that support GraphicsOutput protocol. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiBootManagerGetGopDevicePath ( + IN EFI_HANDLE VideoController + ) +{ + UINTN Index; + EFI_STATUS Status; + EFI_GUID **ProtocolBuffer; + UINTN ProtocolBufferCount; + UINTN ProtocolIndex; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *Next; + EFI_DEVICE_PATH_PROTOCOL *Previous; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EFI_DEVICE_PATH_PROTOCOL *GopPool; + EFI_DEVICE_PATH_PROTOCOL *ReturnDevicePath; + + + Status = gBS->ProtocolsPerHandle ( + VideoController, + &ProtocolBuffer, + &ProtocolBufferCount + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + GopPool = NULL; + + for (ProtocolIndex = 0; ProtocolIndex < ProtocolBufferCount; ProtocolIndex++) { + Status = gBS->OpenProtocolInformation ( + VideoController, + ProtocolBuffer[ProtocolIndex], + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + continue; + } + + for (Index = 0; Index < EntryCount; Index++) { + // + // Query all the children + // + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + continue; + } + + Previous = NULL; + for (Next = DevicePath; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) { + Previous = Next; + } + ASSERT (Previous != NULL); + + if (DevicePathType (Previous) == ACPI_DEVICE_PATH && DevicePathSubType (Previous) == ACPI_ADR_DP) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiGraphicsOutputProtocolGuid, + NULL, + NULL, + NULL, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Append the device path to GOP pool when there is GOP protocol installed. + // + TempDevicePath = GopPool; + GopPool = AppendDevicePathInstance (GopPool, DevicePath); + gBS->FreePool (TempDevicePath); + } + } + + if (DevicePathType (Previous) == HARDWARE_DEVICE_PATH && DevicePathSubType (Previous) == HW_CONTROLLER_DP) { + // + // Recursively look for GOP child in this frame buffer handle + // + DEBUG ((EFI_D_INFO, "[Bds] Looking for GOP child deeper ... \n")); + TempDevicePath = GopPool; + ReturnDevicePath = EfiBootManagerGetGopDevicePath (OpenInfoBuffer[Index].ControllerHandle); + GopPool = AppendDevicePathInstance (GopPool, ReturnDevicePath); + gBS->FreePool (ReturnDevicePath); + gBS->FreePool (TempDevicePath); + } + } + } + + FreePool (OpenInfoBuffer); + } + + FreePool (ProtocolBuffer); + + return GopPool; +} + +/** + Connect the platform active active video controller. + + @param VideoController PCI handle of video controller. + + @retval EFI_NOT_FOUND There is no active video controller. + @retval EFI_SUCCESS The video controller is connected. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectVideoController ( + EFI_HANDLE VideoController OPTIONAL + ) +{ + EFI_DEVICE_PATH_PROTOCOL *Gop; + + if (VideoController == NULL) { + // + // Get the platform vga device + // + VideoController = BmGetVideoController (); + } + + if (VideoController == NULL) { + return EFI_NOT_FOUND; + } + + // + // Try to connect the PCI device path, so that GOP dirver could start on this + // device and create child handles with GraphicsOutput Protocol installed + // on them, then we get device paths of these child handles and select + // them as possible console device. + // + gBS->ConnectController (VideoController, NULL, NULL, FALSE); + + Gop = EfiBootManagerGetGopDevicePath (VideoController); + if (Gop == NULL) { + return EFI_NOT_FOUND; + } + + EfiBootManagerUpdateConsoleVariable (ConOut, Gop, NULL); + FreePool (Gop); + + // + // Necessary for ConPlatform and ConSplitter driver to start up again after ConOut is updated. + // + return gBS->ConnectController (VideoController, NULL, NULL, TRUE); +} + +/** + Fill console handle in System Table if there are no valid console handle in. + + Firstly, check the validation of console handle in System Table. If it is invalid, + update it by the first console device handle from EFI console variable. + + @param VarName The name of the EFI console variable. + @param ConsoleGuid Specified Console protocol GUID. + @param ConsoleHandle On IN, console handle in System Table to be checked. + On OUT, new console handle in system table. + @param ProtocolInterface On IN, console protocol on console handle in System Table to be checked. + On OUT, new console protocol on new console handle in system table. + + @retval TRUE System Table has been updated. + @retval FALSE System Table hasn't been updated. + +**/ +BOOLEAN +BmUpdateSystemTableConsole ( + IN CHAR16 *VarName, + IN EFI_GUID *ConsoleGuid, + IN OUT EFI_HANDLE *ConsoleHandle, + IN OUT VOID **ProtocolInterface + ) +{ + EFI_STATUS Status; + UINTN DevicePathSize; + EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; + EFI_DEVICE_PATH_PROTOCOL *VarConsole; + EFI_DEVICE_PATH_PROTOCOL *Instance; + VOID *Interface; + EFI_HANDLE NewHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + + ASSERT (VarName != NULL); + ASSERT (ConsoleHandle != NULL); + ASSERT (ConsoleGuid != NULL); + ASSERT (ProtocolInterface != NULL); + + if (*ConsoleHandle != NULL) { + Status = gBS->HandleProtocol ( + *ConsoleHandle, + ConsoleGuid, + &Interface + ); + if (Status == EFI_SUCCESS && Interface == *ProtocolInterface) { + // + // If ConsoleHandle is valid and console protocol on this handle also + // also matched, just return. + // + return FALSE; + } + } + + // + // Get all possible consoles device path from EFI variable + // + GetEfiGlobalVariable2 (VarName, (VOID **) &VarConsole, NULL); + if (VarConsole == NULL) { + // + // If there is no any console device, just return. + // + return FALSE; + } + + FullDevicePath = VarConsole; + + do { + // + // Check every instance of the console variable + // + Instance = GetNextDevicePathInstance (&VarConsole, &DevicePathSize); + if (Instance == NULL) { + DEBUG ((EFI_D_ERROR, "[Bds] No valid console instance is found for %s!\n", VarName)); + // We should not ASSERT when all the console devices are removed. + // ASSERT_EFI_ERROR (EFI_NOT_FOUND); + FreePool (FullDevicePath); + return FALSE; + } + + // + // Find console device handle by device path instance + // + Status = gBS->LocateDevicePath ( + ConsoleGuid, + &Instance, + &NewHandle + ); + if (!EFI_ERROR (Status)) { + // + // Get the console protocol on this console device handle + // + Status = gBS->HandleProtocol ( + NewHandle, + ConsoleGuid, + &Interface + ); + if (!EFI_ERROR (Status)) { + // + // Update new console handle in System Table. + // + *ConsoleHandle = NewHandle; + *ProtocolInterface = Interface; + if (CompareGuid (ConsoleGuid, &gEfiSimpleTextOutProtocolGuid)) { + // + // If it is console out device, set console mode 80x25 if current mode is invalid. + // + TextOut = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *) Interface; + if (TextOut->Mode->Mode == -1) { + TextOut->SetMode (TextOut, 0); + } + } + return TRUE; + } + } + + } while (Instance != NULL); + + // + // No any available console devcie found. + // + return FALSE; +} + +/** + This function updates the console variable based on ConVarName. It can + add or remove one specific console device path from the variable + + @param ConsoleType ConIn, ConOut, ErrOut, ConInDev, ConOutDev or ErrOutDev. + @param CustomizedConDevicePath The console device path to be added to + the console variable. Cannot be multi-instance. + @param ExclusiveDevicePath The console device path to be removed + from the console variable. Cannot be multi-instance. + + @retval EFI_UNSUPPORTED The added device path is the same as a removed one. + @retval EFI_SUCCESS Successfully added or removed the device path from the + console variable. + @retval others Return status of RT->SetVariable(). + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerUpdateConsoleVariable ( + IN CONSOLE_TYPE ConsoleType, + IN EFI_DEVICE_PATH_PROTOCOL *CustomizedConDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *ExclusiveDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *VarConsole; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; + + if (ConsoleType >= sizeof (mConVarName) / sizeof (mConVarName[0])) { + return EFI_INVALID_PARAMETER; + } + + // + // Notes: check the device path point, here should check + // with compare memory + // + if (CustomizedConDevicePath == ExclusiveDevicePath) { + return EFI_UNSUPPORTED; + } + // + // Delete the ExclusiveDevicePath from current default console + // + GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &VarConsole, NULL); + // + // Initialize NewDevicePath + // + NewDevicePath = VarConsole; + + // + // If ExclusiveDevicePath is even the part of the instance in VarConsole, delete it. + // In the end, NewDevicePath is the final device path. + // + if (ExclusiveDevicePath != NULL && VarConsole != NULL) { + NewDevicePath = BmDelPartMatchInstance (VarConsole, ExclusiveDevicePath); + } + // + // Try to append customized device path to NewDevicePath. + // + if (CustomizedConDevicePath != NULL) { + if (!BmMatchDevicePaths (NewDevicePath, CustomizedConDevicePath)) { + // + // Check if there is part of CustomizedConDevicePath in NewDevicePath, delete it. + // + NewDevicePath = BmDelPartMatchInstance (NewDevicePath, CustomizedConDevicePath); + // + // In the first check, the default console variable will be _ModuleEntryPoint, + // just append current customized device path + // + TempNewDevicePath = NewDevicePath; + NewDevicePath = AppendDevicePathInstance (NewDevicePath, CustomizedConDevicePath); + if (TempNewDevicePath != NULL) { + FreePool(TempNewDevicePath); + } + } + } + + // + // Finally, Update the variable of the default console by NewDevicePath + // + Status = gRT->SetVariable ( + mConVarName[ConsoleType], + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + | ((ConsoleType < ConInDev) ? EFI_VARIABLE_NON_VOLATILE : 0), + GetDevicePathSize (NewDevicePath), + NewDevicePath + ); + + if (VarConsole == NewDevicePath) { + if (VarConsole != NULL) { + FreePool(VarConsole); + } + } else { + if (VarConsole != NULL) { + FreePool(VarConsole); + } + if (NewDevicePath != NULL) { + FreePool(NewDevicePath); + } + } + + return Status; +} + + +/** + Connect the console device base on the variable ConsoleType. + + @param ConsoleType ConIn, ConOut or ErrOut. + + @retval EFI_NOT_FOUND There is not any console devices connected + success + @retval EFI_SUCCESS Success connect any one instance of the console + device path base on the variable ConVarName. + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectConsoleVariable ( + IN CONSOLE_TYPE ConsoleType + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *StartDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Instance; + EFI_DEVICE_PATH_PROTOCOL *Next; + EFI_DEVICE_PATH_PROTOCOL *CopyOfDevicePath; + UINTN Size; + BOOLEAN DeviceExist; + EFI_HANDLE Handle; + + if ((ConsoleType != ConIn) && (ConsoleType != ConOut) && (ConsoleType != ErrOut)) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + DeviceExist = FALSE; + Handle = NULL; + + // + // Check if the console variable exist + // + GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &StartDevicePath, NULL); + if (StartDevicePath == NULL) { + return EFI_UNSUPPORTED; + } + + CopyOfDevicePath = StartDevicePath; + do { + // + // Check every instance of the console variable + // + Instance = GetNextDevicePathInstance (&CopyOfDevicePath, &Size); + if (Instance == NULL) { + FreePool (StartDevicePath); + return EFI_UNSUPPORTED; + } + + Next = Instance; + while (!IsDevicePathEndType (Next)) { + Next = NextDevicePathNode (Next); + } + + SetDevicePathEndNode (Next); + // + // Connect the USB console + // USB console device path is a short-form device path that + // starts with the first element being a USB WWID + // or a USB Class device path + // + if ((DevicePathType (Instance) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (Instance) == MSG_USB_CLASS_DP) || (DevicePathSubType (Instance) == MSG_USB_WWID_DP)) + ) { + Status = BmConnectUsbShortFormDevicePath (Instance); + if (!EFI_ERROR (Status)) { + DeviceExist = TRUE; + } + } else { + for (Next = Instance; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) { + if (DevicePathType (Next) == ACPI_DEVICE_PATH && DevicePathSubType (Next) == ACPI_ADR_DP) { + break; + } else if (DevicePathType (Next) == HARDWARE_DEVICE_PATH && + DevicePathSubType (Next) == HW_CONTROLLER_DP && + DevicePathType (NextDevicePathNode (Next)) == ACPI_DEVICE_PATH && + DevicePathSubType (NextDevicePathNode (Next)) == ACPI_ADR_DP + ) { + break; + } + } + if (!IsDevicePathEnd (Next)) { + // + // For GOP device path, start the video driver with NULL remaining device path + // + SetDevicePathEndNode (Next); + Status = EfiBootManagerConnectDevicePath (Instance, &Handle); + if (!EFI_ERROR (Status)) { + gBS->ConnectController (Handle, NULL, NULL, TRUE); + } + } else { + Status = EfiBootManagerConnectDevicePath (Instance, NULL); + } + if (EFI_ERROR (Status)) { + // + // Delete the instance from the console varialbe + // + EfiBootManagerUpdateConsoleVariable (ConsoleType, NULL, Instance); + } else { + DeviceExist = TRUE; + } + } + FreePool(Instance); + } while (CopyOfDevicePath != NULL); + + FreePool (StartDevicePath); + + if (!DeviceExist) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + + +/** + This function will search every input/output device in current system, + and make every input/output device as potential console device. +**/ +VOID +EFIAPI +EfiBootManagerConnectAllConsoles ( + VOID + ) +{ + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *ConDevicePath; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + + Index = 0; + HandleCount = 0; + HandleBuffer = NULL; + ConDevicePath = NULL; + + // + // Update all the console variables + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextInProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + for (Index = 0; Index < HandleCount; Index++) { + gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + (VOID **) &ConDevicePath + ); + EfiBootManagerUpdateConsoleVariable (ConIn, ConDevicePath, NULL); + } + + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + HandleBuffer = NULL; + } + + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextOutProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + for (Index = 0; Index < HandleCount; Index++) { + gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + (VOID **) &ConDevicePath + ); + EfiBootManagerUpdateConsoleVariable (ConOut, ConDevicePath, NULL); + EfiBootManagerUpdateConsoleVariable (ErrOut, ConDevicePath, NULL); + } + + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + } + + // + // Connect all console variables + // + EfiBootManagerConnectAllDefaultConsoles (); +} + + +/** + This function will connect all the console devices base on the console + device variable ConIn, ConOut and ErrOut. +**/ +VOID +EFIAPI +EfiBootManagerConnectAllDefaultConsoles ( + VOID + ) +{ + BOOLEAN SystemTableUpdated; + + EfiBootManagerConnectConsoleVariable (ConOut); + PERF_START (NULL, "ConOutReady", "BDS", 1); + PERF_END (NULL, "ConOutReady", "BDS", 0); + + + EfiBootManagerConnectConsoleVariable (ConIn); + PERF_START (NULL, "ConInReady", "BDS", 1); + PERF_END (NULL, "ConInReady", "BDS", 0); + + // + // The _ModuleEntryPoint err out var is legal. + // + EfiBootManagerConnectConsoleVariable (ErrOut); + PERF_START (NULL, "ErrOutReady", "BDS", 1); + PERF_END (NULL, "ErrOutReady", "BDS", 0); + + SystemTableUpdated = FALSE; + // + // Fill console handles in System Table if no console device assignd. + // + if (BmUpdateSystemTableConsole (L"ConIn", &gEfiSimpleTextInProtocolGuid, &gST->ConsoleInHandle, (VOID **) &gST->ConIn)) { + SystemTableUpdated = TRUE; + } + if (BmUpdateSystemTableConsole (L"ConOut", &gEfiSimpleTextOutProtocolGuid, &gST->ConsoleOutHandle, (VOID **) &gST->ConOut)) { + SystemTableUpdated = TRUE; + } + if (BmUpdateSystemTableConsole (L"ErrOut", &gEfiSimpleTextOutProtocolGuid, &gST->StandardErrorHandle, (VOID **) &gST->StdErr)) { + SystemTableUpdated = TRUE; + } + + if (SystemTableUpdated) { + // + // Update the CRC32 in the EFI System Table header + // + gST->Hdr.CRC32 = 0; + gBS->CalculateCrc32 ( + (UINT8 *) &gST->Hdr, + gST->Hdr.HeaderSize, + &gST->Hdr.CRC32 + ); + } +} |