blob: a43364a9893b94ce509a6286262cd33a7c0ca951 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <bootsplash.h>
#include <console/console.h>
#include <fsp/api.h>
#include <fsp/fsp_gop_blt.h>
#include <stdlib.h>
static bool is_bmp_image_valid(efi_bmp_image_header *header)
{
if (header == NULL)
return false;
/* Check if the BMP Header Signature is valid */
if (header->CharB != 'B' || header->CharM != 'M')
return false;
/* Check if the BMP Image Header Length is valid */
if (!header->PixelHeight || !header->PixelWidth)
return false;
if (header->Size < header->ImageOffset)
return false;
if (header->ImageOffset < sizeof(efi_bmp_image_header))
return false;
return true;
}
static bool is_bmp_image_compressed(efi_bmp_image_header *header)
{
if (header == NULL)
return false;
if (header->CompressionType != 0)
return true;
return false;
}
static bool is_bitmap_format_supported(efi_bmp_image_header *header)
{
if (header == NULL)
return false;
/*
* Check BITMAP format is supported
* BMP_IMAGE_HEADER = BITMAP_FILE_HEADER + BITMAP_INFO_HEADER
*/
if (header->HeaderSize != sizeof(efi_bmp_image_header) -
OFFSET_OF(efi_bmp_image_header, HeaderSize))
return false;
return true;
}
static bool do_bmp_image_authentication(efi_bmp_image_header *header)
{
if (header == NULL)
return false;
if (!is_bmp_image_valid(header)) {
printk(BIOS_ERR, "%s: BMP Image Header is invalid.\n", __func__);
return false;
}
/*
* BMP image compression is unsupported by FSP implementation,
* hence, exit if the BMP image is compressed.
*/
if (is_bmp_image_compressed(header)) {
printk(BIOS_ERR, "%s: BMP Image Compression is unsupported.\n", __func__);
return false;
}
if (!is_bitmap_format_supported(header)) {
printk(BIOS_ERR, "%s: BmpHeader Header Size (0x%x) is not as expected.\n",
__func__, header->HeaderSize);
return false;
}
return true;
}
static uint32_t calculate_blt_buffer_size(efi_bmp_image_header *header)
{
uint32_t blt_buffer_size;
if (header == NULL)
return 0;
/* Calculate the size required for BLT buffer */
blt_buffer_size = header->PixelWidth * header->PixelHeight *
sizeof(efi_graphics_output_blt_pixel);
if (!blt_buffer_size)
return 0;
return blt_buffer_size;
}
static uint32_t get_color_map_num(efi_bmp_image_header *header)
{
uint32_t col_map_number = 0;
if (header == NULL)
return 0;
switch (header->BitPerPixel) {
case 1:
col_map_number = 2;
break;
case 4:
col_map_number = 16;
break;
case 8:
col_map_number = 256;
break;
default:
break;
}
/*
* At times BMP file may have padding data between its header section and the
* data section.
*/
if (header->ImageOffset - sizeof(efi_bmp_image_header) <
sizeof(efi_bmp_color_map) * col_map_number)
return 0;
return col_map_number;
}
/* Fill BMP image into BLT buffer format */
static void *fill_blt_buffer(efi_bmp_image_header *header,
uint32_t logo_ptr, uint32_t blt_buffer_size)
{
efi_graphics_output_blt_pixel *gop_blt_buffer;
efi_graphics_output_blt_pixel *gop_blt_ptr;
efi_graphics_output_blt_pixel *gop_blt;
uint8_t *bmp_image;
uint8_t *bmp_image_header;
efi_bmp_color_map *bmp_color_map;
size_t image_index;
if (header == NULL)
return NULL;
gop_blt_ptr = malloc(sizeof(blt_buffer_size));
if (!gop_blt_ptr)
die("%s: out of memory. Consider increasing the `CONFIG_HEAP_SIZE`\n",
__func__);
bmp_image = ((UINT8 *)logo_ptr) + header->ImageOffset;
bmp_image_header = bmp_image;
gop_blt_buffer = gop_blt_ptr;
bmp_color_map = (efi_bmp_color_map *)(logo_ptr + sizeof(efi_bmp_image_header));
for (size_t height = 0; height < header->PixelHeight; height++) {
gop_blt = &gop_blt_buffer[(header->PixelHeight - height - 1) *
header->PixelWidth];
for (size_t width = 0; width < header->PixelWidth; width++, bmp_image++,
gop_blt++) {
size_t index = 0;
switch (header->BitPerPixel) {
/* Translate 1-bit (2 colors) BMP to 24-bit color */
case 1:
for (index = 0; index < 8 && width < header->PixelWidth; index++) {
gop_blt->Red = bmp_color_map[((*bmp_image) >> (7 - index)) & 0x1].Red;
gop_blt->Green = bmp_color_map[((*bmp_image) >> (7 - index)) & 0x1].Green;
gop_blt->Blue = bmp_color_map[((*bmp_image) >> (7 - index)) & 0x1].Blue;
gop_blt++;
width++;
}
gop_blt--;
width--;
break;
/* Translate 4-bit (16 colors) BMP Palette to 24-bit color */
case 4:
index = (*bmp_image) >> 4;
gop_blt->Red = bmp_color_map[index].Red;
gop_blt->Green = bmp_color_map[index].Green;
gop_blt->Blue = bmp_color_map[index].Blue;
if (width < (header->PixelWidth - 1)) {
gop_blt++;
width++;
index = (*bmp_image) & 0x0f;
gop_blt->Red = bmp_color_map[index].Red;
gop_blt->Green = bmp_color_map[index].Green;
gop_blt->Blue = bmp_color_map[index].Blue;
}
break;
/* Translate 8-bit (256 colors) BMP Palette to 24-bit color */
case 8:
gop_blt->Red = bmp_color_map[*bmp_image].Red;
gop_blt->Green = bmp_color_map[*bmp_image].Green;
gop_blt->Blue = bmp_color_map[*bmp_image].Blue;
break;
/* For 24-bit BMP */
case 24:
gop_blt->Blue = *bmp_image++;
gop_blt->Green = *bmp_image++;
gop_blt->Red = *bmp_image;
break;
/* Convert 32 bit to 24bit bmp - just ignore the final byte of each pixel */
case 32:
gop_blt->Blue = *bmp_image++;
gop_blt->Green = *bmp_image++;
gop_blt->Red = *bmp_image++;
break;
/* Other bit format of BMP is not supported. */
default:
free(gop_blt_ptr);
gop_blt_ptr = NULL;
printk(BIOS_ERR, "%s, BMP Bit format not supported. 0x%X\n", __func__,
header->BitPerPixel);
return NULL;
}
}
image_index = (uintptr_t)bmp_image - (uintptr_t)bmp_image_header;
/* Each row in BMP Image should be 4-byte align */
if ((image_index % 4) != 0)
bmp_image = bmp_image + (4 - (image_index % 4));
}
return gop_blt_ptr;
}
/* Convert a *.BMP graphics image to a GOP blt buffer */
void fsp_convert_bmp_to_gop_blt(uint32_t *logo, uint32_t *logo_size,
uint32_t *blt_ptr, uint32_t *blt_size,
uint32_t *pixel_height, uint32_t *pixel_width)
{
uint32_t logo_ptr, logo_ptr_size, blt_buffer_size;
efi_bmp_image_header *bmp_header;
if (!logo || !logo_size || !blt_ptr || !blt_size || !pixel_height || !pixel_width)
return;
bmp_load_logo(&logo_ptr, &logo_ptr_size);
if (!logo_ptr || logo_ptr_size < sizeof(efi_bmp_image_header)) {
printk(BIOS_ERR, "%s: BMP Image size is too small.\n", __func__);
return;
}
bmp_header = (efi_bmp_image_header *)logo_ptr;
if (!do_bmp_image_authentication(bmp_header) || (bmp_header->Size != logo_ptr_size))
return;
blt_buffer_size = calculate_blt_buffer_size(bmp_header);
if (!blt_buffer_size)
return;
if (!get_color_map_num(bmp_header))
return;
*logo = logo_ptr;
*logo_size = logo_ptr_size;
*blt_size = blt_buffer_size;
*pixel_height = bmp_header->PixelHeight;
*pixel_width = bmp_header->PixelWidth;
*blt_ptr = (uint32_t)fill_blt_buffer(bmp_header, logo_ptr, blt_buffer_size);
}