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