2 Provides services to display completion progress of a firmware update on a
3 graphical console that supports the Graphics Output Protocol.
5 Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>
6 Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
20 #include <Protocol/GraphicsOutput.h>
21 #include <Protocol/BootLogo2.h>
24 // Values in percent of of logo height.
26 #define LOGO_BOTTOM_PADDING 20
27 #define PROGRESS_BLOCK_HEIGHT 10
30 // Graphics Output Protocol instance to display progress bar
32 EFI_GRAPHICS_OUTPUT_PROTOCOL
*mGop
= NULL
;
35 // Set to 100 percent so it is reset on first call.
37 UINTN mPreviousProgress
= 100;
40 // Display coordinates for the progress bar.
46 // Width and height of the progress bar.
48 UINTN mBlockWidth
= 0;
49 UINTN mBlockHeight
= 0;
52 // GOP bitmap of the progress bar. Initialized on every new progress of 100%
54 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*mBlockBitmap
;
57 // GOP bitmap of the progress bar backround. Initialized once.
59 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*mProgressBarBackground
;
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.
65 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mLogoDetectionColorMask
= {
75 // Background color of progress bar. Grey.
77 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mProgressBarBackgroundColor
= {
87 // Default color of progress completion. White.
89 const EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION mProgressBarDefaultColor
= {
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.
102 BOOLEAN mGraphicsGood
= FALSE
;
105 Internal function used to find the bounds of the white logo (on black or
108 These bounds are then computed to find the block size, 0%, 100%, etc.
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
;
135 // Return if a Graphics Output Protocol ha snot been found.
138 DEBUG ((DEBUG_ERROR
, "No GOP found. No progress bar support. \n"));
143 // Get boot logo protocol so we know where on the screen to grab
145 Status
= gBS
->LocateProtocol (
146 &gEdkiiBootLogo2ProtocolGuid
,
150 if ((BootLogo
== NULL
) || (EFI_ERROR (Status
))) {
151 DEBUG ((DEBUG_ERROR
, "Failed to locate gEdkiiBootLogo2ProtocolGuid. No Progress bar support. \n", Status
));
156 // Get logo location and size
158 Status
= BootLogo
->GetBootLogo (
166 if (EFI_ERROR (Status
)) {
167 DEBUG ((DEBUG_ERROR
, "Failed to Get Boot Logo Status = %r. No Progress bar support. \n", Status
));
172 // Within logo buffer find where the actual logo starts/ends
178 // Find left side of logo in logo coordinates
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) {
186 // For loop searches from right side back to this column.
189 DEBUG ((DEBUG_INFO
, "StartX found at (%d, %d) Color is: 0x%X \n", LogoX
, LogoY
, Pixel
->Raw
));
192 Pixel
= Pixel
+ Width
;
197 // Find right side of logo
199 for (LogoX
= Width
- 1; LogoX
>= LogoEndX
; LogoX
--) {
200 Pixel
= (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION
*)(Logo
+ LogoX
);
201 for (LogoY
= 0; LogoY
< (INTN
)Height
; LogoY
++) {
202 if ((Pixel
->Raw
& mLogoDetectionColorMask
.Raw
) != 0x0) {
204 DEBUG ((DEBUG_INFO
, "EndX found at (%d, %d) Color is: 0x%X \n", LogoX
, LogoY
, Pixel
->Raw
));
207 Pixel
= Pixel
+ Width
;
212 // Compute mBlockWidth
214 mBlockWidth
= ((LogoEndX
- LogoStartX
) + 99) / 100;
217 // Adjust mStartX based on block width so it is centered under logo
219 mStartX
= LogoStartX
+ OffsetX
- (((mBlockWidth
* 100) - (LogoEndX
- LogoStartX
)) / 2);
220 DEBUG ((DEBUG_INFO
, "mBlockWidth set to 0x%X\n", mBlockWidth
));
221 DEBUG ((DEBUG_INFO
, "mStartX set to 0x%X\n", mStartX
));
224 // Find the top of the logo
226 for (LogoY
= 0, LogoStartY
= Height
; LogoY
< LogoStartY
; LogoY
++) {
227 Pixel
= (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION
*)(Logo
+ (Width
* LogoY
));
228 for (LogoX
= 0; LogoX
< (INTN
)Width
; LogoX
++) {
230 if ((Pixel
->Raw
& mLogoDetectionColorMask
.Raw
) != 0x0) {
232 LogoEndY
= LogoY
; //for next loop will search from bottom side back to this row.
233 DEBUG ((DEBUG_INFO
, "StartY found at (%d, %d) Color is: 0x%X \n", LogoX
, LogoY
, Pixel
->Raw
));
241 // Find the bottom of the logo
243 for (LogoY
= Height
- 1; LogoY
>= LogoEndY
; LogoY
--) {
244 Pixel
= (EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION
*)(Logo
+ (Width
* LogoY
));
245 for (LogoX
= 0; LogoX
< (INTN
)Width
; LogoX
++) {
246 if ((Pixel
->Raw
& mLogoDetectionColorMask
.Raw
) != 0x0) {
248 DEBUG ((DEBUG_INFO
, "EndY found at (%d, %d) Color is: 0x%X \n", LogoX
, LogoY
, Pixel
->Raw
));
256 // Compute bottom padding (distance between logo bottom and progress bar)
258 mStartY
= (((LogoEndY
- LogoStartY
) * LOGO_BOTTOM_PADDING
) / 100) + LogoEndY
+ OffsetY
;
261 // Compute progress bar height
263 mBlockHeight
= (((LogoEndY
- LogoStartY
) * PROGRESS_BLOCK_HEIGHT
) / 100);
265 DEBUG ((DEBUG_INFO
, "mBlockHeight set to 0x%X\n", mBlockHeight
));
268 // Create progress bar background (one time init).
270 mProgressBarBackground
= AllocatePool (mBlockWidth
* 100 * mBlockHeight
* sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
));
271 if (mProgressBarBackground
== NULL
) {
272 DEBUG ((DEBUG_ERROR
, "Failed to allocate progress bar background\n"));
277 // Fill the progress bar with the background color
280 mProgressBarBackground
,
281 (mBlockWidth
* 100 * mBlockHeight
* sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
)),
282 mProgressBarBackgroundColor
.Raw
286 // Allocate mBlockBitmap
288 mBlockBitmap
= AllocatePool (mBlockWidth
* mBlockHeight
* sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
));
289 if (mBlockBitmap
== NULL
) {
290 FreePool (mProgressBarBackground
);
291 DEBUG ((DEBUG_ERROR
, "Failed to allocate block\n"));
296 // Check screen width and height and make sure it fits.
298 if ((mBlockHeight
> Height
) || (mBlockWidth
> Width
) || (mBlockHeight
< 1) || (mBlockWidth
< 1)) {
299 DEBUG ((DEBUG_ERROR
, "DisplayUpdateProgressLib - Progress - Failed to get valid width and height.\n"));
300 DEBUG ((DEBUG_ERROR
, "DisplayUpdateProgressLib - Progress - mBlockHeight: 0x%X mBlockWidth: 0x%X.\n", mBlockHeight
, mBlockWidth
));
301 FreePool (mProgressBarBackground
);
302 FreePool (mBlockBitmap
);
306 mGraphicsGood
= TRUE
;
310 Function indicates the current completion progress of a firmware update.
311 Platform may override with its own specific function.
313 @param[in] Completion A value between 0 and 100 indicating the current
314 completion progress of a firmware update. This
315 value must the the same or higher than previous
316 calls to this service. The first call of 0 or a
317 value of 0 after reaching a value of 100 resets
318 the progress indicator to 0.
319 @param[in] Color Color of the progress indicator. Only used when
320 Completion is 0 to set the color of the progress
321 indicator. If Color is NULL, then the default color
324 @retval EFI_SUCCESS Progress displayed successfully.
325 @retval EFI_INVALID_PARAMETER Completion is not in range 0..100.
326 @retval EFI_INVALID_PARAMETER Completion is less than Completion value from
327 a previous call to this service.
328 @retval EFI_NOT_READY The device used to indicate progress is not
333 DisplayUpdateProgress (
335 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION
*Color OPTIONAL
345 if (Completion
> 100) {
346 return EFI_INVALID_PARAMETER
;
350 // Check to see if this Completion percentage has already been displayed
352 if (Completion
== mPreviousProgress
) {
357 // Find Graphics Output Protocol if not already set. 1 time.
360 Status
= gBS
->HandleProtocol (
361 gST
->ConsoleOutHandle
,
362 &gEfiGraphicsOutputProtocolGuid
,
365 if (EFI_ERROR (Status
)) {
366 Status
= gBS
->LocateProtocol (&gEfiGraphicsOutputProtocolGuid
, NULL
, (VOID
**)&mGop
);
367 if (EFI_ERROR (Status
)) {
369 DEBUG ((DEBUG_ERROR
, "Show Progress Function could not locate GOP. Status = %r\n", Status
));
370 return EFI_NOT_READY
;
381 // Make sure a valid start, end, and size info are available (find the Logo)
383 if (!mGraphicsGood
) {
384 DEBUG ((DEBUG_INFO
, "Graphics Not Good. Not doing any onscreen visual display\n"));
385 return EFI_NOT_READY
;
389 // Do special init on first call of each progress session
391 if (mPreviousProgress
== 100) {
393 // Draw progress bar background
397 mProgressBarBackground
,
408 DEBUG ((DEBUG_VERBOSE
, "Color is 0x%X\n",
409 (Color
== NULL
) ? mProgressBarDefaultColor
.Raw
: Color
->Raw
413 // Update block bitmap with correct color
417 (mBlockWidth
* mBlockHeight
* sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
)),
418 (Color
== NULL
) ? mProgressBarDefaultColor
.Raw
: Color
->Raw
424 mPreviousProgress
= 0;
428 // Can not update progress bar if Completion is less than previous
430 if (Completion
< mPreviousProgress
) {
431 DEBUG ((DEBUG_WARN
, "WARNING: Completion (%d) should not be lesss than Previous (%d)!!!\n", Completion
, mPreviousProgress
));
432 return EFI_INVALID_PARAMETER
;
435 PreX
= ((mPreviousProgress
* mBlockWidth
) + mStartX
);
436 for (Index
= 0; Index
< (Completion
- mPreviousProgress
); Index
++) {
438 // Show progress by coloring new area
455 mPreviousProgress
= Completion
;