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
));
193 Pixel
= Pixel
+ Width
;
198 // Find right side of logo
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) {
205 DEBUG ((DEBUG_INFO
, "EndX found at (%d, %d) Color is: 0x%X \n", LogoX
, LogoY
, Pixel
->Raw
));
209 Pixel
= Pixel
+ Width
;
214 // Compute mBlockWidth
216 mBlockWidth
= ((LogoEndX
- LogoStartX
) + 99) / 100;
219 // Adjust mStartX based on block width so it is centered under logo
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
));
226 // Find the top of the logo
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
++) {
232 if ((Pixel
->Raw
& mLogoDetectionColorMask
.Raw
) != 0x0) {
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
));
244 // Find the bottom of the logo
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) {
251 DEBUG ((DEBUG_INFO
, "EndY found at (%d, %d) Color is: 0x%X \n", LogoX
, LogoY
, Pixel
->Raw
));
260 // Compute bottom padding (distance between logo bottom and progress bar)
262 mStartY
= (((LogoEndY
- LogoStartY
) * LOGO_BOTTOM_PADDING
) / 100) + LogoEndY
+ OffsetY
;
265 // Compute progress bar height
267 mBlockHeight
= (((LogoEndY
- LogoStartY
) * PROGRESS_BLOCK_HEIGHT
) / 100);
269 DEBUG ((DEBUG_INFO
, "mBlockHeight set to 0x%X\n", mBlockHeight
));
272 // Create progress bar background (one time init).
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"));
281 // Fill the progress bar with the background color
284 mProgressBarBackground
,
285 (mBlockWidth
* 100 * mBlockHeight
* sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
)),
286 mProgressBarBackgroundColor
.Raw
290 // Allocate mBlockBitmap
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"));
300 // Check screen width and height and make sure it fits.
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
);
310 mGraphicsGood
= TRUE
;
314 Function indicates the current completion progress of a firmware update.
315 Platform may override with its own specific function.
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
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
337 DisplayUpdateProgress (
339 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION
*Color OPTIONAL
349 if (Completion
> 100) {
350 return EFI_INVALID_PARAMETER
;
354 // Check to see if this Completion percentage has already been displayed
356 if (Completion
== mPreviousProgress
) {
361 // Find Graphics Output Protocol if not already set. 1 time.
364 Status
= gBS
->HandleProtocol (
365 gST
->ConsoleOutHandle
,
366 &gEfiGraphicsOutputProtocolGuid
,
369 if (EFI_ERROR (Status
)) {
370 Status
= gBS
->LocateProtocol (&gEfiGraphicsOutputProtocolGuid
, NULL
, (VOID
**)&mGop
);
371 if (EFI_ERROR (Status
)) {
373 DEBUG ((DEBUG_ERROR
, "Show Progress Function could not locate GOP. Status = %r\n", Status
));
374 return EFI_NOT_READY
;
385 // Make sure a valid start, end, and size info are available (find the Logo)
387 if (!mGraphicsGood
) {
388 DEBUG ((DEBUG_INFO
, "Graphics Not Good. Not doing any onscreen visual display\n"));
389 return EFI_NOT_READY
;
393 // Do special init on first call of each progress session
395 if (mPreviousProgress
== 100) {
397 // Draw progress bar background
401 mProgressBarBackground
,
415 (Color
== NULL
) ? mProgressBarDefaultColor
.Raw
: Color
->Raw
419 // Update block bitmap with correct color
423 (mBlockWidth
* mBlockHeight
* sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
)),
424 (Color
== NULL
) ? mProgressBarDefaultColor
.Raw
: Color
->Raw
430 mPreviousProgress
= 0;
434 // Can not update progress bar if Completion is less than previous
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
;
441 PreX
= ((mPreviousProgress
* mBlockWidth
) + mStartX
);
442 for (Index
= 0; Index
< (Completion
- mPreviousProgress
); Index
++) {
444 // Show progress by coloring new area
461 mPreviousProgress
= Completion
;