Subrata Banik | 7bc92f0 | 2023-08-03 10:11:28 +0000 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | |
| 3 | #include <bootsplash.h> |
| 4 | #include <console/console.h> |
| 5 | #include <fsp/api.h> |
| 6 | #include <fsp/fsp_gop_blt.h> |
| 7 | #include <lib.h> |
| 8 | #include <stdlib.h> |
| 9 | |
| 10 | static bool is_bmp_image_valid(efi_bmp_image_header *header) |
| 11 | { |
| 12 | if (header == NULL) |
| 13 | return false; |
| 14 | |
| 15 | /* Check if the BMP Header Signature is valid */ |
| 16 | if (header->CharB != 'B' || header->CharM != 'M') |
| 17 | return false; |
| 18 | |
| 19 | /* Check if the BMP Image Header Length is valid */ |
| 20 | if (!header->PixelHeight || !header->PixelWidth) |
| 21 | return false; |
| 22 | |
| 23 | if (header->Size < header->ImageOffset) |
| 24 | return false; |
| 25 | |
| 26 | if (header->ImageOffset < sizeof(efi_bmp_image_header)) |
| 27 | return false; |
| 28 | |
| 29 | return true; |
| 30 | } |
| 31 | |
| 32 | static bool is_bmp_image_compressed(efi_bmp_image_header *header) |
| 33 | { |
| 34 | if (header == NULL) |
| 35 | return false; |
| 36 | |
| 37 | if (header->CompressionType != 0) |
| 38 | return true; |
| 39 | |
| 40 | return false; |
| 41 | } |
| 42 | |
| 43 | static bool is_bitmap_format_supported(efi_bmp_image_header *header) |
| 44 | { |
| 45 | if (header == NULL) |
| 46 | return false; |
| 47 | |
| 48 | /* |
| 49 | * Check BITMAP format is supported |
| 50 | * BMP_IMAGE_HEADER = BITMAP_FILE_HEADER + BITMAP_INFO_HEADER |
| 51 | */ |
| 52 | if (header->HeaderSize != sizeof(efi_bmp_image_header) - |
| 53 | OFFSET_OF(efi_bmp_image_header, HeaderSize)) |
| 54 | return false; |
| 55 | |
| 56 | return true; |
| 57 | } |
| 58 | |
| 59 | static bool do_bmp_image_authentication(efi_bmp_image_header *header) |
| 60 | { |
| 61 | if (header == NULL) |
| 62 | return false; |
| 63 | |
| 64 | if (!is_bmp_image_valid(header)) { |
| 65 | printk(BIOS_ERR, "%s: BMP Image Header is invalid.\n", __func__); |
| 66 | return false; |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | * BMP image compression is unsupported by FSP implementation, |
| 71 | * hence, exit if the BMP image is compressed. |
| 72 | */ |
| 73 | if (is_bmp_image_compressed(header)) { |
| 74 | printk(BIOS_ERR, "%s: BMP Image Compression is unsupported.\n", __func__); |
| 75 | return false; |
| 76 | } |
| 77 | |
| 78 | if (!is_bitmap_format_supported(header)) { |
| 79 | printk(BIOS_ERR, "%s: BmpHeader Header Size (0x%x) is not as expected.\n", |
| 80 | __func__, header->HeaderSize); |
| 81 | return false; |
| 82 | } |
| 83 | |
| 84 | return true; |
| 85 | } |
| 86 | |
| 87 | static uint32_t calculate_blt_buffer_size(efi_bmp_image_header *header) |
| 88 | { |
| 89 | uint32_t blt_buffer_size; |
| 90 | |
| 91 | if (header == NULL) |
| 92 | return 0; |
| 93 | |
| 94 | /* Calculate the size required for BLT buffer */ |
| 95 | blt_buffer_size = header->PixelWidth * header->PixelHeight * |
| 96 | sizeof(efi_graphics_output_blt_pixel); |
| 97 | if (!blt_buffer_size) |
| 98 | return 0; |
| 99 | |
| 100 | return blt_buffer_size; |
| 101 | } |
| 102 | |
| 103 | static uint32_t get_color_map_num(efi_bmp_image_header *header) |
| 104 | { |
| 105 | uint32_t col_map_number = 0; |
| 106 | |
| 107 | if (header == NULL) |
| 108 | return 0; |
| 109 | |
| 110 | switch (header->BitPerPixel) { |
| 111 | case 1: |
| 112 | col_map_number = 2; |
| 113 | break; |
| 114 | case 4: |
| 115 | col_map_number = 16; |
| 116 | break; |
| 117 | case 8: |
| 118 | col_map_number = 256; |
| 119 | break; |
| 120 | default: |
| 121 | break; |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | * At times BMP file may have padding data between its header section and the |
| 126 | * data section. |
| 127 | */ |
| 128 | if (header->ImageOffset - sizeof(efi_bmp_image_header) < |
| 129 | sizeof(efi_bmp_color_map) * col_map_number) |
| 130 | return 0; |
| 131 | |
| 132 | return col_map_number; |
| 133 | } |
| 134 | |
| 135 | /* Fill BMP image into BLT buffer format */ |
| 136 | static void *fill_blt_buffer(efi_bmp_image_header *header, |
| 137 | uint32_t logo_ptr, uint32_t blt_buffer_size) |
| 138 | { |
| 139 | efi_graphics_output_blt_pixel *gop_blt_buffer; |
| 140 | efi_graphics_output_blt_pixel *gop_blt_ptr; |
| 141 | efi_graphics_output_blt_pixel *gop_blt; |
| 142 | uint8_t *bmp_image; |
| 143 | uint8_t *bmp_image_header; |
| 144 | efi_bmp_color_map *bmp_color_map; |
| 145 | size_t image_index; |
| 146 | |
| 147 | if (header == NULL) |
| 148 | return NULL; |
| 149 | |
| 150 | gop_blt_ptr = malloc(sizeof(blt_buffer_size)); |
| 151 | if (!gop_blt_ptr) |
| 152 | die("%s: out of memory. Consider increasing the `CONFIG_HEAP_SIZE`\n", |
| 153 | __func__); |
| 154 | |
| 155 | bmp_image = ((UINT8 *)logo_ptr) + header->ImageOffset; |
| 156 | bmp_image_header = bmp_image; |
| 157 | gop_blt_buffer = gop_blt_ptr; |
| 158 | bmp_color_map = (efi_bmp_color_map *)(logo_ptr + sizeof(efi_bmp_image_header)); |
| 159 | |
| 160 | for (size_t height = 0; height < header->PixelHeight; height++) { |
| 161 | gop_blt = &gop_blt_buffer[(header->PixelHeight - height - 1) * |
| 162 | header->PixelWidth]; |
| 163 | for (size_t width = 0; width < header->PixelWidth; width++, bmp_image++, |
| 164 | gop_blt++) { |
| 165 | size_t index = 0; |
| 166 | switch (header->BitPerPixel) { |
| 167 | /* Translate 1-bit (2 colors) BMP to 24-bit color */ |
| 168 | case 1: |
| 169 | for (index = 0; index < 8 && width < header->PixelWidth; index++) { |
| 170 | gop_blt->Red = bmp_color_map[((*bmp_image) >> (7 - index)) & 0x1].Red; |
| 171 | gop_blt->Green = bmp_color_map[((*bmp_image) >> (7 - index)) & 0x1].Green; |
| 172 | gop_blt->Blue = bmp_color_map[((*bmp_image) >> (7 - index)) & 0x1].Blue; |
| 173 | gop_blt++; |
| 174 | width++; |
| 175 | } |
| 176 | gop_blt--; |
| 177 | width--; |
| 178 | break; |
| 179 | |
| 180 | /* Translate 4-bit (16 colors) BMP Palette to 24-bit color */ |
| 181 | case 4: |
| 182 | index = (*bmp_image) >> 4; |
| 183 | gop_blt->Red = bmp_color_map[index].Red; |
| 184 | gop_blt->Green = bmp_color_map[index].Green; |
| 185 | gop_blt->Blue = bmp_color_map[index].Blue; |
| 186 | if (width < (header->PixelWidth - 1)) { |
| 187 | gop_blt++; |
| 188 | width++; |
| 189 | index = (*bmp_image) & 0x0f; |
| 190 | gop_blt->Red = bmp_color_map[index].Red; |
| 191 | gop_blt->Green = bmp_color_map[index].Green; |
| 192 | gop_blt->Blue = bmp_color_map[index].Blue; |
| 193 | } |
| 194 | break; |
| 195 | |
| 196 | /* Translate 8-bit (256 colors) BMP Palette to 24-bit color */ |
| 197 | case 8: |
| 198 | gop_blt->Red = bmp_color_map[*bmp_image].Red; |
| 199 | gop_blt->Green = bmp_color_map[*bmp_image].Green; |
| 200 | gop_blt->Blue = bmp_color_map[*bmp_image].Blue; |
| 201 | break; |
| 202 | |
| 203 | /* For 24-bit BMP */ |
| 204 | case 24: |
| 205 | gop_blt->Blue = *bmp_image++; |
| 206 | gop_blt->Green = *bmp_image++; |
| 207 | gop_blt->Red = *bmp_image; |
| 208 | break; |
| 209 | |
Martin Roth | 74f1877 | 2023-09-03 21:38:29 -0600 | [diff] [blame^] | 210 | /* Convert 32 bit to 24bit bmp - just ignore the final byte of each pixel */ |
Subrata Banik | 7bc92f0 | 2023-08-03 10:11:28 +0000 | [diff] [blame] | 211 | case 32: |
| 212 | gop_blt->Blue = *bmp_image++; |
| 213 | gop_blt->Green = *bmp_image++; |
| 214 | gop_blt->Red = *bmp_image++; |
| 215 | break; |
| 216 | |
| 217 | /* Other bit format of BMP is not supported. */ |
| 218 | default: |
| 219 | free(gop_blt_ptr); |
| 220 | gop_blt_ptr = NULL; |
| 221 | |
| 222 | printk(BIOS_ERR, "%s, BMP Bit format not supported. 0x%X\n", __func__, |
| 223 | header->BitPerPixel); |
| 224 | return NULL; |
| 225 | } |
| 226 | } |
| 227 | image_index = (uintptr_t)bmp_image - (uintptr_t)bmp_image_header; |
| 228 | /* Each row in BMP Image should be 4-byte align */ |
| 229 | if ((image_index % 4) != 0) |
| 230 | bmp_image = bmp_image + (4 - (image_index % 4)); |
| 231 | } |
| 232 | |
| 233 | return gop_blt_ptr; |
| 234 | } |
| 235 | |
| 236 | /* Convert a *.BMP graphics image to a GOP blt buffer */ |
| 237 | void fsp_convert_bmp_to_gop_blt(uint32_t *logo, uint32_t *logo_size, |
| 238 | uint32_t *blt_ptr, uint32_t *blt_size, |
| 239 | uint32_t *pixel_height, uint32_t *pixel_width) |
| 240 | { |
| 241 | uint32_t logo_ptr, logo_ptr_size, blt_buffer_size; |
| 242 | efi_bmp_image_header *bmp_header; |
| 243 | |
| 244 | if (!logo || !logo_size || !blt_ptr || !blt_size || !pixel_height || !pixel_width) |
| 245 | return; |
| 246 | |
| 247 | bmp_load_logo(&logo_ptr, &logo_ptr_size); |
| 248 | if (!logo_ptr || logo_ptr_size < sizeof(efi_bmp_image_header)) { |
| 249 | printk(BIOS_ERR, "%s: BMP Image size is too small.\n", __func__); |
| 250 | return; |
| 251 | } |
| 252 | |
| 253 | bmp_header = (efi_bmp_image_header *)logo_ptr; |
| 254 | if (!do_bmp_image_authentication(bmp_header) || (bmp_header->Size != logo_ptr_size)) |
| 255 | return; |
| 256 | |
| 257 | blt_buffer_size = calculate_blt_buffer_size(bmp_header); |
| 258 | if (!blt_buffer_size) |
| 259 | return; |
| 260 | |
| 261 | if (!get_color_map_num(bmp_header)) |
| 262 | return; |
| 263 | |
| 264 | *logo = logo_ptr; |
| 265 | *logo_size = logo_ptr_size; |
| 266 | *blt_size = blt_buffer_size; |
| 267 | *pixel_height = bmp_header->PixelHeight; |
| 268 | *pixel_width = bmp_header->PixelWidth; |
| 269 | *blt_ptr = (uint32_t)fill_blt_buffer(bmp_header, logo_ptr, blt_buffer_size); |
| 270 | } |