]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
OvmfPkg/PlatformBootManagerLib: fix PCI interrupt link (LNKx)
[mirror_edk2.git] / OvmfPkg / Library / PlatformBootManagerLib / BdsPlatform.c
CommitLineData
30541881
RN
1/** @file\r
2 Platform BDS customizations.\r
3\r
84f736a7 4 Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR>\r
b26f0cf9 5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
30541881
RN
6\r
7**/\r
8\r
9#include "BdsPlatform.h"\r
10#include <Guid/RootBridgesConnectedEventGroup.h>\r
2eb35898 11#include <Protocol/FirmwareVolume2.h>\r
a7488174 12#include <Library/PlatformBmPrintScLib.h>\r
8d65d3b2 13#include <Library/Tcg2PhysicalPresenceLib.h>\r
bc5df198 14#include <Library/XenPlatformLib.h>\r
30541881
RN
15\r
16\r
17//\r
18// Global data\r
19//\r
20\r
21VOID *mEfiDevPathNotifyReg;\r
22EFI_EVENT mEfiDevPathEvent;\r
23VOID *mEmuVariableEventReg;\r
24EFI_EVENT mEmuVariableEvent;\r
30541881
RN
25UINT16 mHostBridgeDevId;\r
26\r
27//\r
28// Table of host IRQs matching PCI IRQs A-D\r
29// (for configuring PCI Interrupt Line register)\r
30//\r
31CONST UINT8 PciHostIrqs[] = {\r
6573ae8c
BH
32 0x0a, // LNKA, LNKE\r
33 0x0a, // LNKB, LNKF\r
34 0x0b, // LNKC, LNKG\r
35 0x0b // LNKD, LNKH\r
30541881
RN
36};\r
37\r
30541881
RN
38//\r
39// Type definitions\r
40//\r
41\r
42typedef\r
43EFI_STATUS\r
44(EFIAPI *PROTOCOL_INSTANCE_CALLBACK)(\r
45 IN EFI_HANDLE Handle,\r
46 IN VOID *Instance,\r
47 IN VOID *Context\r
48 );\r
49\r
50/**\r
51 @param[in] Handle - Handle of PCI device instance\r
52 @param[in] PciIo - PCI IO protocol instance\r
53 @param[in] Pci - PCI Header register block\r
54**/\r
55typedef\r
56EFI_STATUS\r
57(EFIAPI *VISIT_PCI_INSTANCE_CALLBACK)(\r
58 IN EFI_HANDLE Handle,\r
59 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
60 IN PCI_TYPE00 *Pci\r
61 );\r
62\r
63\r
64//\r
65// Function prototypes\r
66//\r
67\r
68EFI_STATUS\r
69VisitAllInstancesOfProtocol (\r
70 IN EFI_GUID *Id,\r
71 IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,\r
72 IN VOID *Context\r
73 );\r
74\r
75EFI_STATUS\r
76VisitAllPciInstancesOfProtocol (\r
77 IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction\r
78 );\r
79\r
80VOID\r
81InstallDevicePathCallback (\r
82 VOID\r
83 );\r
84\r
07dd96e8
RN
85VOID\r
86PlatformRegisterFvBootOption (\r
87 EFI_GUID *FileGuid,\r
88 CHAR16 *Description,\r
89 UINT32 Attributes\r
90 )\r
91{\r
92 EFI_STATUS Status;\r
93 INTN OptionIndex;\r
94 EFI_BOOT_MANAGER_LOAD_OPTION NewOption;\r
95 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
96 UINTN BootOptionCount;\r
97 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;\r
98 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
99 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
100\r
101 Status = gBS->HandleProtocol (\r
102 gImageHandle,\r
103 &gEfiLoadedImageProtocolGuid,\r
104 (VOID **) &LoadedImage\r
105 );\r
106 ASSERT_EFI_ERROR (Status);\r
107\r
108 EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);\r
109 DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);\r
110 ASSERT (DevicePath != NULL);\r
111 DevicePath = AppendDevicePathNode (\r
112 DevicePath,\r
113 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
114 );\r
115 ASSERT (DevicePath != NULL);\r
116\r
117 Status = EfiBootManagerInitializeLoadOption (\r
118 &NewOption,\r
119 LoadOptionNumberUnassigned,\r
120 LoadOptionTypeBoot,\r
121 Attributes,\r
122 Description,\r
123 DevicePath,\r
124 NULL,\r
125 0\r
126 );\r
127 ASSERT_EFI_ERROR (Status);\r
128 FreePool (DevicePath);\r
129\r
130 BootOptions = EfiBootManagerGetLoadOptions (\r
131 &BootOptionCount, LoadOptionTypeBoot\r
132 );\r
133\r
134 OptionIndex = EfiBootManagerFindLoadOption (\r
135 &NewOption, BootOptions, BootOptionCount\r
136 );\r
137\r
138 if (OptionIndex == -1) {\r
139 Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);\r
140 ASSERT_EFI_ERROR (Status);\r
141 }\r
142 EfiBootManagerFreeLoadOption (&NewOption);\r
143 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
144}\r
145\r
2eb35898
LE
146/**\r
147 Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options\r
148 whose device paths do not resolve exactly to an FvFile in the system.\r
149\r
150 This removes any boot options that point to binaries built into the firmware\r
151 and have become stale due to any of the following:\r
152 - DXEFV's base address or size changed (historical),\r
153 - DXEFV's FvNameGuid changed,\r
154 - the FILE_GUID of the pointed-to binary changed,\r
155 - the referenced binary is no longer built into the firmware.\r
156\r
157 EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only\r
158 avoids exact duplicates.\r
159**/\r
160VOID\r
161RemoveStaleFvFileOptions (\r
162 VOID\r
163 )\r
164{\r
165 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
166 UINTN BootOptionCount;\r
167 UINTN Index;\r
168\r
169 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,\r
170 LoadOptionTypeBoot);\r
171\r
172 for (Index = 0; Index < BootOptionCount; ++Index) {\r
173 EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;\r
174 EFI_STATUS Status;\r
175 EFI_HANDLE FvHandle;\r
176\r
177 //\r
178 // If the device path starts with neither MemoryMapped(...) nor Fv(...),\r
179 // then keep the boot option.\r
180 //\r
181 Node1 = BootOptions[Index].FilePath;\r
182 if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&\r
183 DevicePathSubType (Node1) == HW_MEMMAP_DP) &&\r
184 !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&\r
185 DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {\r
186 continue;\r
187 }\r
188\r
189 //\r
190 // If the second device path node is not FvFile(...), then keep the boot\r
191 // option.\r
192 //\r
193 Node2 = NextDevicePathNode (Node1);\r
194 if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||\r
195 DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {\r
196 continue;\r
197 }\r
198\r
199 //\r
200 // Locate the Firmware Volume2 protocol instance that is denoted by the\r
201 // boot option. If this lookup fails (i.e., the boot option references a\r
202 // firmware volume that doesn't exist), then we'll proceed to delete the\r
203 // boot option.\r
204 //\r
205 SearchNode = Node1;\r
206 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,\r
207 &SearchNode, &FvHandle);\r
208\r
209 if (!EFI_ERROR (Status)) {\r
210 //\r
211 // The firmware volume was found; now let's see if it contains the FvFile\r
212 // identified by GUID.\r
213 //\r
214 EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;\r
215 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;\r
216 UINTN BufferSize;\r
217 EFI_FV_FILETYPE FoundType;\r
218 EFI_FV_FILE_ATTRIBUTES FileAttributes;\r
219 UINT32 AuthenticationStatus;\r
220\r
221 Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,\r
222 (VOID **)&FvProtocol);\r
223 ASSERT_EFI_ERROR (Status);\r
224\r
225 FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;\r
226 //\r
227 // Buffer==NULL means we request metadata only: BufferSize, FoundType,\r
228 // FileAttributes.\r
229 //\r
230 Status = FvProtocol->ReadFile (\r
231 FvProtocol,\r
232 &FvFileNode->FvFileName, // NameGuid\r
233 NULL, // Buffer\r
234 &BufferSize,\r
235 &FoundType,\r
236 &FileAttributes,\r
237 &AuthenticationStatus\r
238 );\r
239 if (!EFI_ERROR (Status)) {\r
240 //\r
241 // The FvFile was found. Keep the boot option.\r
242 //\r
243 continue;\r
244 }\r
245 }\r
246\r
247 //\r
248 // Delete the boot option.\r
249 //\r
250 Status = EfiBootManagerDeleteLoadOptionVariable (\r
251 BootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
252 DEBUG_CODE (\r
253 CHAR16 *DevicePathString;\r
254\r
255 DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,\r
256 FALSE, FALSE);\r
257 DEBUG ((\r
70d5086c 258 EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_VERBOSE,\r
2eb35898
LE
259 "%a: removing stale Boot#%04x %s: %r\n",\r
260 __FUNCTION__,\r
261 (UINT32)BootOptions[Index].OptionNumber,\r
262 DevicePathString == NULL ? L"<unavailable>" : DevicePathString,\r
263 Status\r
264 ));\r
265 if (DevicePathString != NULL) {\r
266 FreePool (DevicePathString);\r
267 }\r
268 );\r
269 }\r
270\r
271 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
272}\r
273\r
07dd96e8
RN
274VOID\r
275PlatformRegisterOptionsAndKeys (\r
276 VOID\r
277 )\r
278{\r
279 EFI_STATUS Status;\r
280 EFI_INPUT_KEY Enter;\r
281 EFI_INPUT_KEY F2;\r
282 EFI_INPUT_KEY Esc;\r
283 EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
284\r
285 //\r
286 // Register ENTER as CONTINUE key\r
287 //\r
288 Enter.ScanCode = SCAN_NULL;\r
289 Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;\r
290 Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);\r
291 ASSERT_EFI_ERROR (Status);\r
292\r
293 //\r
294 // Map F2 to Boot Manager Menu\r
295 //\r
296 F2.ScanCode = SCAN_F2;\r
297 F2.UnicodeChar = CHAR_NULL;\r
298 Esc.ScanCode = SCAN_ESC;\r
299 Esc.UnicodeChar = CHAR_NULL;\r
300 Status = EfiBootManagerGetBootManagerMenu (&BootOption);\r
301 ASSERT_EFI_ERROR (Status);\r
302 Status = EfiBootManagerAddKeyOptionVariable (\r
303 NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL\r
304 );\r
305 ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
306 Status = EfiBootManagerAddKeyOptionVariable (\r
307 NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL\r
308 );\r
309 ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
07dd96e8
RN
310}\r
311\r
30541881
RN
312EFI_STATUS\r
313EFIAPI\r
314ConnectRootBridge (\r
315 IN EFI_HANDLE RootBridgeHandle,\r
316 IN VOID *Instance,\r
317 IN VOID *Context\r
318 );\r
319\r
7ebad830
LE
320STATIC\r
321EFI_STATUS\r
322EFIAPI\r
323ConnectVirtioPciRng (\r
324 IN EFI_HANDLE Handle,\r
325 IN VOID *Instance,\r
326 IN VOID *Context\r
327 );\r
328\r
30541881
RN
329STATIC\r
330VOID\r
331SaveS3BootScript (\r
332 VOID\r
333 );\r
334\r
335//\r
336// BDS Platform Functions\r
337//\r
fe1b9e8e
LE
338/**\r
339 Do the platform init, can be customized by OEM/IBV\r
340\r
341 Possible things that can be done in PlatformBootManagerBeforeConsole:\r
342\r
343 > Update console variable: 1. include hot-plug devices;\r
344 > 2. Clear ConIn and add SOL for AMT\r
345 > Register new Driver#### or Boot####\r
346 > Register new Key####: e.g.: F12\r
347 > Signal ReadyToLock event\r
348 > Authentication action: 1. connect Auth devices;\r
349 > 2. Identify auto logon user.\r
350**/\r
30541881
RN
351VOID\r
352EFIAPI\r
a7566234 353PlatformBootManagerBeforeConsole (\r
30541881
RN
354 VOID\r
355 )\r
30541881 356{\r
579afd6b
LE
357 EFI_HANDLE Handle;\r
358 EFI_STATUS Status;\r
9b08c655 359 UINT16 FrontPageTimeout;\r
579afd6b 360 RETURN_STATUS PcdStatus;\r
30541881 361\r
70d5086c 362 DEBUG ((DEBUG_INFO, "PlatformBootManagerBeforeConsole\n"));\r
30541881
RN
363 InstallDevicePathCallback ();\r
364\r
365 VisitAllInstancesOfProtocol (&gEfiPciRootBridgeIoProtocolGuid,\r
366 ConnectRootBridge, NULL);\r
367\r
368 //\r
369 // Signal the ACPI platform driver that it can download QEMU ACPI tables.\r
370 //\r
371 EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);\r
372\r
373 //\r
374 // We can't signal End-of-Dxe earlier than this. Namely, End-of-Dxe triggers\r
375 // the preparation of S3 system information. That logic has a hard dependency\r
376 // on the presence of the FACS ACPI table. Since our ACPI tables are only\r
377 // installed after PCI enumeration completes, we must not trigger the S3 save\r
378 // earlier, hence we can't signal End-of-Dxe earlier.\r
379 //\r
380 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);\r
381\r
382 if (QemuFwCfgS3Enabled ()) {\r
383 //\r
384 // Save the boot script too. Note that this will require us to emit the\r
385 // DxeSmmReadyToLock event just below, which in turn locks down SMM.\r
386 //\r
387 SaveS3BootScript ();\r
388 }\r
389\r
390 //\r
391 // Prevent further changes to LockBoxes or SMRAM.\r
392 //\r
393 Handle = NULL;\r
394 Status = gBS->InstallProtocolInterface (&Handle,\r
395 &gEfiDxeSmmReadyToLockProtocolGuid, EFI_NATIVE_INTERFACE,\r
396 NULL);\r
397 ASSERT_EFI_ERROR (Status);\r
e9e9ad64 398\r
9789894e 399 //\r
18064510
LE
400 // Dispatch deferred images after EndOfDxe event and ReadyToLock\r
401 // installation.\r
9789894e
RN
402 //\r
403 EfiBootManagerDispatchDeferredImages ();\r
404\r
05480e2f
AP
405 PlatformInitializeConsole (\r
406 XenDetected() ? gXenPlatformConsole : gPlatformConsole);\r
9b08c655
LE
407\r
408 FrontPageTimeout = GetFrontPageTimeoutFromQemu ();\r
409 PcdStatus = PcdSet16S (PcdPlatformBootTimeOut, FrontPageTimeout);\r
579afd6b 410 ASSERT_RETURN_ERROR (PcdStatus);\r
9b08c655
LE
411 //\r
412 // Reflect the PCD in the standard Timeout variable.\r
413 //\r
414 Status = gRT->SetVariable (\r
415 EFI_TIME_OUT_VARIABLE_NAME,\r
416 &gEfiGlobalVariableGuid,\r
417 (EFI_VARIABLE_NON_VOLATILE |\r
418 EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
419 EFI_VARIABLE_RUNTIME_ACCESS),\r
420 sizeof FrontPageTimeout,\r
421 &FrontPageTimeout\r
422 );\r
423 DEBUG ((\r
424 EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE,\r
425 "%a: SetVariable(%s, %u): %r\n",\r
426 __FUNCTION__,\r
427 EFI_TIME_OUT_VARIABLE_NAME,\r
428 FrontPageTimeout,\r
429 Status\r
430 ));\r
07dd96e8
RN
431\r
432 PlatformRegisterOptionsAndKeys ();\r
7ebad830
LE
433\r
434 //\r
435 // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL\r
436 // instances on Virtio PCI RNG devices.\r
437 //\r
438 VisitAllInstancesOfProtocol (&gEfiPciIoProtocolGuid, ConnectVirtioPciRng,\r
439 NULL);\r
30541881
RN
440}\r
441\r
442\r
443EFI_STATUS\r
444EFIAPI\r
445ConnectRootBridge (\r
446 IN EFI_HANDLE RootBridgeHandle,\r
447 IN VOID *Instance,\r
448 IN VOID *Context\r
449 )\r
450{\r
451 EFI_STATUS Status;\r
452\r
453 //\r
454 // Make the PCI bus driver connect the root bridge, non-recursively. This\r
455 // will produce a number of child handles with PciIo on them.\r
456 //\r
457 Status = gBS->ConnectController (\r
458 RootBridgeHandle, // ControllerHandle\r
459 NULL, // DriverImageHandle\r
460 NULL, // RemainingDevicePath -- produce all\r
461 // children\r
462 FALSE // Recursive\r
463 );\r
464 return Status;\r
465}\r
466\r
467\r
7ebad830
LE
468STATIC\r
469EFI_STATUS\r
470EFIAPI\r
471ConnectVirtioPciRng (\r
472 IN EFI_HANDLE Handle,\r
473 IN VOID *Instance,\r
474 IN VOID *Context\r
475 )\r
476{\r
477 EFI_PCI_IO_PROTOCOL *PciIo;\r
478 EFI_STATUS Status;\r
479 UINT16 VendorId;\r
480 UINT16 DeviceId;\r
481 UINT8 RevisionId;\r
482 BOOLEAN Virtio10;\r
483 UINT16 SubsystemId;\r
484\r
485 PciIo = Instance;\r
486\r
487 //\r
488 // Read and check VendorId.\r
489 //\r
490 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,\r
491 1, &VendorId);\r
492 if (EFI_ERROR (Status)) {\r
493 goto Error;\r
494 }\r
495 if (VendorId != VIRTIO_VENDOR_ID) {\r
496 return EFI_SUCCESS;\r
497 }\r
498\r
499 //\r
500 // Read DeviceId and RevisionId.\r
501 //\r
502 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_DEVICE_ID_OFFSET,\r
503 1, &DeviceId);\r
504 if (EFI_ERROR (Status)) {\r
505 goto Error;\r
506 }\r
507 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,\r
508 1, &RevisionId);\r
509 if (EFI_ERROR (Status)) {\r
510 goto Error;\r
511 }\r
512\r
513 //\r
514 // From DeviceId and RevisionId, determine whether the device is a\r
515 // modern-only Virtio 1.0 device. In case of Virtio 1.0, DeviceId can\r
516 // immediately be restricted to VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and\r
517 // SubsystemId will only play a sanity-check role. Otherwise, DeviceId can\r
518 // only be sanity-checked, and SubsystemId will decide.\r
519 //\r
520 if (DeviceId == 0x1040 + VIRTIO_SUBSYSTEM_ENTROPY_SOURCE &&\r
521 RevisionId >= 0x01) {\r
522 Virtio10 = TRUE;\r
523 } else if (DeviceId >= 0x1000 && DeviceId <= 0x103F && RevisionId == 0x00) {\r
524 Virtio10 = FALSE;\r
525 } else {\r
526 return EFI_SUCCESS;\r
527 }\r
528\r
529 //\r
530 // Read and check SubsystemId as dictated by Virtio10.\r
531 //\r
532 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,\r
533 PCI_SUBSYSTEM_ID_OFFSET, 1, &SubsystemId);\r
534 if (EFI_ERROR (Status)) {\r
535 goto Error;\r
536 }\r
537 if ((Virtio10 && SubsystemId >= 0x40) ||\r
538 (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE)) {\r
539 Status = gBS->ConnectController (\r
540 Handle, // ControllerHandle\r
541 NULL, // DriverImageHandle -- connect all drivers\r
542 NULL, // RemainingDevicePath -- produce all child handles\r
543 FALSE // Recursive -- don't follow child handles\r
544 );\r
545 if (EFI_ERROR (Status)) {\r
546 goto Error;\r
547 }\r
548 }\r
549 return EFI_SUCCESS;\r
550\r
551Error:\r
552 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
553 return Status;\r
554}\r
555\r
556\r
fe1b9e8e
LE
557/**\r
558 Add IsaKeyboard to ConIn; add IsaSerial to ConOut, ConIn, ErrOut.\r
559\r
560 @param[in] DeviceHandle Handle of the LPC Bridge device.\r
561\r
562 @retval EFI_SUCCESS Console devices on the LPC bridge have been added to\r
563 ConOut, ConIn, and ErrOut.\r
564\r
565 @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing\r
566 from DeviceHandle.\r
567**/\r
30541881
RN
568EFI_STATUS\r
569PrepareLpcBridgeDevicePath (\r
570 IN EFI_HANDLE DeviceHandle\r
571 )\r
30541881
RN
572{\r
573 EFI_STATUS Status;\r
574 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
575 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
576 CHAR16 *DevPathStr;\r
577\r
578 DevicePath = NULL;\r
579 Status = gBS->HandleProtocol (\r
580 DeviceHandle,\r
581 &gEfiDevicePathProtocolGuid,\r
582 (VOID*)&DevicePath\r
583 );\r
584 if (EFI_ERROR (Status)) {\r
585 return Status;\r
586 }\r
587 TempDevicePath = DevicePath;\r
588\r
589 //\r
590 // Register Keyboard\r
591 //\r
18064510
LE
592 DevicePath = AppendDevicePathNode (DevicePath,\r
593 (EFI_DEVICE_PATH_PROTOCOL *)&gPnpPs2KeyboardDeviceNode);\r
30541881 594\r
9dc08ec6 595 EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);\r
30541881
RN
596\r
597 //\r
598 // Register COM1\r
599 //\r
600 DevicePath = TempDevicePath;\r
601 gPnp16550ComPortDeviceNode.UID = 0;\r
602\r
18064510
LE
603 DevicePath = AppendDevicePathNode (DevicePath,\r
604 (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode);\r
605 DevicePath = AppendDevicePathNode (DevicePath,\r
606 (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);\r
607 DevicePath = AppendDevicePathNode (DevicePath,\r
608 (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);\r
30541881
RN
609\r
610 //\r
611 // Print Device Path\r
612 //\r
77f47588 613 DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);\r
30541881
RN
614 if (DevPathStr != NULL) {\r
615 DEBUG((\r
70d5086c 616 DEBUG_INFO,\r
30541881
RN
617 "BdsPlatform.c+%d: COM%d DevPath: %s\n",\r
618 __LINE__,\r
619 gPnp16550ComPortDeviceNode.UID + 1,\r
620 DevPathStr\r
621 ));\r
622 FreePool(DevPathStr);\r
623 }\r
624\r
9dc08ec6
RN
625 EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
626 EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);\r
627 EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);\r
30541881
RN
628\r
629 //\r
630 // Register COM2\r
631 //\r
632 DevicePath = TempDevicePath;\r
633 gPnp16550ComPortDeviceNode.UID = 1;\r
634\r
18064510
LE
635 DevicePath = AppendDevicePathNode (DevicePath,\r
636 (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode);\r
637 DevicePath = AppendDevicePathNode (DevicePath,\r
638 (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);\r
639 DevicePath = AppendDevicePathNode (DevicePath,\r
640 (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);\r
30541881
RN
641\r
642 //\r
643 // Print Device Path\r
644 //\r
77f47588 645 DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);\r
30541881
RN
646 if (DevPathStr != NULL) {\r
647 DEBUG((\r
70d5086c 648 DEBUG_INFO,\r
30541881
RN
649 "BdsPlatform.c+%d: COM%d DevPath: %s\n",\r
650 __LINE__,\r
651 gPnp16550ComPortDeviceNode.UID + 1,\r
652 DevPathStr\r
653 ));\r
654 FreePool(DevPathStr);\r
655 }\r
656\r
9dc08ec6
RN
657 EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
658 EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);\r
659 EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);\r
30541881
RN
660\r
661 return EFI_SUCCESS;\r
662}\r
663\r
664EFI_STATUS\r
665GetGopDevicePath (\r
666 IN EFI_DEVICE_PATH_PROTOCOL *PciDevicePath,\r
667 OUT EFI_DEVICE_PATH_PROTOCOL **GopDevicePath\r
668 )\r
669{\r
670 UINTN Index;\r
671 EFI_STATUS Status;\r
672 EFI_HANDLE PciDeviceHandle;\r
673 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
674 EFI_DEVICE_PATH_PROTOCOL *TempPciDevicePath;\r
675 UINTN GopHandleCount;\r
676 EFI_HANDLE *GopHandleBuffer;\r
677\r
678 if (PciDevicePath == NULL || GopDevicePath == NULL) {\r
679 return EFI_INVALID_PARAMETER;\r
680 }\r
681\r
682 //\r
683 // Initialize the GopDevicePath to be PciDevicePath\r
684 //\r
685 *GopDevicePath = PciDevicePath;\r
686 TempPciDevicePath = PciDevicePath;\r
687\r
688 Status = gBS->LocateDevicePath (\r
689 &gEfiDevicePathProtocolGuid,\r
690 &TempPciDevicePath,\r
691 &PciDeviceHandle\r
692 );\r
693 if (EFI_ERROR (Status)) {\r
694 return Status;\r
695 }\r
696\r
697 //\r
f17c0ab6 698 // Try to connect this handle, so that GOP driver could start on this\r
30541881
RN
699 // device and create child handles with GraphicsOutput Protocol installed\r
700 // on them, then we get device paths of these child handles and select\r
701 // them as possible console device.\r
702 //\r
703 gBS->ConnectController (PciDeviceHandle, NULL, NULL, FALSE);\r
704\r
705 Status = gBS->LocateHandleBuffer (\r
706 ByProtocol,\r
707 &gEfiGraphicsOutputProtocolGuid,\r
708 NULL,\r
709 &GopHandleCount,\r
710 &GopHandleBuffer\r
711 );\r
712 if (!EFI_ERROR (Status)) {\r
713 //\r
714 // Add all the child handles as possible Console Device\r
715 //\r
716 for (Index = 0; Index < GopHandleCount; Index++) {\r
18064510
LE
717 Status = gBS->HandleProtocol (GopHandleBuffer[Index],\r
718 &gEfiDevicePathProtocolGuid, (VOID*)&TempDevicePath);\r
30541881
RN
719 if (EFI_ERROR (Status)) {\r
720 continue;\r
721 }\r
722 if (CompareMem (\r
723 PciDevicePath,\r
724 TempDevicePath,\r
725 GetDevicePathSize (PciDevicePath) - END_DEVICE_PATH_LENGTH\r
726 ) == 0) {\r
727 //\r
728 // In current implementation, we only enable one of the child handles\r
729 // as console device, i.e. sotre one of the child handle's device\r
730 // path to variable "ConOut"\r
f17c0ab6 731 // In future, we could select all child handles to be console device\r
30541881
RN
732 //\r
733\r
734 *GopDevicePath = TempDevicePath;\r
735\r
736 //\r
18064510
LE
737 // Delete the PCI device's path that added by\r
738 // GetPlugInPciVgaDevicePath(). Add the integrity GOP device path.\r
30541881 739 //\r
9dc08ec6
RN
740 EfiBootManagerUpdateConsoleVariable (ConOutDev, NULL, PciDevicePath);\r
741 EfiBootManagerUpdateConsoleVariable (ConOutDev, TempDevicePath, NULL);\r
30541881
RN
742 }\r
743 }\r
744 gBS->FreePool (GopHandleBuffer);\r
745 }\r
746\r
747 return EFI_SUCCESS;\r
748}\r
749\r
fe1b9e8e
LE
750/**\r
751 Add PCI display to ConOut.\r
752\r
753 @param[in] DeviceHandle Handle of the PCI display device.\r
754\r
755 @retval EFI_SUCCESS The PCI display device has been added to ConOut.\r
756\r
757 @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing\r
758 from DeviceHandle.\r
759**/\r
30541881 760EFI_STATUS\r
4fdb585c 761PreparePciDisplayDevicePath (\r
30541881
RN
762 IN EFI_HANDLE DeviceHandle\r
763 )\r
30541881
RN
764{\r
765 EFI_STATUS Status;\r
766 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
767 EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;\r
768\r
769 DevicePath = NULL;\r
770 GopDevicePath = NULL;\r
771 Status = gBS->HandleProtocol (\r
772 DeviceHandle,\r
773 &gEfiDevicePathProtocolGuid,\r
774 (VOID*)&DevicePath\r
775 );\r
776 if (EFI_ERROR (Status)) {\r
777 return Status;\r
778 }\r
779\r
780 GetGopDevicePath (DevicePath, &GopDevicePath);\r
781 DevicePath = GopDevicePath;\r
782\r
9dc08ec6 783 EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
30541881
RN
784\r
785 return EFI_SUCCESS;\r
786}\r
787\r
fe1b9e8e 788/**\r
30541881 789 Add PCI Serial to ConOut, ConIn, ErrOut.\r
30541881 790\r
fe1b9e8e 791 @param[in] DeviceHandle Handle of the PCI serial device.\r
30541881 792\r
fe1b9e8e
LE
793 @retval EFI_SUCCESS The PCI serial device has been added to ConOut, ConIn,\r
794 ErrOut.\r
30541881 795\r
fe1b9e8e
LE
796 @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing\r
797 from DeviceHandle.\r
798**/\r
799EFI_STATUS\r
800PreparePciSerialDevicePath (\r
801 IN EFI_HANDLE DeviceHandle\r
802 )\r
30541881
RN
803{\r
804 EFI_STATUS Status;\r
805 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
806\r
807 DevicePath = NULL;\r
808 Status = gBS->HandleProtocol (\r
809 DeviceHandle,\r
810 &gEfiDevicePathProtocolGuid,\r
811 (VOID*)&DevicePath\r
812 );\r
813 if (EFI_ERROR (Status)) {\r
814 return Status;\r
815 }\r
816\r
18064510
LE
817 DevicePath = AppendDevicePathNode (DevicePath,\r
818 (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);\r
819 DevicePath = AppendDevicePathNode (DevicePath,\r
820 (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);\r
30541881 821\r
9dc08ec6
RN
822 EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
823 EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);\r
824 EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);\r
30541881
RN
825\r
826 return EFI_SUCCESS;\r
827}\r
828\r
829EFI_STATUS\r
830VisitAllInstancesOfProtocol (\r
831 IN EFI_GUID *Id,\r
832 IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,\r
833 IN VOID *Context\r
834 )\r
835{\r
836 EFI_STATUS Status;\r
837 UINTN HandleCount;\r
838 EFI_HANDLE *HandleBuffer;\r
839 UINTN Index;\r
840 VOID *Instance;\r
841\r
842 //\r
843 // Start to check all the PciIo to find all possible device\r
844 //\r
845 HandleCount = 0;\r
846 HandleBuffer = NULL;\r
847 Status = gBS->LocateHandleBuffer (\r
848 ByProtocol,\r
849 Id,\r
850 NULL,\r
851 &HandleCount,\r
852 &HandleBuffer\r
853 );\r
854 if (EFI_ERROR (Status)) {\r
855 return Status;\r
856 }\r
857\r
858 for (Index = 0; Index < HandleCount; Index++) {\r
859 Status = gBS->HandleProtocol (HandleBuffer[Index], Id, &Instance);\r
860 if (EFI_ERROR (Status)) {\r
861 continue;\r
862 }\r
863\r
864 Status = (*CallBackFunction) (\r
865 HandleBuffer[Index],\r
866 Instance,\r
867 Context\r
868 );\r
869 }\r
870\r
871 gBS->FreePool (HandleBuffer);\r
872\r
873 return EFI_SUCCESS;\r
874}\r
875\r
876\r
877EFI_STATUS\r
878EFIAPI\r
879VisitingAPciInstance (\r
880 IN EFI_HANDLE Handle,\r
881 IN VOID *Instance,\r
882 IN VOID *Context\r
883 )\r
884{\r
885 EFI_STATUS Status;\r
886 EFI_PCI_IO_PROTOCOL *PciIo;\r
887 PCI_TYPE00 Pci;\r
888\r
889 PciIo = (EFI_PCI_IO_PROTOCOL*) Instance;\r
890\r
891 //\r
892 // Check for all PCI device\r
893 //\r
894 Status = PciIo->Pci.Read (\r
895 PciIo,\r
896 EfiPciIoWidthUint32,\r
897 0,\r
898 sizeof (Pci) / sizeof (UINT32),\r
899 &Pci\r
900 );\r
901 if (EFI_ERROR (Status)) {\r
902 return Status;\r
903 }\r
904\r
905 return (*(VISIT_PCI_INSTANCE_CALLBACK)(UINTN) Context) (\r
906 Handle,\r
907 PciIo,\r
908 &Pci\r
909 );\r
910\r
911}\r
912\r
913\r
914\r
915EFI_STATUS\r
916VisitAllPciInstances (\r
917 IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction\r
918 )\r
919{\r
920 return VisitAllInstancesOfProtocol (\r
921 &gEfiPciIoProtocolGuid,\r
922 VisitingAPciInstance,\r
923 (VOID*)(UINTN) CallBackFunction\r
924 );\r
925}\r
926\r
927\r
928/**\r
929 Do platform specific PCI Device check and add them to\r
930 ConOut, ConIn, ErrOut.\r
931\r
932 @param[in] Handle - Handle of PCI device instance\r
933 @param[in] PciIo - PCI IO protocol instance\r
934 @param[in] Pci - PCI Header register block\r
935\r
18064510
LE
936 @retval EFI_SUCCESS - PCI Device check and Console variable update\r
937 successfully.\r
30541881
RN
938 @retval EFI_STATUS - PCI Device check or Console variable update fail.\r
939\r
940**/\r
941EFI_STATUS\r
942EFIAPI\r
943DetectAndPreparePlatformPciDevicePath (\r
944 IN EFI_HANDLE Handle,\r
945 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
946 IN PCI_TYPE00 *Pci\r
947 )\r
948{\r
949 EFI_STATUS Status;\r
950\r
951 Status = PciIo->Attributes (\r
952 PciIo,\r
953 EfiPciIoAttributeOperationEnable,\r
954 EFI_PCI_DEVICE_ENABLE,\r
955 NULL\r
956 );\r
957 ASSERT_EFI_ERROR (Status);\r
958\r
f803c03c
LE
959 //\r
960 // Here we decide whether it is LPC Bridge\r
961 //\r
962 if ((IS_PCI_LPC (Pci)) ||\r
963 ((IS_PCI_ISA_PDECODE (Pci)) &&\r
964 (Pci->Hdr.VendorId == 0x8086) &&\r
965 (Pci->Hdr.DeviceId == 0x7000)\r
966 )\r
967 ) {\r
30541881 968 //\r
f803c03c
LE
969 // Add IsaKeyboard to ConIn,\r
970 // add IsaSerial to ConOut, ConIn, ErrOut\r
30541881 971 //\r
70d5086c 972 DEBUG ((DEBUG_INFO, "Found LPC Bridge device\n"));\r
f803c03c
LE
973 PrepareLpcBridgeDevicePath (Handle);\r
974 return EFI_SUCCESS;\r
975 }\r
976 //\r
977 // Here we decide which Serial device to enable in PCI bus\r
978 //\r
979 if (IS_PCI_16550SERIAL (Pci)) {\r
30541881 980 //\r
f803c03c 981 // Add them to ConOut, ConIn, ErrOut.\r
30541881 982 //\r
70d5086c 983 DEBUG ((DEBUG_INFO, "Found PCI 16550 SERIAL device\n"));\r
f803c03c
LE
984 PreparePciSerialDevicePath (Handle);\r
985 return EFI_SUCCESS;\r
30541881
RN
986 }\r
987\r
988 //\r
4fdb585c 989 // Here we decide which display device to enable in PCI bus\r
30541881 990 //\r
4fdb585c 991 if (IS_PCI_DISPLAY (Pci)) {\r
30541881
RN
992 //\r
993 // Add them to ConOut.\r
994 //\r
70d5086c 995 DEBUG ((DEBUG_INFO, "Found PCI display device\n"));\r
4fdb585c 996 PreparePciDisplayDevicePath (Handle);\r
30541881
RN
997 return EFI_SUCCESS;\r
998 }\r
999\r
1000 return Status;\r
1001}\r
1002\r
1003\r
fe1b9e8e
LE
1004/**\r
1005 Connect the predefined platform default console device.\r
1006\r
1007 Always try to find and enable PCI display devices.\r
1008\r
1009 @param[in] PlatformConsole Predefined platform default console device array.\r
1010**/\r
e9e9ad64
RN
1011VOID\r
1012PlatformInitializeConsole (\r
1013 IN PLATFORM_CONSOLE_CONNECT_ENTRY *PlatformConsole\r
30541881 1014 )\r
30541881 1015{\r
30541881 1016 UINTN Index;\r
30541881
RN
1017\r
1018 //\r
f803c03c
LE
1019 // Do platform specific PCI Device check and add them to ConOut, ConIn,\r
1020 // ErrOut\r
30541881 1021 //\r
f803c03c 1022 VisitAllPciInstances (DetectAndPreparePlatformPciDevicePath);\r
30541881 1023\r
f803c03c
LE
1024 //\r
1025 // Have chance to connect the platform default console,\r
1026 // the platform default console is the minimum device group\r
1027 // the platform should support\r
1028 //\r
1029 for (Index = 0; PlatformConsole[Index].DevicePath != NULL; ++Index) {\r
30541881 1030 //\r
f803c03c 1031 // Update the console variable with the connect type\r
30541881 1032 //\r
f803c03c
LE
1033 if ((PlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) {\r
1034 EfiBootManagerUpdateConsoleVariable (ConIn,\r
1035 PlatformConsole[Index].DevicePath, NULL);\r
1036 }\r
1037 if ((PlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) {\r
1038 EfiBootManagerUpdateConsoleVariable (ConOut,\r
1039 PlatformConsole[Index].DevicePath, NULL);\r
1040 }\r
1041 if ((PlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) {\r
1042 EfiBootManagerUpdateConsoleVariable (ErrOut,\r
1043 PlatformConsole[Index].DevicePath, NULL);\r
30541881 1044 }\r
30541881 1045 }\r
30541881
RN
1046}\r
1047\r
1048\r
1049/**\r
1050 Configure PCI Interrupt Line register for applicable devices\r
1051 Ported from SeaBIOS, src/fw/pciinit.c, *_pci_slot_get_irq()\r
1052\r
1053 @param[in] Handle - Handle of PCI device instance\r
1054 @param[in] PciIo - PCI IO protocol instance\r
1055 @param[in] PciHdr - PCI Header register block\r
1056\r
1057 @retval EFI_SUCCESS - PCI Interrupt Line register configured successfully.\r
1058\r
1059**/\r
1060EFI_STATUS\r
1061EFIAPI\r
1062SetPciIntLine (\r
1063 IN EFI_HANDLE Handle,\r
1064 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1065 IN PCI_TYPE00 *PciHdr\r
1066 )\r
1067{\r
1068 EFI_DEVICE_PATH_PROTOCOL *DevPathNode;\r
1069 EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
1070 UINTN RootSlot;\r
1071 UINTN Idx;\r
1072 UINT8 IrqLine;\r
1073 EFI_STATUS Status;\r
1074 UINT32 RootBusNumber;\r
1075\r
1076 Status = EFI_SUCCESS;\r
1077\r
1078 if (PciHdr->Device.InterruptPin != 0) {\r
1079\r
1080 DevPathNode = DevicePathFromHandle (Handle);\r
1081 ASSERT (DevPathNode != NULL);\r
1082 DevPath = DevPathNode;\r
1083\r
1084 RootBusNumber = 0;\r
1085 if (DevicePathType (DevPathNode) == ACPI_DEVICE_PATH &&\r
1086 DevicePathSubType (DevPathNode) == ACPI_DP &&\r
1087 ((ACPI_HID_DEVICE_PATH *)DevPathNode)->HID == EISA_PNP_ID(0x0A03)) {\r
1088 RootBusNumber = ((ACPI_HID_DEVICE_PATH *)DevPathNode)->UID;\r
1089 }\r
1090\r
1091 //\r
1092 // Compute index into PciHostIrqs[] table by walking\r
1093 // the device path and adding up all device numbers\r
1094 //\r
1095 Status = EFI_NOT_FOUND;\r
1096 RootSlot = 0;\r
1097 Idx = PciHdr->Device.InterruptPin - 1;\r
1098 while (!IsDevicePathEnd (DevPathNode)) {\r
1099 if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH &&\r
1100 DevicePathSubType (DevPathNode) == HW_PCI_DP) {\r
1101\r
1102 Idx += ((PCI_DEVICE_PATH *)DevPathNode)->Device;\r
1103\r
1104 //\r
1105 // Unlike SeaBIOS, which starts climbing from the leaf device\r
1106 // up toward the root, we traverse the device path starting at\r
1107 // the root moving toward the leaf node.\r
1108 // The slot number of the top-level parent bridge is needed for\r
1109 // Q35 cases with more than 24 slots on the root bus.\r
1110 //\r
1111 if (Status != EFI_SUCCESS) {\r
1112 Status = EFI_SUCCESS;\r
1113 RootSlot = ((PCI_DEVICE_PATH *)DevPathNode)->Device;\r
1114 }\r
1115 }\r
1116\r
1117 DevPathNode = NextDevicePathNode (DevPathNode);\r
1118 }\r
1119 if (EFI_ERROR (Status)) {\r
1120 return Status;\r
1121 }\r
1122 if (RootBusNumber == 0 && RootSlot == 0) {\r
1123 DEBUG((\r
70d5086c 1124 DEBUG_ERROR,\r
30541881
RN
1125 "%a: PCI host bridge (00:00.0) should have no interrupts!\n",\r
1126 __FUNCTION__\r
1127 ));\r
1128 ASSERT (FALSE);\r
1129 }\r
1130\r
1131 //\r
1132 // Final PciHostIrqs[] index calculation depends on the platform\r
1133 // and should match SeaBIOS src/fw/pciinit.c *_pci_slot_get_irq()\r
1134 //\r
1135 switch (mHostBridgeDevId) {\r
1136 case INTEL_82441_DEVICE_ID:\r
1137 Idx -= 1;\r
1138 break;\r
1139 case INTEL_Q35_MCH_DEVICE_ID:\r
1140 //\r
1141 // SeaBIOS contains the following comment:\r
1142 // "Slots 0-24 rotate slot:pin mapping similar to piix above, but\r
1143 // with a different starting index - see q35-acpi-dsdt.dsl.\r
1144 //\r
1145 // Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)"\r
1146 //\r
1147 if (RootSlot > 24) {\r
1148 //\r
1149 // in this case, subtract back out RootSlot from Idx\r
1150 // (SeaBIOS never adds it to begin with, but that would make our\r
1151 // device path traversal loop above too awkward)\r
1152 //\r
1153 Idx -= RootSlot;\r
1154 }\r
1155 break;\r
1156 default:\r
1157 ASSERT (FALSE); // should never get here\r
1158 }\r
1159 Idx %= ARRAY_SIZE (PciHostIrqs);\r
1160 IrqLine = PciHostIrqs[Idx];\r
1161\r
1162 DEBUG_CODE_BEGIN ();\r
1163 {\r
1164 CHAR16 *DevPathString;\r
1165 STATIC CHAR16 Fallback[] = L"<failed to convert>";\r
1166 UINTN Segment, Bus, Device, Function;\r
1167\r
1168 DevPathString = ConvertDevicePathToText (DevPath, FALSE, FALSE);\r
1169 if (DevPathString == NULL) {\r
1170 DevPathString = Fallback;\r
1171 }\r
1172 Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);\r
1173 ASSERT_EFI_ERROR (Status);\r
1174\r
70d5086c 1175 DEBUG ((DEBUG_VERBOSE, "%a: [%02x:%02x.%x] %s -> 0x%02x\n", __FUNCTION__,\r
30541881
RN
1176 (UINT32)Bus, (UINT32)Device, (UINT32)Function, DevPathString,\r
1177 IrqLine));\r
1178\r
1179 if (DevPathString != Fallback) {\r
1180 FreePool (DevPathString);\r
1181 }\r
1182 }\r
1183 DEBUG_CODE_END ();\r
1184\r
1185 //\r
1186 // Set PCI Interrupt Line register for this device to PciHostIrqs[Idx]\r
1187 //\r
1188 Status = PciIo->Pci.Write (\r
1189 PciIo,\r
1190 EfiPciIoWidthUint8,\r
1191 PCI_INT_LINE_OFFSET,\r
1192 1,\r
1193 &IrqLine\r
1194 );\r
1195 }\r
1196\r
1197 return Status;\r
1198}\r
1199\r
1200\r
1201VOID\r
1202PciAcpiInitialization (\r
1203 )\r
1204{\r
1205 UINTN Pmba;\r
1206\r
1207 //\r
1208 // Query Host Bridge DID to determine platform type\r
1209 //\r
1210 mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);\r
1211 switch (mHostBridgeDevId) {\r
1212 case INTEL_82441_DEVICE_ID:\r
1213 Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);\r
1214 //\r
1215 // 00:01.0 ISA Bridge (PIIX4) LNK routing targets\r
1216 //\r
6573ae8c
BH
1217 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), PciHostIrqs[0]); // A\r
1218 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), PciHostIrqs[1]); // B\r
1219 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), PciHostIrqs[2]); // C\r
1220 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), PciHostIrqs[3]); // D\r
30541881
RN
1221 break;\r
1222 case INTEL_Q35_MCH_DEVICE_ID:\r
1223 Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);\r
1224 //\r
1225 // 00:1f.0 LPC Bridge (Q35) LNK routing targets\r
1226 //\r
6573ae8c
BH
1227 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), PciHostIrqs[0]); // A\r
1228 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), PciHostIrqs[1]); // B\r
1229 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), PciHostIrqs[2]); // C\r
1230 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), PciHostIrqs[3]); // D\r
1231 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), PciHostIrqs[0]); // E\r
1232 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), PciHostIrqs[1]); // F\r
1233 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), PciHostIrqs[2]); // G\r
1234 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), PciHostIrqs[3]); // H\r
30541881
RN
1235 break;\r
1236 default:\r
ad256f95
AP
1237 if (XenDetected ()) {\r
1238 //\r
1239 // There is no PCI bus in this case.\r
1240 //\r
1241 return;\r
1242 }\r
70d5086c 1243 DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",\r
30541881
RN
1244 __FUNCTION__, mHostBridgeDevId));\r
1245 ASSERT (FALSE);\r
1246 return;\r
1247 }\r
1248\r
1249 //\r
1250 // Initialize PCI_INTERRUPT_LINE for applicable present PCI devices\r
1251 //\r
1252 VisitAllPciInstances (SetPciIntLine);\r
1253\r
1254 //\r
1255 // Set ACPI SCI_EN bit in PMCNTRL\r
1256 //\r
1257 IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);\r
1258}\r
1259\r
30541881
RN
1260EFI_STATUS\r
1261EFIAPI\r
1262ConnectRecursivelyIfPciMassStorage (\r
1263 IN EFI_HANDLE Handle,\r
1264 IN EFI_PCI_IO_PROTOCOL *Instance,\r
1265 IN PCI_TYPE00 *PciHeader\r
1266 )\r
1267{\r
1268 EFI_STATUS Status;\r
1269 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
1270 CHAR16 *DevPathStr;\r
1271\r
da2369d2
GL
1272 //\r
1273 // Recognize PCI Mass Storage, and Xen PCI devices\r
1274 //\r
1275 if (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ||\r
1276 (XenDetected() && IS_CLASS2 (PciHeader, 0xFF, 0x80))) {\r
30541881
RN
1277 DevicePath = NULL;\r
1278 Status = gBS->HandleProtocol (\r
1279 Handle,\r
1280 &gEfiDevicePathProtocolGuid,\r
1281 (VOID*)&DevicePath\r
1282 );\r
1283 if (EFI_ERROR (Status)) {\r
1284 return Status;\r
1285 }\r
1286\r
1287 //\r
1288 // Print Device Path\r
1289 //\r
77f47588 1290 DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);\r
30541881
RN
1291 if (DevPathStr != NULL) {\r
1292 DEBUG((\r
70d5086c 1293 DEBUG_INFO,\r
da2369d2 1294 "Found %s device: %s\n",\r
18064510
LE
1295 (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ?\r
1296 L"Mass Storage" :\r
1297 L"Xen"\r
1298 ),\r
30541881
RN
1299 DevPathStr\r
1300 ));\r
1301 FreePool(DevPathStr);\r
1302 }\r
1303\r
1304 Status = gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
1305 if (EFI_ERROR (Status)) {\r
1306 return Status;\r
1307 }\r
1308\r
1309 }\r
1310\r
1311 return EFI_SUCCESS;\r
1312}\r
1313\r
1314\r
1315/**\r
1316 This notification function is invoked when the\r
1317 EMU Variable FVB has been changed.\r
1318\r
8c0b0b34
TH
1319 @param Event The event that occurred\r
1320 @param Context For EFI compatibility. Not used.\r
30541881
RN
1321\r
1322**/\r
1323VOID\r
1324EFIAPI\r
1325EmuVariablesUpdatedCallback (\r
1326 IN EFI_EVENT Event,\r
1327 IN VOID *Context\r
1328 )\r
1329{\r
70d5086c 1330 DEBUG ((DEBUG_INFO, "EmuVariablesUpdatedCallback\n"));\r
30541881
RN
1331 UpdateNvVarsOnFileSystem ();\r
1332}\r
1333\r
1334\r
1335EFI_STATUS\r
1336EFIAPI\r
1337VisitingFileSystemInstance (\r
1338 IN EFI_HANDLE Handle,\r
1339 IN VOID *Instance,\r
1340 IN VOID *Context\r
1341 )\r
1342{\r
1343 EFI_STATUS Status;\r
1344 STATIC BOOLEAN ConnectedToFileSystem = FALSE;\r
579afd6b 1345 RETURN_STATUS PcdStatus;\r
30541881
RN
1346\r
1347 if (ConnectedToFileSystem) {\r
1348 return EFI_ALREADY_STARTED;\r
1349 }\r
1350\r
1351 Status = ConnectNvVarsToFileSystem (Handle);\r
1352 if (EFI_ERROR (Status)) {\r
1353 return Status;\r
1354 }\r
1355\r
1356 ConnectedToFileSystem = TRUE;\r
1357 mEmuVariableEvent =\r
1358 EfiCreateProtocolNotifyEvent (\r
1359 &gEfiDevicePathProtocolGuid,\r
1360 TPL_CALLBACK,\r
1361 EmuVariablesUpdatedCallback,\r
1362 NULL,\r
1363 &mEmuVariableEventReg\r
1364 );\r
579afd6b
LE
1365 PcdStatus = PcdSet64S (PcdEmuVariableEvent,\r
1366 (UINT64)(UINTN) mEmuVariableEvent);\r
1367 ASSERT_RETURN_ERROR (PcdStatus);\r
30541881
RN
1368\r
1369 return EFI_SUCCESS;\r
1370}\r
1371\r
1372\r
1373VOID\r
1374PlatformBdsRestoreNvVarsFromHardDisk (\r
1375 )\r
1376{\r
1377 VisitAllPciInstances (ConnectRecursivelyIfPciMassStorage);\r
1378 VisitAllInstancesOfProtocol (\r
1379 &gEfiSimpleFileSystemProtocolGuid,\r
1380 VisitingFileSystemInstance,\r
1381 NULL\r
1382 );\r
1383\r
1384}\r
1385\r
fe1b9e8e
LE
1386/**\r
1387 Connect with predefined platform connect sequence.\r
1388\r
1389 The OEM/IBV can customize with their own connect sequence.\r
1390**/\r
30541881
RN
1391VOID\r
1392PlatformBdsConnectSequence (\r
1393 VOID\r
1394 )\r
30541881 1395{\r
245c643c
LE
1396 UINTN Index;\r
1397 RETURN_STATUS Status;\r
30541881 1398\r
70d5086c 1399 DEBUG ((DEBUG_INFO, "PlatformBdsConnectSequence\n"));\r
30541881
RN
1400\r
1401 Index = 0;\r
1402\r
1403 //\r
1404 // Here we can get the customized platform connect sequence\r
1405 // Notes: we can connect with new variable which record the\r
1406 // last time boots connect device path sequence\r
1407 //\r
1408 while (gPlatformConnectSequence[Index] != NULL) {\r
1409 //\r
1410 // Build the platform boot option\r
1411 //\r
fed691a6 1412 EfiBootManagerConnectDevicePath (gPlatformConnectSequence[Index], NULL);\r
30541881
RN
1413 Index++;\r
1414 }\r
1415\r
245c643c
LE
1416 Status = ConnectDevicesFromQemu ();\r
1417 if (RETURN_ERROR (Status)) {\r
1418 //\r
1419 // Just use the simple policy to connect all devices\r
1420 //\r
1421 DEBUG ((DEBUG_INFO, "EfiBootManagerConnectAll\n"));\r
1422 EfiBootManagerConnectAll ();\r
1423 }\r
30541881
RN
1424}\r
1425\r
30541881
RN
1426/**\r
1427 Save the S3 boot script.\r
1428\r
1429 Note that DxeSmmReadyToLock must be signaled after this function returns;\r
1430 otherwise the script wouldn't be saved actually.\r
1431**/\r
1432STATIC\r
1433VOID\r
1434SaveS3BootScript (\r
1435 VOID\r
1436 )\r
1437{\r
1438 EFI_STATUS Status;\r
1439 EFI_S3_SAVE_STATE_PROTOCOL *BootScript;\r
1440 STATIC CONST UINT8 Info[] = { 0xDE, 0xAD, 0xBE, 0xEF };\r
1441\r
1442 Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, NULL,\r
1443 (VOID **) &BootScript);\r
1444 ASSERT_EFI_ERROR (Status);\r
1445\r
1446 //\r
1447 // Despite the opcode documentation in the PI spec, the protocol\r
1448 // implementation embeds a deep copy of the info in the boot script, rather\r
1449 // than storing just a pointer to runtime or NVS storage.\r
1450 //\r
1451 Status = BootScript->Write(BootScript, EFI_BOOT_SCRIPT_INFORMATION_OPCODE,\r
1452 (UINT32) sizeof Info,\r
1453 (EFI_PHYSICAL_ADDRESS)(UINTN) &Info);\r
1454 ASSERT_EFI_ERROR (Status);\r
1455}\r
1456\r
1457\r
fe1b9e8e
LE
1458/**\r
1459 Do the platform specific action after the console is ready\r
1460\r
1461 Possible things that can be done in PlatformBootManagerAfterConsole:\r
1462\r
1463 > Console post action:\r
1464 > Dynamically switch output mode from 100x31 to 80x25 for certain senarino\r
1465 > Signal console ready platform customized event\r
1466 > Run diagnostics like memory testing\r
1467 > Connect certain devices\r
1468 > Dispatch aditional option roms\r
1469 > Special boot: e.g.: USB boot, enter UI\r
1470**/\r
30541881
RN
1471VOID\r
1472EFIAPI\r
a7566234
RN
1473PlatformBootManagerAfterConsole (\r
1474 VOID\r
30541881 1475 )\r
30541881 1476{\r
30541881
RN
1477 EFI_BOOT_MODE BootMode;\r
1478\r
70d5086c 1479 DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole\n"));\r
30541881
RN
1480\r
1481 if (PcdGetBool (PcdOvmfFlashVariablesEnable)) {\r
70d5086c 1482 DEBUG ((DEBUG_INFO, "PlatformBdsPolicyBehavior: not restoring NvVars "\r
30541881
RN
1483 "from disk since flash variables appear to be supported.\n"));\r
1484 } else {\r
1485 //\r
1486 // Try to restore variables from the hard disk early so\r
1487 // they can be used for the other BDS connect operations.\r
1488 //\r
1489 PlatformBdsRestoreNvVarsFromHardDisk ();\r
1490 }\r
1491\r
30541881
RN
1492 //\r
1493 // Get current Boot Mode\r
1494 //\r
30edcbf5 1495 BootMode = GetBootModeHob ();\r
837d9eea 1496 DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));\r
30541881
RN
1497\r
1498 //\r
1499 // Go the different platform policy with different boot mode\r
1500 // Notes: this part code can be change with the table policy\r
1501 //\r
1502 ASSERT (BootMode == BOOT_WITH_FULL_CONFIGURATION);\r
30541881
RN
1503\r
1504 //\r
e64a2ebe 1505 // Logo show\r
30541881 1506 //\r
ab970515 1507 BootLogoEnableLogo ();\r
30541881 1508\r
5942ea61
LE
1509 //\r
1510 // Set PCI Interrupt Line registers and ACPI SCI_EN\r
1511 //\r
1512 PciAcpiInitialization ();\r
1513\r
8d65d3b2
MAL
1514 //\r
1515 // Process TPM PPI request\r
1516 //\r
1517 Tcg2PhysicalPresenceLibProcessRequest (NULL);\r
1518\r
30541881 1519 //\r
a34a8869 1520 // Process QEMU's -kernel command line option\r
30541881 1521 //\r
a34a8869 1522 TryRunningQemuKernel ();\r
30541881
RN
1523\r
1524 //\r
a34a8869 1525 // Perform some platform specific connect sequence\r
30541881 1526 //\r
a34a8869 1527 PlatformBdsConnectSequence ();\r
30541881 1528\r
04fe914b
RN
1529 EfiBootManagerRefreshAllBootOption ();\r
1530\r
14b2ebc3
GL
1531 //\r
1532 // Register UEFI Shell\r
1533 //\r
1534 PlatformRegisterFvBootOption (\r
84f736a7 1535 &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
14b2ebc3
GL
1536 );\r
1537\r
2eb35898 1538 RemoveStaleFvFileOptions ();\r
2542feea 1539 SetBootOrderFromQemu ();\r
a7488174
LE
1540\r
1541 PlatformBmPrintScRegisterHandler ();\r
30541881
RN
1542}\r
1543\r
30541881
RN
1544/**\r
1545 This notification function is invoked when an instance of the\r
1546 EFI_DEVICE_PATH_PROTOCOL is produced.\r
1547\r
8c0b0b34
TH
1548 @param Event The event that occurred\r
1549 @param Context For EFI compatibility. Not used.\r
30541881
RN
1550\r
1551**/\r
1552VOID\r
1553EFIAPI\r
1554NotifyDevPath (\r
1555 IN EFI_EVENT Event,\r
1556 IN VOID *Context\r
1557 )\r
1558{\r
1559 EFI_HANDLE Handle;\r
1560 EFI_STATUS Status;\r
1561 UINTN BufferSize;\r
1562 EFI_DEVICE_PATH_PROTOCOL *DevPathNode;\r
1563 ATAPI_DEVICE_PATH *Atapi;\r
1564\r
1565 //\r
1566 // Examine all new handles\r
1567 //\r
1568 for (;;) {\r
1569 //\r
1570 // Get the next handle\r
1571 //\r
1572 BufferSize = sizeof (Handle);\r
1573 Status = gBS->LocateHandle (\r
1574 ByRegisterNotify,\r
1575 NULL,\r
1576 mEfiDevPathNotifyReg,\r
1577 &BufferSize,\r
1578 &Handle\r
1579 );\r
1580\r
1581 //\r
1582 // If not found, we're done\r
1583 //\r
1584 if (EFI_NOT_FOUND == Status) {\r
1585 break;\r
1586 }\r
1587\r
1588 if (EFI_ERROR (Status)) {\r
1589 continue;\r
1590 }\r
1591\r
1592 //\r
1593 // Get the DevicePath protocol on that handle\r
1594 //\r
18064510
LE
1595 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid,\r
1596 (VOID **)&DevPathNode);\r
30541881
RN
1597 ASSERT_EFI_ERROR (Status);\r
1598\r
1599 while (!IsDevicePathEnd (DevPathNode)) {\r
1600 //\r
1601 // Find the handler to dump this device path node\r
1602 //\r
1603 if (\r
1604 (DevicePathType(DevPathNode) == MESSAGING_DEVICE_PATH) &&\r
1605 (DevicePathSubType(DevPathNode) == MSG_ATAPI_DP)\r
1606 ) {\r
1607 Atapi = (ATAPI_DEVICE_PATH*) DevPathNode;\r
1608 PciOr16 (\r
1609 PCI_LIB_ADDRESS (\r
1610 0,\r
1611 1,\r
1612 1,\r
1613 (Atapi->PrimarySecondary == 1) ? 0x42: 0x40\r
1614 ),\r
1615 BIT15\r
1616 );\r
1617 }\r
1618\r
1619 //\r
1620 // Next device path node\r
1621 //\r
1622 DevPathNode = NextDevicePathNode (DevPathNode);\r
1623 }\r
1624 }\r
1625\r
1626 return;\r
1627}\r
1628\r
1629\r
1630VOID\r
1631InstallDevicePathCallback (\r
1632 VOID\r
1633 )\r
1634{\r
70d5086c 1635 DEBUG ((DEBUG_INFO, "Registered NotifyDevPath Event\n"));\r
30541881
RN
1636 mEfiDevPathEvent = EfiCreateProtocolNotifyEvent (\r
1637 &gEfiDevicePathProtocolGuid,\r
1638 TPL_CALLBACK,\r
1639 NotifyDevPath,\r
1640 NULL,\r
1641 &mEfiDevPathNotifyReg\r
1642 );\r
1643}\r
1644\r
a7566234 1645/**\r
18064510
LE
1646 This function is called each second during the boot manager waits the\r
1647 timeout.\r
a7566234
RN
1648\r
1649 @param TimeoutRemain The remaining timeout.\r
1650**/\r
1651VOID\r
1652EFIAPI\r
1653PlatformBootManagerWaitCallback (\r
1654 UINT16 TimeoutRemain\r
1655 )\r
1656{\r
fd096a99
LE
1657 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;\r
1658 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;\r
c3c90d8a 1659 UINT16 TimeoutInitial;\r
fd096a99 1660\r
c3c90d8a
PB
1661 TimeoutInitial = PcdGet16 (PcdPlatformBootTimeOut);\r
1662\r
1663 //\r
1664 // If PcdPlatformBootTimeOut is set to zero, then we consider\r
1665 // that no progress update should be enacted (since we'd only\r
1666 // ever display a one-shot progress of either 0% or 100%).\r
1667 //\r
1668 if (TimeoutInitial == 0) {\r
1669 return;\r
1670 }\r
fd096a99
LE
1671\r
1672 Black.Raw = 0x00000000;\r
1673 White.Raw = 0x00FFFFFF;\r
1674\r
1675 BootLogoUpdateProgress (\r
1676 White.Pixel,\r
1677 Black.Pixel,\r
1678 L"Start boot option",\r
1679 White.Pixel,\r
c3c90d8a 1680 (TimeoutInitial - TimeoutRemain) * 100 / TimeoutInitial,\r
fd096a99
LE
1681 0\r
1682 );\r
a7566234
RN
1683}\r
1684\r
5f66615b
RN
1685/**\r
1686 The function is called when no boot option could be launched,\r
1687 including platform recovery options and options pointing to applications\r
1688 built into firmware volumes.\r
1689\r
1690 If this function returns, BDS attempts to enter an infinite loop.\r
1691**/\r
1692VOID\r
1693EFIAPI\r
1694PlatformBootManagerUnableToBoot (\r
1695 VOID\r
1696 )\r
1697{\r
1698 EFI_STATUS Status;\r
1699 EFI_INPUT_KEY Key;\r
1700 EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;\r
1701 UINTN Index;\r
1702\r
1703 //\r
1704 // BootManagerMenu doesn't contain the correct information when return status\r
1705 // is EFI_NOT_FOUND.\r
1706 //\r
1707 Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
1708 if (EFI_ERROR (Status)) {\r
1709 return;\r
1710 }\r
1711 //\r
1712 // Normally BdsDxe does not print anything to the system console, but this is\r
1713 // a last resort -- the end-user will likely not see any DEBUG messages\r
1714 // logged in this situation.\r
1715 //\r
1716 // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn\r
1717 // here to see if it makes sense to request and wait for a keypress.\r
1718 //\r
1719 if (gST->ConIn != NULL) {\r
1720 AsciiPrint (\r
1721 "%a: No bootable option or device was found.\n"\r
1722 "%a: Press any key to enter the Boot Manager Menu.\n",\r
1723 gEfiCallerBaseName,\r
1724 gEfiCallerBaseName\r
1725 );\r
1726 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);\r
1727 ASSERT_EFI_ERROR (Status);\r
1728 ASSERT (Index == 0);\r
1729\r
1730 //\r
1731 // Drain any queued keys.\r
1732 //\r
1733 while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {\r
1734 //\r
1735 // just throw away Key\r
1736 //\r
1737 }\r
1738 }\r
1739\r
1740 for (;;) {\r
1741 EfiBootManagerBoot (&BootManagerMenu);\r
1742 }\r
1743}\r