]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DisplayUpdateProgressLibGraphics/DisplayUpdateProgressLibGraphics.c
83053464e06eb7531708c2b81b79f3c38e4a9fe8
[mirror_edk2.git] / MdeModulePkg / Library / DisplayUpdateProgressLibGraphics / DisplayUpdateProgressLibGraphics.c
1 /** @file
2 Provides services to display completion progress of a firmware update on a
3 graphical console that supports the Graphics Output Protocol.
4
5 Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>
6 Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include <PiDxe.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/MemoryAllocationLib.h>
15 #include <Library/UefiBootServicesTableLib.h>
16 #include <Library/DebugLib.h>
17 #include <Library/BaseLib.h>
18 #include <Library/UefiLib.h>
19
20 #include <Protocol/GraphicsOutput.h>
21 #include <Protocol/BootLogo2.h>
22
23 //
24 // Values in percent of of logo height.
25 //
26 #define LOGO_BOTTOM_PADDING 20
27 #define PROGRESS_BLOCK_HEIGHT 10
28
29 //
30 // Graphics Output Protocol instance to display progress bar
31 //
32 EFI_GRAPHICS_OUTPUT_PROTOCOL *mGop = NULL;
33
34 //
35 // Set to 100 percent so it is reset on first call.
36 //
37 UINTN mPreviousProgress = 100;
38
39 //
40 // Display coordinates for the progress bar.
41 //
42 UINTN mStartX = 0;
43 UINTN mStartY = 0;
44
45 //
46 // Width and height of the progress bar.
47 //
48 UINTN mBlockWidth = 0;
49 UINTN mBlockHeight = 0;
50
51 //
52 // GOP bitmap of the progress bar. Initialized on every new progress of 100%
53 //
54 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mBlockBitmap;
55
56 //
57 // GOP bitmap of the progress bar backround. Initialized once.
58 //
59 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mProgressBarBackground;
60
61 //
62 // Default mask used to detect the left, right , top, and bottom of logo. Only
63 // green and blue pixels are used for logo detection.
64 //
65 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mLogoDetectionColorMask = {
66 {
67 0xFF, // Blue
68 0xFF, // Green
69 0x00, // Red
70 0x00 // Reserved
71 }
72 };
73
74 //
75 // Background color of progress bar. Grey.
76 //
77 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mProgressBarBackgroundColor = {
78 {
79 0x80, // Blue
80 0x80, // Green
81 0x80, // Red
82 0x00 // Reserved
83 }
84 };
85
86 //
87 // Default color of progress completion. White.
88 //
89 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mProgressBarDefaultColor = {
90 {
91 0xFF, // Blue
92 0xFF, // Green
93 0xFF, // Red
94 0x00 // Reserved
95 }
96 };
97
98 //
99 // Set to TRUE if a valid Graphics Output Protocol is found and the progress
100 // bar fits under the boot logo using the current graphics mode.
101 //
102 BOOLEAN mGraphicsGood = FALSE;
103
104 /**
105 Internal function used to find the bounds of the white logo (on black or
106 red background).
107
108 These bounds are then computed to find the block size, 0%, 100%, etc.
109
110 **/
111 VOID
112 FindDim (
113 VOID
114 )
115 {
116 EFI_STATUS Status;
117 INTN LogoX;
118 INTN LogoStartX;
119 INTN LogoEndX;
120 INTN LogoY;
121 INTN LogoStartY;
122 INTN LogoEndY;
123 UINTN OffsetX; // Logo screen coordinate
124 UINTN OffsetY; // Logo screen coordinate
125 UINTN Width; // Width of logo in pixels
126 UINTN Height; // Height of logo in pixels
127 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Logo;
128 EDKII_BOOT_LOGO2_PROTOCOL *BootLogo;
129 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *Pixel;
130
131 Logo = NULL;
132 BootLogo = NULL;
133
134 //
135 // Return if a Graphics Output Protocol ha snot been found.
136 //
137 if (mGop == NULL) {
138 DEBUG ((DEBUG_ERROR, "No GOP found. No progress bar support. \n"));
139 return;
140 }
141
142 //
143 // Get boot logo protocol so we know where on the screen to grab
144 //
145 Status = gBS->LocateProtocol (
146 &gEdkiiBootLogo2ProtocolGuid,
147 NULL,
148 (VOID **)&BootLogo
149 );
150 if ((BootLogo == NULL) || (EFI_ERROR (Status))) {
151 DEBUG ((DEBUG_ERROR, "Failed to locate gEdkiiBootLogo2ProtocolGuid. No Progress bar support. \n", Status));
152 return;
153 }
154
155 //
156 // Get logo location and size
157 //
158 Status = BootLogo->GetBootLogo (
159 BootLogo,
160 &Logo,
161 &OffsetX,
162 &OffsetY,
163 &Width,
164 &Height
165 );
166 if (EFI_ERROR (Status)) {
167 DEBUG ((DEBUG_ERROR, "Failed to Get Boot Logo Status = %r. No Progress bar support. \n", Status));
168 return;
169 }
170
171 //
172 // Within logo buffer find where the actual logo starts/ends
173 //
174 LogoEndX = 0;
175 LogoEndY = 0;
176
177 //
178 // Find left side of logo in logo coordinates
179 //
180 for (LogoX = 0, LogoStartX = Width; LogoX < LogoStartX; LogoX++) {
181 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + LogoX);
182 for (LogoY = 0; LogoY < (INTN)Height; LogoY++) {
183 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
184 LogoStartX = LogoX;
185 //
186 // For loop searches from right side back to this column.
187 //
188 LogoEndX = LogoX;
189 DEBUG ((DEBUG_INFO, "StartX found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
190 break;
191 }
192
193 Pixel = Pixel + Width;
194 }
195 }
196
197 //
198 // Find right side of logo
199 //
200 for (LogoX = Width - 1; LogoX >= LogoEndX; LogoX--) {
201 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + LogoX);
202 for (LogoY = 0; LogoY < (INTN)Height; LogoY++) {
203 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
204 LogoEndX = LogoX;
205 DEBUG ((DEBUG_INFO, "EndX found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
206 break;
207 }
208
209 Pixel = Pixel + Width;
210 }
211 }
212
213 //
214 // Compute mBlockWidth
215 //
216 mBlockWidth = ((LogoEndX - LogoStartX) + 99) / 100;
217
218 //
219 // Adjust mStartX based on block width so it is centered under logo
220 //
221 mStartX = LogoStartX + OffsetX - (((mBlockWidth * 100) - (LogoEndX - LogoStartX)) / 2);
222 DEBUG ((DEBUG_INFO, "mBlockWidth set to 0x%X\n", mBlockWidth));
223 DEBUG ((DEBUG_INFO, "mStartX set to 0x%X\n", mStartX));
224
225 //
226 // Find the top of the logo
227 //
228 for (LogoY = 0, LogoStartY = Height; LogoY < LogoStartY; LogoY++) {
229 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + (Width * LogoY));
230 for (LogoX = 0; LogoX < (INTN)Width; LogoX++) {
231 // not black or red
232 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
233 LogoStartY = LogoY;
234 LogoEndY = LogoY; // for next loop will search from bottom side back to this row.
235 DEBUG ((DEBUG_INFO, "StartY found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
236 break;
237 }
238
239 Pixel++;
240 }
241 }
242
243 //
244 // Find the bottom of the logo
245 //
246 for (LogoY = Height - 1; LogoY >= LogoEndY; LogoY--) {
247 Pixel = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *)(Logo + (Width * LogoY));
248 for (LogoX = 0; LogoX < (INTN)Width; LogoX++) {
249 if ((Pixel->Raw & mLogoDetectionColorMask.Raw) != 0x0) {
250 LogoEndY = LogoY;
251 DEBUG ((DEBUG_INFO, "EndY found at (%d, %d) Color is: 0x%X \n", LogoX, LogoY, Pixel->Raw));
252 break;
253 }
254
255 Pixel++;
256 }
257 }
258
259 //
260 // Compute bottom padding (distance between logo bottom and progress bar)
261 //
262 mStartY = (((LogoEndY - LogoStartY) * LOGO_BOTTOM_PADDING) / 100) + LogoEndY + OffsetY;
263
264 //
265 // Compute progress bar height
266 //
267 mBlockHeight = (((LogoEndY - LogoStartY) * PROGRESS_BLOCK_HEIGHT) / 100);
268
269 DEBUG ((DEBUG_INFO, "mBlockHeight set to 0x%X\n", mBlockHeight));
270
271 //
272 // Create progress bar background (one time init).
273 //
274 mProgressBarBackground = AllocatePool (mBlockWidth * 100 * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
275 if (mProgressBarBackground == NULL) {
276 DEBUG ((DEBUG_ERROR, "Failed to allocate progress bar background\n"));
277 return;
278 }
279
280 //
281 // Fill the progress bar with the background color
282 //
283 SetMem32 (
284 mProgressBarBackground,
285 (mBlockWidth * 100 * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
286 mProgressBarBackgroundColor.Raw
287 );
288
289 //
290 // Allocate mBlockBitmap
291 //
292 mBlockBitmap = AllocatePool (mBlockWidth * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
293 if (mBlockBitmap == NULL) {
294 FreePool (mProgressBarBackground);
295 DEBUG ((DEBUG_ERROR, "Failed to allocate block\n"));
296 return;
297 }
298
299 //
300 // Check screen width and height and make sure it fits.
301 //
302 if ((mBlockHeight > Height) || (mBlockWidth > Width) || (mBlockHeight < 1) || (mBlockWidth < 1)) {
303 DEBUG ((DEBUG_ERROR, "DisplayUpdateProgressLib - Progress - Failed to get valid width and height.\n"));
304 DEBUG ((DEBUG_ERROR, "DisplayUpdateProgressLib - Progress - mBlockHeight: 0x%X mBlockWidth: 0x%X.\n", mBlockHeight, mBlockWidth));
305 FreePool (mProgressBarBackground);
306 FreePool (mBlockBitmap);
307 return;
308 }
309
310 mGraphicsGood = TRUE;
311 }
312
313 /**
314 Function indicates the current completion progress of a firmware update.
315 Platform may override with its own specific function.
316
317 @param[in] Completion A value between 0 and 100 indicating the current
318 completion progress of a firmware update. This
319 value must the the same or higher than previous
320 calls to this service. The first call of 0 or a
321 value of 0 after reaching a value of 100 resets
322 the progress indicator to 0.
323 @param[in] Color Color of the progress indicator. Only used when
324 Completion is 0 to set the color of the progress
325 indicator. If Color is NULL, then the default color
326 is used.
327
328 @retval EFI_SUCCESS Progress displayed successfully.
329 @retval EFI_INVALID_PARAMETER Completion is not in range 0..100.
330 @retval EFI_INVALID_PARAMETER Completion is less than Completion value from
331 a previous call to this service.
332 @retval EFI_NOT_READY The device used to indicate progress is not
333 available.
334 **/
335 EFI_STATUS
336 EFIAPI
337 DisplayUpdateProgress (
338 IN UINTN Completion,
339 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *Color OPTIONAL
340 )
341 {
342 EFI_STATUS Status;
343 UINTN PreX;
344 UINTN Index;
345
346 //
347 // Check range
348 //
349 if (Completion > 100) {
350 return EFI_INVALID_PARAMETER;
351 }
352
353 //
354 // Check to see if this Completion percentage has already been displayed
355 //
356 if (Completion == mPreviousProgress) {
357 return EFI_SUCCESS;
358 }
359
360 //
361 // Find Graphics Output Protocol if not already set. 1 time.
362 //
363 if (mGop == NULL) {
364 Status = gBS->HandleProtocol (
365 gST->ConsoleOutHandle,
366 &gEfiGraphicsOutputProtocolGuid,
367 (VOID **)&mGop
368 );
369 if (EFI_ERROR (Status)) {
370 Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&mGop);
371 if (EFI_ERROR (Status)) {
372 mGop = NULL;
373 DEBUG ((DEBUG_ERROR, "Show Progress Function could not locate GOP. Status = %r\n", Status));
374 return EFI_NOT_READY;
375 }
376 }
377
378 //
379 // Run once
380 //
381 FindDim ();
382 }
383
384 //
385 // Make sure a valid start, end, and size info are available (find the Logo)
386 //
387 if (!mGraphicsGood) {
388 DEBUG ((DEBUG_INFO, "Graphics Not Good. Not doing any onscreen visual display\n"));
389 return EFI_NOT_READY;
390 }
391
392 //
393 // Do special init on first call of each progress session
394 //
395 if (mPreviousProgress == 100) {
396 //
397 // Draw progress bar background
398 //
399 mGop->Blt (
400 mGop,
401 mProgressBarBackground,
402 EfiBltBufferToVideo,
403 0,
404 0,
405 mStartX,
406 mStartY,
407 (mBlockWidth * 100),
408 mBlockHeight,
409 0
410 );
411
412 DEBUG ((
413 DEBUG_VERBOSE,
414 "Color is 0x%X\n",
415 (Color == NULL) ? mProgressBarDefaultColor.Raw : Color->Raw
416 ));
417
418 //
419 // Update block bitmap with correct color
420 //
421 SetMem32 (
422 mBlockBitmap,
423 (mBlockWidth * mBlockHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
424 (Color == NULL) ? mProgressBarDefaultColor.Raw : Color->Raw
425 );
426
427 //
428 // Clear previous
429 //
430 mPreviousProgress = 0;
431 }
432
433 //
434 // Can not update progress bar if Completion is less than previous
435 //
436 if (Completion < mPreviousProgress) {
437 DEBUG ((DEBUG_WARN, "WARNING: Completion (%d) should not be lesss than Previous (%d)!!!\n", Completion, mPreviousProgress));
438 return EFI_INVALID_PARAMETER;
439 }
440
441 PreX = ((mPreviousProgress * mBlockWidth) + mStartX);
442 for (Index = 0; Index < (Completion - mPreviousProgress); Index++) {
443 //
444 // Show progress by coloring new area
445 //
446 mGop->Blt (
447 mGop,
448 mBlockBitmap,
449 EfiBltBufferToVideo,
450 0,
451 0,
452 PreX,
453 mStartY,
454 mBlockWidth,
455 mBlockHeight,
456 0
457 );
458 PreX += mBlockWidth;
459 }
460
461 mPreviousProgress = Completion;
462
463 return EFI_SUCCESS;
464 }