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 <stdlib.h>
8
is_bmp_image_valid(efi_bmp_image_header * header)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
is_bmp_image_compressed(efi_bmp_image_header * header)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
is_bitmap_format_supported(efi_bmp_image_header * header)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
do_bmp_image_authentication(efi_bmp_image_header * header)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
calculate_blt_buffer_size(efi_bmp_image_header * header)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
get_color_map_num(efi_bmp_image_header * header)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 */
fill_blt_buffer(efi_bmp_image_header * header,uintptr_t logo_ptr,size_t blt_buffer_size)135 static void *fill_blt_buffer(efi_bmp_image_header *header,
136 uintptr_t logo_ptr, size_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
154 bmp_image = ((uint8_t *)logo_ptr) + header->ImageOffset;
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
209 /* Convert 32 bit to 24bit bmp - just ignore the final byte of each pixel */
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 */
fsp_convert_bmp_to_gop_blt(efi_uintn_t * logo,uint32_t * logo_size,efi_uintn_t * blt_ptr,efi_uintn_t * blt_size,uint32_t * pixel_height,uint32_t * pixel_width)236 void fsp_convert_bmp_to_gop_blt(efi_uintn_t *logo, uint32_t *logo_size,
237 efi_uintn_t *blt_ptr, efi_uintn_t *blt_size,
238 uint32_t *pixel_height, uint32_t *pixel_width)
239 {
240 uintptr_t logo_ptr;
241 size_t 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 logo_ptr = (uintptr_t)bmp_load_logo(&logo_ptr_size);
248
249 if (!logo_ptr || logo_ptr_size < sizeof(efi_bmp_image_header)) {
250 printk(BIOS_ERR, "%s: BMP Image size is too small.\n", __func__);
251 return;
252 }
253
254 bmp_header = (efi_bmp_image_header *)logo_ptr;
255 if (!do_bmp_image_authentication(bmp_header) || (bmp_header->Size != logo_ptr_size))
256 return;
257
258 blt_buffer_size = calculate_blt_buffer_size(bmp_header);
259 if (!blt_buffer_size)
260 return;
261
262 if (!get_color_map_num(bmp_header))
263 return;
264
265 *logo = logo_ptr;
266 *logo_size = logo_ptr_size;
267 *blt_size = blt_buffer_size;
268 *pixel_height = bmp_header->PixelHeight;
269 *pixel_width = bmp_header->PixelWidth;
270 *blt_ptr = (uintptr_t)fill_blt_buffer(bmp_header, logo_ptr, blt_buffer_size);
271 }
272