3 Provides services to convert a BMP graphics image to a GOP BLT buffer and
4 from a GOP BLT buffer to a BMP graphics image.
6 Caution: This module requires additional review when modified.
7 This module processes external input - BMP image.
8 This external input must be validated carefully to avoid security issue such
9 as buffer overflow, integer overflow.
11 TranslateBmpToGopBlt() receives untrusted input and performs basic validation.
13 Copyright (c) 2016-2017, Microsoft Corporation
14 Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
17 SPDX-License-Identifier: BSD-2-Clause-Patent
22 #include <Library/DebugLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/SafeIntLib.h>
26 #include <IndustryStandard/Bmp.h>
28 #include <Library/BmpSupportLib.h>
31 // BMP Image header for an uncompressed 24-bit per pixel BMP image.
33 const BMP_IMAGE_HEADER mBmpImageHeaderTemplate
= {
36 0, // Size will be updated at runtime
38 sizeof (BMP_IMAGE_HEADER
), // ImageOffset
39 sizeof (BMP_IMAGE_HEADER
) - OFFSET_OF (BMP_IMAGE_HEADER
, HeaderSize
), // HeaderSize
40 0, // PixelWidth will be updated at runtime
41 0, // PixelHeight will be updated at runtime
45 0, // ImageSize will be updated at runtime
53 Translate a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer
54 is passed in a GopBlt buffer will be allocated by this routine using
55 EFI_BOOT_SERVICES.AllocatePool(). If a GopBlt buffer is passed in it will be
56 used if it is big enough.
58 @param[in] BmpImage Pointer to BMP file.
59 @param[in] BmpImageSize Number of bytes in BmpImage.
60 @param[in, out] GopBlt Buffer containing GOP version of BmpImage.
61 @param[in, out] GopBltSize Size of GopBlt in bytes.
62 @param[out] PixelHeight Height of GopBlt/BmpImage in pixels.
63 @param[out] PixelWidth Width of GopBlt/BmpImage in pixels.
65 @retval RETURN_SUCCESS GopBlt and GopBltSize are returned.
66 @retval RETURN_INVALID_PARAMETER BmpImage is NULL.
67 @retval RETURN_INVALID_PARAMETER GopBlt is NULL.
68 @retval RETURN_INVALID_PARAMETER GopBltSize is NULL.
69 @retval RETURN_INVALID_PARAMETER PixelHeight is NULL.
70 @retval RETURN_INVALID_PARAMETER PixelWidth is NULL.
71 @retval RETURN_UNSUPPORTED BmpImage is not a valid *.BMP image.
72 @retval RETURN_BUFFER_TOO_SMALL The passed in GopBlt buffer is not big
73 enough. The required size is returned in
75 @retval RETURN_OUT_OF_RESOURCES The GopBlt buffer could not be allocated.
80 TranslateBmpToGopBlt (
82 IN UINTN BmpImageSize
,
83 IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL
**GopBlt
,
84 IN OUT UINTN
*GopBltSize
,
85 OUT UINTN
*PixelHeight
,
91 BMP_IMAGE_HEADER
*BmpHeader
;
92 BMP_COLOR_MAP
*BmpColorMap
;
93 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*BltBuffer
;
94 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*Blt
;
100 UINT32 DataSizePerLine
;
103 RETURN_STATUS Status
;
107 if (BmpImage
== NULL
|| GopBlt
== NULL
|| GopBltSize
== NULL
) {
108 return RETURN_INVALID_PARAMETER
;
110 if (PixelHeight
== NULL
|| PixelWidth
== NULL
) {
111 return RETURN_INVALID_PARAMETER
;
114 if (BmpImageSize
< sizeof (BMP_IMAGE_HEADER
)) {
115 DEBUG ((DEBUG_ERROR
, "TranslateBmpToGopBlt: BmpImageSize too small\n"));
116 return RETURN_UNSUPPORTED
;
119 BmpHeader
= (BMP_IMAGE_HEADER
*)BmpImage
;
121 if (BmpHeader
->CharB
!= 'B' || BmpHeader
->CharM
!= 'M') {
122 DEBUG ((DEBUG_ERROR
, "TranslateBmpToGopBlt: BmpHeader->Char fields incorrect\n"));
123 return RETURN_UNSUPPORTED
;
127 // Doesn't support compress.
129 if (BmpHeader
->CompressionType
!= 0) {
130 DEBUG ((DEBUG_ERROR
, "TranslateBmpToGopBlt: Compression Type unsupported.\n"));
131 return RETURN_UNSUPPORTED
;
134 if ((BmpHeader
->PixelHeight
== 0) || (BmpHeader
->PixelWidth
== 0)) {
135 DEBUG ((DEBUG_ERROR
, "TranslateBmpToGopBlt: BmpHeader->PixelHeight or BmpHeader->PixelWidth is 0.\n"));
136 return RETURN_UNSUPPORTED
;
140 // Only support BITMAPINFOHEADER format.
141 // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER
143 if (BmpHeader
->HeaderSize
!= sizeof (BMP_IMAGE_HEADER
) - OFFSET_OF (BMP_IMAGE_HEADER
, HeaderSize
)) {
146 "TranslateBmpToGopBlt: BmpHeader->Headership is not as expected. Headersize is 0x%x\n",
147 BmpHeader
->HeaderSize
149 return RETURN_UNSUPPORTED
;
153 // The data size in each line must be 4 byte alignment.
155 Status
= SafeUint32Mult (
156 BmpHeader
->PixelWidth
,
157 BmpHeader
->BitPerPixel
,
160 if (EFI_ERROR (Status
)) {
163 "TranslateBmpToGopBlt: invalid BmpImage... PixelWidth:0x%x BitPerPixel:0x%x\n",
164 BmpHeader
->PixelWidth
,
165 BmpHeader
->BitPerPixel
167 return RETURN_UNSUPPORTED
;
170 Status
= SafeUint32Add (DataSizePerLine
, 31, &DataSizePerLine
);
171 if (EFI_ERROR (Status
)) {
174 "TranslateBmpToGopBlt: invalid BmpImage... DataSizePerLine:0x%x\n",
178 return RETURN_UNSUPPORTED
;
181 DataSizePerLine
= (DataSizePerLine
>> 3) &(~0x3);
182 Status
= SafeUint32Mult (
184 BmpHeader
->PixelHeight
,
188 if (EFI_ERROR (Status
)) {
191 "TranslateBmpToGopBlt: invalid BmpImage... DataSizePerLine:0x%x PixelHeight:0x%x\n",
192 DataSizePerLine
, BmpHeader
->PixelHeight
195 return RETURN_UNSUPPORTED
;
198 Status
= SafeUint32Mult (
199 BmpHeader
->PixelHeight
,
204 if (EFI_ERROR (Status
)) {
207 "TranslateBmpToGopBlt: invalid BmpImage... PixelHeight:0x%x DataSizePerLine:0x%x\n",
208 BmpHeader
->PixelHeight
, DataSizePerLine
211 return RETURN_UNSUPPORTED
;
214 if ((BmpHeader
->Size
!= BmpImageSize
) ||
215 (BmpHeader
->Size
< BmpHeader
->ImageOffset
) ||
216 (BmpHeader
->Size
- BmpHeader
->ImageOffset
!= DataSize
)) {
218 DEBUG ((DEBUG_ERROR
, "TranslateBmpToGopBlt: invalid BmpImage... \n"));
219 DEBUG ((DEBUG_ERROR
, " BmpHeader->Size: 0x%x\n", BmpHeader
->Size
));
220 DEBUG ((DEBUG_ERROR
, " BmpHeader->ImageOffset: 0x%x\n", BmpHeader
->ImageOffset
));
221 DEBUG ((DEBUG_ERROR
, " BmpImageSize: 0x%lx\n", (UINTN
)BmpImageSize
));
222 DEBUG ((DEBUG_ERROR
, " DataSize: 0x%lx\n", (UINTN
)DataSize
));
224 return RETURN_UNSUPPORTED
;
228 // Calculate Color Map offset in the image.
231 BmpColorMap
= (BMP_COLOR_MAP
*)(Image
+ sizeof (BMP_IMAGE_HEADER
));
232 if (BmpHeader
->ImageOffset
< sizeof (BMP_IMAGE_HEADER
)) {
233 return RETURN_UNSUPPORTED
;
236 if (BmpHeader
->ImageOffset
> sizeof (BMP_IMAGE_HEADER
)) {
237 switch (BmpHeader
->BitPerPixel
) {
252 // BMP file may has padding data between the bmp header section and the
255 if (BmpHeader
->ImageOffset
- sizeof (BMP_IMAGE_HEADER
) < sizeof (BMP_COLOR_MAP
) * ColorMapNum
) {
256 return RETURN_UNSUPPORTED
;
261 // Calculate graphics image data address in the image
263 Image
= ((UINT8
*)BmpImage
) + BmpHeader
->ImageOffset
;
267 // Calculate the BltBuffer needed size.
269 Status
= SafeUint32Mult (
270 BmpHeader
->PixelWidth
,
271 BmpHeader
->PixelHeight
,
275 if (EFI_ERROR (Status
)) {
278 "TranslateBmpToGopBlt: invalid BltBuffer needed size... PixelWidth:0x%x PixelHeight:0x%x\n",
279 BmpHeader
->PixelWidth
, BmpHeader
->PixelHeight
282 return RETURN_UNSUPPORTED
;
285 Temp
= BltBufferSize
;
286 Status
= SafeUint32Mult (
288 sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
),
292 if (EFI_ERROR (Status
)) {
295 "TranslateBmpToGopBlt: invalid BltBuffer needed size... PixelWidth x PixelHeight:0x%x struct size:0x%x\n",
296 Temp
, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
)
299 return RETURN_UNSUPPORTED
;
303 if (*GopBlt
== NULL
) {
305 // GopBlt is not allocated by caller.
307 DEBUG ((DEBUG_INFO
, "Bmp Support: Allocating 0x%X bytes of memory\n", BltBufferSize
));
308 *GopBltSize
= (UINTN
)BltBufferSize
;
309 *GopBlt
= AllocatePool (*GopBltSize
);
311 if (*GopBlt
== NULL
) {
312 return RETURN_OUT_OF_RESOURCES
;
316 // GopBlt has been allocated by caller.
318 if (*GopBltSize
< (UINTN
)BltBufferSize
) {
319 *GopBltSize
= (UINTN
)BltBufferSize
;
320 return RETURN_BUFFER_TOO_SMALL
;
324 *PixelWidth
= BmpHeader
->PixelWidth
;
325 *PixelHeight
= BmpHeader
->PixelHeight
;
326 DEBUG ((DEBUG_INFO
, "BmpHeader->ImageOffset 0x%X\n", BmpHeader
->ImageOffset
));
327 DEBUG ((DEBUG_INFO
, "BmpHeader->PixelWidth 0x%X\n", BmpHeader
->PixelWidth
));
328 DEBUG ((DEBUG_INFO
, "BmpHeader->PixelHeight 0x%X\n", BmpHeader
->PixelHeight
));
329 DEBUG ((DEBUG_INFO
, "BmpHeader->BitPerPixel 0x%X\n", BmpHeader
->BitPerPixel
));
330 DEBUG ((DEBUG_INFO
, "BmpHeader->ImageSize 0x%X\n", BmpHeader
->ImageSize
));
331 DEBUG ((DEBUG_INFO
, "BmpHeader->HeaderSize 0x%X\n", BmpHeader
->HeaderSize
));
332 DEBUG ((DEBUG_INFO
, "BmpHeader->Size 0x%X\n", BmpHeader
->Size
));
335 // Translate image from BMP to Blt buffer format
338 for (Height
= 0; Height
< BmpHeader
->PixelHeight
; Height
++) {
339 Blt
= &BltBuffer
[ (BmpHeader
->PixelHeight
- Height
- 1) * BmpHeader
->PixelWidth
];
340 for (Width
= 0; Width
< BmpHeader
->PixelWidth
; Width
++, Image
++, Blt
++) {
341 switch (BmpHeader
->BitPerPixel
) {
344 // Translate 1-bit (2 colors) BMP to 24-bit color
346 for (Index
= 0; Index
< 8 && Width
< BmpHeader
->PixelWidth
; Index
++) {
347 Blt
->Red
= BmpColorMap
[ ((*Image
) >> (7 - Index
)) & 0x1].Red
;
348 Blt
->Green
= BmpColorMap
[ ((*Image
) >> (7 - Index
)) & 0x1].Green
;
349 Blt
->Blue
= BmpColorMap
[ ((*Image
) >> (7 - Index
)) & 0x1].Blue
;
360 // Translate 4-bit (16 colors) BMP Palette to 24-bit color
362 Index
= (*Image
) >> 4;
363 Blt
->Red
= BmpColorMap
[Index
].Red
;
364 Blt
->Green
= BmpColorMap
[Index
].Green
;
365 Blt
->Blue
= BmpColorMap
[Index
].Blue
;
366 if (Width
< (BmpHeader
->PixelWidth
- 1)) {
369 Index
= (*Image
) & 0x0f;
370 Blt
->Red
= BmpColorMap
[Index
].Red
;
371 Blt
->Green
= BmpColorMap
[Index
].Green
;
372 Blt
->Blue
= BmpColorMap
[Index
].Blue
;
378 // Translate 8-bit (256 colors) BMP Palette to 24-bit color
380 Blt
->Red
= BmpColorMap
[*Image
].Red
;
381 Blt
->Green
= BmpColorMap
[*Image
].Green
;
382 Blt
->Blue
= BmpColorMap
[*Image
].Blue
;
389 Blt
->Blue
= *Image
++;
390 Blt
->Green
= *Image
++;
396 //Conver 32 bit to 24bit bmp - just ignore the final byte of each pixel
397 Blt
->Blue
= *Image
++;
398 Blt
->Green
= *Image
++;
404 // Other bit format BMP is not supported.
410 DEBUG ((DEBUG_ERROR
, "Bmp Bit format not supported. 0x%X\n", BmpHeader
->BitPerPixel
));
411 return RETURN_UNSUPPORTED
;
417 ImageIndex
= (UINTN
)Image
- (UINTN
)ImageHeader
;
418 if ((ImageIndex
% 4) != 0) {
420 // Bmp Image starts each row on a 32-bit boundary!
422 Image
= Image
+ (4 - (ImageIndex
% 4));
426 return RETURN_SUCCESS
;
430 Translate a GOP blt buffer to an uncompressed 24-bit per pixel BMP graphics
431 image. If a NULL BmpImage is passed in a BmpImage buffer will be allocated by
432 this routine using EFI_BOOT_SERVICES.AllocatePool(). If a BmpImage buffer is
433 passed in it will be used if it is big enough.
435 @param [in] GopBlt Pointer to GOP blt buffer.
436 @param [in] PixelHeight Height of GopBlt/BmpImage in pixels.
437 @param [in] PixelWidth Width of GopBlt/BmpImage in pixels.
438 @param [in, out] BmpImage Buffer containing BMP version of GopBlt.
439 @param [in, out] BmpImageSize Size of BmpImage in bytes.
441 @retval RETURN_SUCCESS BmpImage and BmpImageSize are returned.
442 @retval RETURN_INVALID_PARAMETER GopBlt is NULL.
443 @retval RETURN_INVALID_PARAMETER BmpImage is NULL.
444 @retval RETURN_INVALID_PARAMETER BmpImageSize is NULL.
445 @retval RETURN_UNSUPPORTED GopBlt cannot be converted to a *.BMP image.
446 @retval RETURN_BUFFER_TOO_SMALL The passed in BmpImage buffer is not big
447 enough. The required size is returned in
449 @retval RETURN_OUT_OF_RESOURCES The BmpImage buffer could not be allocated.
454 TranslateGopBltToBmp (
455 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*GopBlt
,
456 IN UINT32 PixelHeight
,
457 IN UINT32 PixelWidth
,
458 IN OUT VOID
**BmpImage
,
459 IN OUT UINT32
*BmpImageSize
462 RETURN_STATUS Status
;
465 BMP_IMAGE_HEADER
*BmpImageHeader
;
469 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*BltPixel
;
471 if (GopBlt
== NULL
|| BmpImage
== NULL
|| BmpImageSize
== NULL
) {
472 return RETURN_INVALID_PARAMETER
;
475 if ((PixelHeight
== 0) || (PixelWidth
== 0)) {
476 return RETURN_UNSUPPORTED
;
480 // Allocate memory for BMP file.
482 PaddingSize
= PixelWidth
& 0x3;
485 // First check PixelWidth * 3 + PaddingSize doesn't overflow
487 Status
= SafeUint32Mult (PixelWidth
, 3, &BmpSize
);
488 if (EFI_ERROR (Status
)) {
491 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
495 return RETURN_UNSUPPORTED
;
497 Status
= SafeUint32Add (BmpSize
, PaddingSize
, &BmpSize
);
498 if (EFI_ERROR (Status
)) {
501 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
505 return RETURN_UNSUPPORTED
;
509 // Second check (mLogoWidth * 3 + PaddingSize) * mLogoHeight + sizeof (BMP_IMAGE_HEADER) doesn't overflow
511 Status
= SafeUint32Mult (BmpSize
, PixelHeight
, &BmpSize
);
512 if (EFI_ERROR (Status
)) {
515 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
519 return RETURN_UNSUPPORTED
;
521 Status
= SafeUint32Add (BmpSize
, sizeof (BMP_IMAGE_HEADER
), &BmpSize
);
522 if (EFI_ERROR (Status
)) {
525 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
529 return RETURN_UNSUPPORTED
;
533 // The image should be stored in EfiBootServicesData, allowing the system to
534 // reclaim the memory
536 if (*BmpImage
== NULL
) {
537 *BmpImage
= AllocateZeroPool (BmpSize
);
538 if (*BmpImage
== NULL
) {
539 return EFI_OUT_OF_RESOURCES
;
541 *BmpImageSize
= BmpSize
;
542 } else if (*BmpImageSize
< BmpSize
) {
543 *BmpImageSize
= BmpSize
;
544 return RETURN_BUFFER_TOO_SMALL
;
547 BmpImageHeader
= (BMP_IMAGE_HEADER
*)*BmpImage
;
548 CopyMem (BmpImageHeader
, &mBmpImageHeaderTemplate
, sizeof (BMP_IMAGE_HEADER
));
549 BmpImageHeader
->Size
= *BmpImageSize
;
550 BmpImageHeader
->ImageSize
= *BmpImageSize
- sizeof (BMP_IMAGE_HEADER
);
551 BmpImageHeader
->PixelWidth
= PixelWidth
;
552 BmpImageHeader
->PixelHeight
= PixelHeight
;
555 // Convert BLT buffer to BMP file.
557 Image
= (UINT8
*)BmpImageHeader
+ sizeof (BMP_IMAGE_HEADER
);
558 for (Row
= 0; Row
< PixelHeight
; Row
++) {
559 BltPixel
= &GopBlt
[(PixelHeight
- Row
- 1) * PixelWidth
];
561 for (Col
= 0; Col
< PixelWidth
; Col
++) {
562 *Image
++ = BltPixel
->Blue
;
563 *Image
++ = BltPixel
->Green
;
564 *Image
++ = BltPixel
->Red
;
569 // Padding for 4 byte alignment.
571 Image
+= PaddingSize
;
574 return RETURN_SUCCESS
;