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