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