BMP files by tools other than MS paint can have a variable number of padding bytes, which results in the DataSize being less than (ImageSize - HeaderSize). Fix the check to be less stringent. Test: use BMP created by/saved by Photoshop Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
		
			
				
	
	
		
			576 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			576 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   Provides services to convert a BMP graphics image to a GOP BLT buffer and
 | |
|   from a GOP BLT buffer to a BMP graphics image.
 | |
| 
 | |
|   Caution: This module requires additional review when modified.
 | |
|   This module processes external input - BMP image.
 | |
|   This external input must be validated carefully to avoid security issue such
 | |
|   as buffer overflow, integer overflow.
 | |
| 
 | |
|   TranslateBmpToGopBlt() receives untrusted input and performs basic validation.
 | |
| 
 | |
|   Copyright (c) 2016-2017, Microsoft Corporation
 | |
|   Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   All rights reserved.
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include <PiDxe.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| #include <Library/MemoryAllocationLib.h>
 | |
| #include <Library/SafeIntLib.h>
 | |
| #include <IndustryStandard/Bmp.h>
 | |
| 
 | |
| #include <Library/BmpSupportLib.h>
 | |
| 
 | |
| //
 | |
| // BMP Image header for an uncompressed 24-bit per pixel BMP image.
 | |
| //
 | |
| const BMP_IMAGE_HEADER  mBmpImageHeaderTemplate = {
 | |
|   'B',    // CharB
 | |
|   'M',    // CharM
 | |
|   0,      // Size will be updated at runtime
 | |
|   {0, 0}, // Reserved
 | |
|   sizeof (BMP_IMAGE_HEADER), // ImageOffset
 | |
|   sizeof (BMP_IMAGE_HEADER) - OFFSET_OF (BMP_IMAGE_HEADER, HeaderSize), // HeaderSize
 | |
|   0,      // PixelWidth will be updated at runtime
 | |
|   0,      // PixelHeight will be updated at runtime
 | |
|   1,      // Planes
 | |
|   24,     // BitPerPixel
 | |
|   0,      // CompressionType
 | |
|   0,      // ImageSize will be updated at runtime
 | |
|   0,      // XPixelsPerMeter
 | |
|   0,      // YPixelsPerMeter
 | |
|   0,      // NumberOfColors
 | |
|   0       // ImportantColors
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Translate a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer
 | |
|   is passed in a GopBlt buffer will be allocated by this routine using
 | |
|   EFI_BOOT_SERVICES.AllocatePool(). If a GopBlt buffer is passed in it will be
 | |
|   used if it is big enough.
 | |
| 
 | |
|   @param[in]       BmpImage      Pointer to BMP file.
 | |
|   @param[in]       BmpImageSize  Number of bytes in BmpImage.
 | |
|   @param[in, out]  GopBlt        Buffer containing GOP version of BmpImage.
 | |
|   @param[in, out]  GopBltSize    Size of GopBlt in bytes.
 | |
|   @param[out]      PixelHeight   Height of GopBlt/BmpImage in pixels.
 | |
|   @param[out]      PixelWidth    Width of GopBlt/BmpImage in pixels.
 | |
| 
 | |
|   @retval  RETURN_SUCCESS            GopBlt and GopBltSize are returned.
 | |
|   @retval  RETURN_INVALID_PARAMETER  BmpImage is NULL.
 | |
|   @retval  RETURN_INVALID_PARAMETER  GopBlt is NULL.
 | |
|   @retval  RETURN_INVALID_PARAMETER  GopBltSize is NULL.
 | |
|   @retval  RETURN_INVALID_PARAMETER  PixelHeight is NULL.
 | |
|   @retval  RETURN_INVALID_PARAMETER  PixelWidth is NULL.
 | |
|   @retval  RETURN_UNSUPPORTED        BmpImage is not a valid *.BMP image.
 | |
|   @retval  RETURN_BUFFER_TOO_SMALL   The passed in GopBlt buffer is not big
 | |
|                                      enough.  The required size is returned in
 | |
|                                      GopBltSize.
 | |
|   @retval  RETURN_OUT_OF_RESOURCES   The GopBlt buffer could not be allocated.
 | |
| 
 | |
| **/
 | |
| RETURN_STATUS
 | |
| EFIAPI
 | |
| TranslateBmpToGopBlt (
 | |
|   IN     VOID                           *BmpImage,
 | |
|   IN     UINTN                          BmpImageSize,
 | |
|   IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL  **GopBlt,
 | |
|   IN OUT UINTN                          *GopBltSize,
 | |
|   OUT    UINTN                          *PixelHeight,
 | |
|   OUT    UINTN                          *PixelWidth
 | |
|   )
 | |
| {
 | |
|   UINT8                          *Image;
 | |
|   UINT8                          *ImageHeader;
 | |
|   BMP_IMAGE_HEADER               *BmpHeader;
 | |
|   BMP_COLOR_MAP                  *BmpColorMap;
 | |
|   EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BltBuffer;
 | |
|   EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *Blt;
 | |
|   UINT32                         BltBufferSize;
 | |
|   UINTN                          Index;
 | |
|   UINTN                          Height;
 | |
|   UINTN                          Width;
 | |
|   UINTN                          ImageIndex;
 | |
|   UINT32                         DataSizePerLine;
 | |
|   BOOLEAN                        IsAllocated;
 | |
|   UINT32                         ColorMapNum;
 | |
|   RETURN_STATUS                  Status;
 | |
|   UINT32                         DataSize;
 | |
|   UINT32                         Temp;
 | |
| 
 | |
|   if (BmpImage == NULL || GopBlt == NULL || GopBltSize == NULL) {
 | |
|     return RETURN_INVALID_PARAMETER;
 | |
|   }
 | |
|   if (PixelHeight == NULL || PixelWidth == NULL) {
 | |
|     return RETURN_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (BmpImageSize < sizeof (BMP_IMAGE_HEADER)) {
 | |
|     DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: BmpImageSize too small\n"));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   BmpHeader = (BMP_IMAGE_HEADER *)BmpImage;
 | |
| 
 | |
|   if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') {
 | |
|     DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: BmpHeader->Char fields incorrect\n"));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Doesn't support compress.
 | |
|   //
 | |
|   if (BmpHeader->CompressionType != 0) {
 | |
|     DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: Compression Type unsupported.\n"));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   if ((BmpHeader->PixelHeight == 0) || (BmpHeader->PixelWidth == 0)) {
 | |
|     DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: BmpHeader->PixelHeight or BmpHeader->PixelWidth is 0.\n"));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Only support BITMAPINFOHEADER format.
 | |
|   // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER
 | |
|   //
 | |
|   if (BmpHeader->HeaderSize != sizeof (BMP_IMAGE_HEADER) - OFFSET_OF (BMP_IMAGE_HEADER, HeaderSize)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateBmpToGopBlt: BmpHeader->Headership is not as expected.  Headersize is 0x%x\n",
 | |
|       BmpHeader->HeaderSize
 | |
|       ));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // The data size in each line must be 4 byte alignment.
 | |
|   //
 | |
|   Status = SafeUint32Mult (
 | |
|              BmpHeader->PixelWidth,
 | |
|              BmpHeader->BitPerPixel,
 | |
|              &DataSizePerLine
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateBmpToGopBlt: invalid BmpImage... PixelWidth:0x%x BitPerPixel:0x%x\n",
 | |
|       BmpHeader->PixelWidth,
 | |
|       BmpHeader->BitPerPixel
 | |
|       ));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   Status = SafeUint32Add (DataSizePerLine, 31, &DataSizePerLine);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateBmpToGopBlt: invalid BmpImage... DataSizePerLine:0x%x\n",
 | |
|       DataSizePerLine
 | |
|       ));
 | |
| 
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   DataSizePerLine = (DataSizePerLine >> 3) &(~0x3);
 | |
|   Status = SafeUint32Mult (
 | |
|              DataSizePerLine,
 | |
|              BmpHeader->PixelHeight,
 | |
|              &BltBufferSize
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateBmpToGopBlt: invalid BmpImage... DataSizePerLine:0x%x PixelHeight:0x%x\n",
 | |
|       DataSizePerLine, BmpHeader->PixelHeight
 | |
|       ));
 | |
| 
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   Status = SafeUint32Mult (
 | |
|              BmpHeader->PixelHeight,
 | |
|              DataSizePerLine,
 | |
|              &DataSize
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateBmpToGopBlt: invalid BmpImage... PixelHeight:0x%x DataSizePerLine:0x%x\n",
 | |
|       BmpHeader->PixelHeight, DataSizePerLine
 | |
|       ));
 | |
| 
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   if ((BmpHeader->Size != BmpImageSize) ||
 | |
|       (BmpHeader->Size < BmpHeader->ImageOffset) ||
 | |
|       (BmpHeader->Size - BmpHeader->ImageOffset < DataSize)) {
 | |
| 
 | |
|     DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: invalid BmpImage... \n"));
 | |
|     DEBUG ((DEBUG_ERROR, "   BmpHeader->Size: 0x%x\n", BmpHeader->Size));
 | |
|     DEBUG ((DEBUG_ERROR, "   BmpHeader->ImageOffset: 0x%x\n", BmpHeader->ImageOffset));
 | |
|     DEBUG ((DEBUG_ERROR, "   BmpImageSize: 0x%lx\n", (UINTN)BmpImageSize));
 | |
|     DEBUG ((DEBUG_ERROR, "   DataSize: 0x%lx\n", (UINTN)DataSize));
 | |
| 
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Calculate Color Map offset in the image.
 | |
|   //
 | |
|   Image = BmpImage;
 | |
|   BmpColorMap = (BMP_COLOR_MAP *)(Image + sizeof (BMP_IMAGE_HEADER));
 | |
|   if (BmpHeader->ImageOffset < sizeof (BMP_IMAGE_HEADER)) {
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   if (BmpHeader->ImageOffset > sizeof (BMP_IMAGE_HEADER)) {
 | |
|     switch (BmpHeader->BitPerPixel) {
 | |
|     case 1:
 | |
|       ColorMapNum = 2;
 | |
|       break;
 | |
|     case 4:
 | |
|       ColorMapNum = 16;
 | |
|       break;
 | |
|     case 8:
 | |
|       ColorMapNum = 256;
 | |
|       break;
 | |
|     default:
 | |
|       ColorMapNum = 0;
 | |
|       break;
 | |
|     }
 | |
|     //
 | |
|     // BMP file may has padding data between the bmp header section and the
 | |
|     // bmp data section.
 | |
|     //
 | |
|     if (BmpHeader->ImageOffset - sizeof (BMP_IMAGE_HEADER) < sizeof (BMP_COLOR_MAP) * ColorMapNum) {
 | |
|       return RETURN_UNSUPPORTED;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Calculate graphics image data address in the image
 | |
|   //
 | |
|   Image = ((UINT8 *)BmpImage) + BmpHeader->ImageOffset;
 | |
|   ImageHeader = Image;
 | |
| 
 | |
|   //
 | |
|   // Calculate the BltBuffer needed size.
 | |
|   //
 | |
|   Status = SafeUint32Mult (
 | |
|              BmpHeader->PixelWidth,
 | |
|              BmpHeader->PixelHeight,
 | |
|              &BltBufferSize
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateBmpToGopBlt: invalid BltBuffer needed size... PixelWidth:0x%x PixelHeight:0x%x\n",
 | |
|       BmpHeader->PixelWidth, BmpHeader->PixelHeight
 | |
|       ));
 | |
| 
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   Temp = BltBufferSize;
 | |
|   Status = SafeUint32Mult (
 | |
|              BltBufferSize,
 | |
|              sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
 | |
|              &BltBufferSize
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateBmpToGopBlt: invalid BltBuffer needed size... PixelWidth x PixelHeight:0x%x struct size:0x%x\n",
 | |
|       Temp, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
 | |
|       ));
 | |
| 
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   IsAllocated = FALSE;
 | |
|   if (*GopBlt == NULL) {
 | |
|     //
 | |
|     // GopBlt is not allocated by caller.
 | |
|     //
 | |
|     DEBUG ((DEBUG_INFO, "Bmp Support: Allocating 0x%X bytes of memory\n", BltBufferSize));
 | |
|     *GopBltSize = (UINTN)BltBufferSize;
 | |
|     *GopBlt = AllocatePool (*GopBltSize);
 | |
|     IsAllocated = TRUE;
 | |
|     if (*GopBlt == NULL) {
 | |
|       return RETURN_OUT_OF_RESOURCES;
 | |
|     }
 | |
|   } else {
 | |
|     //
 | |
|     // GopBlt has been allocated by caller.
 | |
|     //
 | |
|     if (*GopBltSize < (UINTN)BltBufferSize) {
 | |
|       *GopBltSize = (UINTN)BltBufferSize;
 | |
|       return RETURN_BUFFER_TOO_SMALL;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   *PixelWidth  = BmpHeader->PixelWidth;
 | |
|   *PixelHeight = BmpHeader->PixelHeight;
 | |
|   DEBUG ((DEBUG_INFO, "BmpHeader->ImageOffset 0x%X\n", BmpHeader->ImageOffset));
 | |
|   DEBUG ((DEBUG_INFO, "BmpHeader->PixelWidth 0x%X\n", BmpHeader->PixelWidth));
 | |
|   DEBUG ((DEBUG_INFO, "BmpHeader->PixelHeight 0x%X\n", BmpHeader->PixelHeight));
 | |
|   DEBUG ((DEBUG_INFO, "BmpHeader->BitPerPixel 0x%X\n", BmpHeader->BitPerPixel));
 | |
|   DEBUG ((DEBUG_INFO, "BmpHeader->ImageSize 0x%X\n", BmpHeader->ImageSize));
 | |
|   DEBUG ((DEBUG_INFO, "BmpHeader->HeaderSize 0x%X\n", BmpHeader->HeaderSize));
 | |
|   DEBUG ((DEBUG_INFO, "BmpHeader->Size 0x%X\n", BmpHeader->Size));
 | |
| 
 | |
|   //
 | |
|   // Translate image from BMP to Blt buffer format
 | |
|   //
 | |
|   BltBuffer = *GopBlt;
 | |
|   for (Height = 0; Height < BmpHeader->PixelHeight; Height++) {
 | |
|     Blt = &BltBuffer[ (BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth];
 | |
|     for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt++) {
 | |
|       switch (BmpHeader->BitPerPixel) {
 | |
|       case 1:
 | |
|         //
 | |
|         // Translate 1-bit (2 colors) BMP to 24-bit color
 | |
|         //
 | |
|         for (Index = 0; Index < 8 && Width < BmpHeader->PixelWidth; Index++) {
 | |
|           Blt->Red   = BmpColorMap[ ((*Image) >> (7 - Index)) & 0x1].Red;
 | |
|           Blt->Green = BmpColorMap[ ((*Image) >> (7 - Index)) & 0x1].Green;
 | |
|           Blt->Blue  = BmpColorMap[ ((*Image) >> (7 - Index)) & 0x1].Blue;
 | |
|           Blt++;
 | |
|           Width++;
 | |
|         }
 | |
| 
 | |
|         Blt--;
 | |
|         Width--;
 | |
|         break;
 | |
| 
 | |
|       case 4:
 | |
|         //
 | |
|         // Translate 4-bit (16 colors) BMP Palette to 24-bit color
 | |
|         //
 | |
|         Index = (*Image) >> 4;
 | |
|         Blt->Red   = BmpColorMap[Index].Red;
 | |
|         Blt->Green = BmpColorMap[Index].Green;
 | |
|         Blt->Blue  = BmpColorMap[Index].Blue;
 | |
|         if (Width < (BmpHeader->PixelWidth - 1)) {
 | |
|           Blt++;
 | |
|           Width++;
 | |
|           Index = (*Image) & 0x0f;
 | |
|           Blt->Red   = BmpColorMap[Index].Red;
 | |
|           Blt->Green = BmpColorMap[Index].Green;
 | |
|           Blt->Blue  = BmpColorMap[Index].Blue;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case 8:
 | |
|         //
 | |
|         // Translate 8-bit (256 colors) BMP Palette to 24-bit color
 | |
|         //
 | |
|         Blt->Red   = BmpColorMap[*Image].Red;
 | |
|         Blt->Green = BmpColorMap[*Image].Green;
 | |
|         Blt->Blue  = BmpColorMap[*Image].Blue;
 | |
|         break;
 | |
| 
 | |
|       case 24:
 | |
|         //
 | |
|         // It is 24-bit BMP.
 | |
|         //
 | |
|         Blt->Blue  = *Image++;
 | |
|         Blt->Green = *Image++;
 | |
|         Blt->Red   = *Image;
 | |
|         break;
 | |
| 
 | |
|       case 32:
 | |
|         //
 | |
|         //Conver 32 bit to 24bit bmp - just ignore the final byte of each pixel
 | |
|         Blt->Blue  = *Image++;
 | |
|         Blt->Green = *Image++;
 | |
|         Blt->Red   = *Image++;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         //
 | |
|         // Other bit format BMP is not supported.
 | |
|         //
 | |
|         if (IsAllocated) {
 | |
|           FreePool (*GopBlt);
 | |
|           *GopBlt = NULL;
 | |
|         }
 | |
|         DEBUG ((DEBUG_ERROR, "Bmp Bit format not supported.  0x%X\n", BmpHeader->BitPerPixel));
 | |
|         return RETURN_UNSUPPORTED;
 | |
|         break;
 | |
|       };
 | |
| 
 | |
|     }
 | |
| 
 | |
|     ImageIndex = (UINTN)Image - (UINTN)ImageHeader;
 | |
|     if ((ImageIndex % 4) != 0) {
 | |
|       //
 | |
|       // Bmp Image starts each row on a 32-bit boundary!
 | |
|       //
 | |
|       Image = Image + (4 - (ImageIndex % 4));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return RETURN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Translate a GOP blt buffer to an uncompressed 24-bit per pixel BMP graphics
 | |
|   image. If a NULL BmpImage is passed in a BmpImage buffer will be allocated by
 | |
|   this routine using EFI_BOOT_SERVICES.AllocatePool(). If a BmpImage buffer is
 | |
|   passed in it will be used if it is big enough.
 | |
| 
 | |
|   @param [in]      GopBlt        Pointer to GOP blt buffer.
 | |
|   @param [in]      PixelHeight   Height of GopBlt/BmpImage in pixels.
 | |
|   @param [in]      PixelWidth    Width of GopBlt/BmpImage in pixels.
 | |
|   @param [in, out] BmpImage      Buffer containing BMP version of GopBlt.
 | |
|   @param [in, out] BmpImageSize  Size of BmpImage in bytes.
 | |
| 
 | |
|   @retval RETURN_SUCCESS            BmpImage and BmpImageSize are returned.
 | |
|   @retval RETURN_INVALID_PARAMETER  GopBlt is NULL.
 | |
|   @retval RETURN_INVALID_PARAMETER  BmpImage is NULL.
 | |
|   @retval RETURN_INVALID_PARAMETER  BmpImageSize is NULL.
 | |
|   @retval RETURN_UNSUPPORTED        GopBlt cannot be converted to a *.BMP image.
 | |
|   @retval RETURN_BUFFER_TOO_SMALL   The passed in BmpImage buffer is not big
 | |
|                                     enough.  The required size is returned in
 | |
|                                     BmpImageSize.
 | |
|   @retval RETURN_OUT_OF_RESOURCES   The BmpImage buffer could not be allocated.
 | |
| 
 | |
| **/
 | |
| RETURN_STATUS
 | |
| EFIAPI
 | |
| TranslateGopBltToBmp (
 | |
|   IN     EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *GopBlt,
 | |
|   IN     UINT32                         PixelHeight,
 | |
|   IN     UINT32                         PixelWidth,
 | |
|   IN OUT VOID                           **BmpImage,
 | |
|   IN OUT UINT32                         *BmpImageSize
 | |
|   )
 | |
| {
 | |
|   RETURN_STATUS                  Status;
 | |
|   UINT32                         PaddingSize;
 | |
|   UINT32                         BmpSize;
 | |
|   BMP_IMAGE_HEADER               *BmpImageHeader;
 | |
|   UINT8                          *Image;
 | |
|   UINTN                          Col;
 | |
|   UINTN                          Row;
 | |
|   EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BltPixel;
 | |
| 
 | |
|   if (GopBlt == NULL || BmpImage == NULL || BmpImageSize == NULL) {
 | |
|     return RETURN_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((PixelHeight == 0) || (PixelWidth == 0)) {
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Allocate memory for BMP file.
 | |
|   //
 | |
|   PaddingSize = PixelWidth & 0x3;
 | |
| 
 | |
|   //
 | |
|   // First check PixelWidth * 3 + PaddingSize doesn't overflow
 | |
|   //
 | |
|   Status = SafeUint32Mult (PixelWidth, 3, &BmpSize);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
 | |
|       PixelHeight,
 | |
|       PixelWidth
 | |
|       ));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
|   Status = SafeUint32Add (BmpSize, PaddingSize, &BmpSize);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
 | |
|       PixelHeight,
 | |
|       PixelWidth
 | |
|       ));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Second check (mLogoWidth * 3 + PaddingSize) * mLogoHeight + sizeof (BMP_IMAGE_HEADER) doesn't overflow
 | |
|   //
 | |
|   Status = SafeUint32Mult (BmpSize, PixelHeight, &BmpSize);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
 | |
|       PixelHeight,
 | |
|       PixelWidth
 | |
|       ));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
|   Status = SafeUint32Add (BmpSize, sizeof (BMP_IMAGE_HEADER), &BmpSize);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((
 | |
|       DEBUG_ERROR,
 | |
|       "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
 | |
|       PixelHeight,
 | |
|       PixelWidth
 | |
|       ));
 | |
|     return RETURN_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // The image should be stored in EfiBootServicesData, allowing the system to
 | |
|   // reclaim the memory
 | |
|   //
 | |
|   if (*BmpImage == NULL) {
 | |
|     *BmpImage = AllocateZeroPool (BmpSize);
 | |
|     if (*BmpImage == NULL) {
 | |
|       return EFI_OUT_OF_RESOURCES;
 | |
|     }
 | |
|     *BmpImageSize = BmpSize;
 | |
|   } else if (*BmpImageSize < BmpSize) {
 | |
|     *BmpImageSize = BmpSize;
 | |
|     return RETURN_BUFFER_TOO_SMALL;
 | |
|   }
 | |
| 
 | |
|   BmpImageHeader = (BMP_IMAGE_HEADER *)*BmpImage;
 | |
|   CopyMem (BmpImageHeader, &mBmpImageHeaderTemplate, sizeof (BMP_IMAGE_HEADER));
 | |
|   BmpImageHeader->Size        = *BmpImageSize;
 | |
|   BmpImageHeader->ImageSize   = *BmpImageSize - sizeof (BMP_IMAGE_HEADER);
 | |
|   BmpImageHeader->PixelWidth  = PixelWidth;
 | |
|   BmpImageHeader->PixelHeight = PixelHeight;
 | |
| 
 | |
|   //
 | |
|   // Convert BLT buffer to BMP file.
 | |
|   //
 | |
|   Image = (UINT8 *)BmpImageHeader + sizeof (BMP_IMAGE_HEADER);
 | |
|   for (Row = 0; Row < PixelHeight; Row++) {
 | |
|     BltPixel = &GopBlt[(PixelHeight - Row - 1) * PixelWidth];
 | |
| 
 | |
|     for (Col = 0; Col < PixelWidth; Col++) {
 | |
|       *Image++ = BltPixel->Blue;
 | |
|       *Image++ = BltPixel->Green;
 | |
|       *Image++ = BltPixel->Red;
 | |
|       BltPixel++;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Padding for 4 byte alignment.
 | |
|     //
 | |
|     Image += PaddingSize;
 | |
|   }
 | |
| 
 | |
|   return RETURN_SUCCESS;
 | |
| }
 |