]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/splash.c
Merge pull request #7388 from keszybz/doc-tweak
[thirdparty/systemd.git] / src / boot / efi / splash.c
1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation; either version 2.1 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
13 * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
14 */
15
16 #include <efi.h>
17 #include <efilib.h>
18
19 #include "graphics.h"
20 #include "splash.h"
21 #include "util.h"
22
23 struct bmp_file {
24 CHAR8 signature[2];
25 UINT32 size;
26 UINT16 reserved[2];
27 UINT32 offset;
28 } __attribute__((packed));
29
30 /* we require at least BITMAPINFOHEADER, later versions are
31 accepted, but their features ignored */
32 struct bmp_dib {
33 UINT32 size;
34 UINT32 x;
35 UINT32 y;
36 UINT16 planes;
37 UINT16 depth;
38 UINT32 compression;
39 UINT32 image_size;
40 INT32 x_pixel_meter;
41 INT32 y_pixel_meter;
42 UINT32 colors_used;
43 UINT32 colors_important;
44 } __attribute__((packed));
45
46 struct bmp_map {
47 UINT8 blue;
48 UINT8 green;
49 UINT8 red;
50 UINT8 reserved;
51 } __attribute__((packed));
52
53 EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
54 struct bmp_map **ret_map, UINT8 **pixmap) {
55 struct bmp_file *file;
56 struct bmp_dib *dib;
57 struct bmp_map *map;
58 UINTN row_size;
59
60 if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
61 return EFI_INVALID_PARAMETER;
62
63 /* check file header */
64 file = (struct bmp_file *)bmp;
65 if (file->signature[0] != 'B' || file->signature[1] != 'M')
66 return EFI_INVALID_PARAMETER;
67 if (file->size != size)
68 return EFI_INVALID_PARAMETER;
69 if (file->size < file->offset)
70 return EFI_INVALID_PARAMETER;
71
72 /* check device-independent bitmap */
73 dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file));
74 if (dib->size < sizeof(struct bmp_dib))
75 return EFI_UNSUPPORTED;
76
77 switch (dib->depth) {
78 case 1:
79 case 4:
80 case 8:
81 case 24:
82 if (dib->compression != 0)
83 return EFI_UNSUPPORTED;
84
85 break;
86
87 case 16:
88 case 32:
89 if (dib->compression != 0 && dib->compression != 3)
90 return EFI_UNSUPPORTED;
91
92 break;
93
94 default:
95 return EFI_UNSUPPORTED;
96 }
97
98 row_size = ((UINTN) dib->depth * dib->x + 31) / 32 * 4;
99 if (file->size - file->offset < dib->y * row_size)
100 return EFI_INVALID_PARAMETER;
101 if (row_size * dib->y > 64 * 1024 * 1024)
102 return EFI_INVALID_PARAMETER;
103
104 /* check color table */
105 map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size);
106 if (file->offset < sizeof(struct bmp_file) + dib->size)
107 return EFI_INVALID_PARAMETER;
108
109 if (file->offset > sizeof(struct bmp_file) + dib->size) {
110 UINT32 map_count;
111 UINTN map_size;
112
113 if (dib->colors_used)
114 map_count = dib->colors_used;
115 else {
116 switch (dib->depth) {
117 case 1:
118 case 4:
119 case 8:
120 map_count = 1 << dib->depth;
121 break;
122
123 default:
124 map_count = 0;
125 break;
126 }
127 }
128
129 map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
130 if (map_size != sizeof(struct bmp_map) * map_count)
131 return EFI_INVALID_PARAMETER;
132 }
133
134 *ret_map = map;
135 *ret_dib = dib;
136 *pixmap = bmp + file->offset;
137
138 return EFI_SUCCESS;
139 }
140
141 static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
142 UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
143
144 alpha = (source & 0xff);
145
146 /* convert src from RGBA to XRGB */
147 src = source >> 8;
148
149 /* decompose into RB and G components */
150 src_rb = (src & 0xff00ff);
151 src_g = (src & 0x00ff00);
152
153 dst_rb = (*dst & 0xff00ff);
154 dst_g = (*dst & 0x00ff00);
155
156 /* blend */
157 rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff;
158 g = ((((src_g - dst_g) * alpha + 0x008000) >> 8) + dst_g) & 0x00ff00;
159
160 *dst = (rb | g);
161 }
162
163 EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
164 struct bmp_dib *dib, struct bmp_map *map,
165 UINT8 *pixmap) {
166 UINT8 *in;
167 UINTN y;
168
169 /* transform and copy pixels */
170 in = pixmap;
171 for (y = 0; y < dib->y; y++) {
172 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out;
173 UINTN row_size;
174 UINTN x;
175
176 out = &buf[(dib->y - y - 1) * dib->x];
177 for (x = 0; x < dib->x; x++, in++, out++) {
178 switch (dib->depth) {
179 case 1: {
180 UINTN i;
181
182 for (i = 0; i < 8 && x < dib->x; i++) {
183 out->Red = map[((*in) >> (7 - i)) & 1].red;
184 out->Green = map[((*in) >> (7 - i)) & 1].green;
185 out->Blue = map[((*in) >> (7 - i)) & 1].blue;
186 out++;
187 x++;
188 }
189 out--;
190 x--;
191 break;
192 }
193
194 case 4: {
195 UINTN i;
196
197 i = (*in) >> 4;
198 out->Red = map[i].red;
199 out->Green = map[i].green;
200 out->Blue = map[i].blue;
201 if (x < (dib->x - 1)) {
202 out++;
203 x++;
204 i = (*in) & 0x0f;
205 out->Red = map[i].red;
206 out->Green = map[i].green;
207 out->Blue = map[i].blue;
208 }
209 break;
210 }
211
212 case 8:
213 out->Red = map[*in].red;
214 out->Green = map[*in].green;
215 out->Blue = map[*in].blue;
216 break;
217
218 case 16: {
219 UINT16 i = *(UINT16 *) in;
220
221 out->Red = (i & 0x7c00) >> 7;
222 out->Green = (i & 0x3e0) >> 2;
223 out->Blue = (i & 0x1f) << 3;
224 in += 1;
225 break;
226 }
227
228 case 24:
229 out->Red = in[2];
230 out->Green = in[1];
231 out->Blue = in[0];
232 in += 2;
233 break;
234
235 case 32: {
236 UINT32 i = *(UINT32 *) in;
237
238 pixel_blend((UINT32 *)out, i);
239
240 in += 3;
241 break;
242 }
243 }
244 }
245
246 /* add row padding; new lines always start at 32 bit boundary */
247 row_size = in - pixmap;
248 in += ((row_size + 3) & ~3) - row_size;
249 }
250
251 return EFI_SUCCESS;
252 }
253
254 EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) {
255 EFI_GRAPHICS_OUTPUT_BLT_PIXEL pixel = {};
256 EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
257 EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
258 struct bmp_dib *dib;
259 struct bmp_map *map;
260 UINT8 *pixmap;
261 UINT64 blt_size;
262 VOID *blt = NULL;
263 UINTN x_pos = 0;
264 UINTN y_pos = 0;
265 EFI_STATUS err;
266
267 if (!background) {
268 if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
269 pixel.Red = 0xc0;
270 pixel.Green = 0xc0;
271 pixel.Blue = 0xc0;
272 }
273 background = &pixel;
274 }
275
276 err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
277 if (EFI_ERROR(err))
278 return err;
279
280 err = bmp_parse_header(content, len, &dib, &map, &pixmap);
281 if (EFI_ERROR(err))
282 goto err;
283
284 if (dib->x < GraphicsOutput->Mode->Info->HorizontalResolution)
285 x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - dib->x) / 2;
286 if (dib->y < GraphicsOutput->Mode->Info->VerticalResolution)
287 y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - dib->y) / 2;
288
289 uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
290 (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)background,
291 EfiBltVideoFill, 0, 0, 0, 0,
292 GraphicsOutput->Mode->Info->HorizontalResolution,
293 GraphicsOutput->Mode->Info->VerticalResolution, 0);
294
295 /* EFI buffer */
296 blt_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
297 blt = AllocatePool(blt_size);
298 if (!blt)
299 return EFI_OUT_OF_RESOURCES;
300
301 err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
302 blt, EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0,
303 dib->x, dib->y, 0);
304 if (EFI_ERROR(err))
305 goto err;
306
307 err = bmp_to_blt(blt, dib, map, pixmap);
308 if (EFI_ERROR(err))
309 goto err;
310
311 err = graphics_mode(TRUE);
312 if (EFI_ERROR(err))
313 goto err;
314
315 err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
316 blt, EfiBltBufferToVideo, 0, 0, x_pos, y_pos,
317 dib->x, dib->y, 0);
318 err:
319 FreePool(blt);
320 return err;
321 }