]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/XenPlatformPei/Xen.c
OvmfPkg/XenPlatformPei: Relocate shared_info page mapping
[mirror_edk2.git] / OvmfPkg / XenPlatformPei / Xen.c
CommitLineData
3b96221f
AP
1/**@file\r
2 Xen Platform PEI support\r
3\r
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
5 Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>\r
6 Copyright (c) 2019, Citrix Systems, Inc.\r
7\r
8 SPDX-License-Identifier: BSD-2-Clause-Patent\r
9\r
10**/\r
11\r
12//\r
13// The package level header files this module uses\r
14//\r
15#include <PiPei.h>\r
16\r
17//\r
18// The Library classes this module consumes\r
19//\r
51e0bd28
AP
20#include <Library/BaseMemoryLib.h>\r
21#include <Library/CpuLib.h>\r
3b96221f
AP
22#include <Library/DebugLib.h>\r
23#include <Library/HobLib.h>\r
c75c6405 24#include <Library/LocalApicLib.h>\r
3b96221f
AP
25#include <Library/MemoryAllocationLib.h>\r
26#include <Library/PcdLib.h>\r
c75c6405 27#include <Library/SafeIntLib.h>\r
3b96221f
AP
28#include <Guid/XenInfo.h>\r
29#include <IndustryStandard/E820.h>\r
30#include <Library/ResourcePublicationLib.h>\r
31#include <Library/MtrrLib.h>\r
51e0bd28 32#include <IndustryStandard/PageTable.h>\r
60d26545 33#include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>\r
80b619d5 34#include <Library/XenHypercallLib.h>\r
a749e1f9 35#include <IndustryStandard/Xen/memory.h>\r
3b96221f
AP
36\r
37#include "Platform.h"\r
38#include "Xen.h"\r
39\r
40STATIC UINT32 mXenLeaf = 0;\r
41\r
42EFI_XEN_INFO mXenInfo;\r
43\r
8651e1ce
AP
44//\r
45// Location of the firmware info struct setup by hvmloader.\r
46// Only the E820 table is used by OVMF.\r
47//\r
48EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;\r
a749e1f9
AP
49STATIC EFI_E820_ENTRY64 mE820Entries[128];\r
50STATIC UINT32 mE820EntriesCount;\r
8651e1ce 51\r
3b96221f
AP
52/**\r
53 Returns E820 map provided by Xen\r
54\r
55 @param Entries Pointer to E820 map\r
56 @param Count Number of entries\r
57\r
58 @return EFI_STATUS\r
59**/\r
60EFI_STATUS\r
61XenGetE820Map (\r
62 EFI_E820_ENTRY64 **Entries,\r
63 UINT32 *Count\r
64 )\r
65{\r
a749e1f9
AP
66 INTN ReturnCode;\r
67 xen_memory_map_t Parameters;\r
68 UINTN LoopIndex;\r
69 UINTN Index;\r
70 EFI_E820_ENTRY64 TmpEntry;\r
71\r
594b5002
AP
72 //\r
73 // Get E820 produced by hvmloader\r
74 //\r
75 if (mXenHvmloaderInfo != NULL) {\r
76 ASSERT (mXenHvmloaderInfo->E820 < MAX_ADDRESS);\r
77 *Entries = (EFI_E820_ENTRY64 *)(UINTN) mXenHvmloaderInfo->E820;\r
78 *Count = mXenHvmloaderInfo->E820EntriesCount;\r
79\r
80 return EFI_SUCCESS;\r
3b96221f
AP
81 }\r
82\r
a749e1f9
AP
83 //\r
84 // Otherwise, get the E820 table from the Xen hypervisor\r
85 //\r
86\r
87 if (mE820EntriesCount > 0) {\r
88 *Entries = mE820Entries;\r
89 *Count = mE820EntriesCount;\r
90 return EFI_SUCCESS;\r
91 }\r
92\r
93 Parameters.nr_entries = 128;\r
94 set_xen_guest_handle (Parameters.buffer, mE820Entries);\r
95\r
96 // Returns a errno\r
97 ReturnCode = XenHypercallMemoryOp (XENMEM_memory_map, &Parameters);\r
98 ASSERT (ReturnCode == 0);\r
99\r
100 mE820EntriesCount = Parameters.nr_entries;\r
101\r
102 //\r
103 // Sort E820 entries\r
104 //\r
105 for (LoopIndex = 1; LoopIndex < mE820EntriesCount; LoopIndex++) {\r
106 for (Index = LoopIndex; Index < mE820EntriesCount; Index++) {\r
107 if (mE820Entries[Index - 1].BaseAddr > mE820Entries[Index].BaseAddr) {\r
108 TmpEntry = mE820Entries[Index];\r
109 mE820Entries[Index] = mE820Entries[Index - 1];\r
110 mE820Entries[Index - 1] = TmpEntry;\r
111 }\r
112 }\r
113 }\r
114\r
115 *Count = mE820EntriesCount;\r
116 *Entries = mE820Entries;\r
117\r
118 return EFI_SUCCESS;\r
3b96221f
AP
119}\r
120\r
121/**\r
122 Connects to the Hypervisor.\r
123\r
3b96221f
AP
124 @return EFI_STATUS\r
125\r
126**/\r
127EFI_STATUS\r
128XenConnect (\r
3b96221f
AP
129 )\r
130{\r
131 UINT32 Index;\r
132 UINT32 TransferReg;\r
133 UINT32 TransferPages;\r
134 UINT32 XenVersion;\r
8651e1ce
AP
135 EFI_XEN_OVMF_INFO *Info;\r
136 CHAR8 Sig[sizeof (Info->Signature) + 1];\r
60d26545 137 UINT32 *PVHResetVectorData;\r
80b619d5 138 RETURN_STATUS Status;\r
3b96221f 139\r
12998837
AP
140 ASSERT (mXenLeaf != 0);\r
141\r
142 //\r
143 // Prepare HyperPages to be able to make hypercalls\r
144 //\r
145\r
146 AsmCpuid (mXenLeaf + 2, &TransferPages, &TransferReg, NULL, NULL);\r
3b96221f
AP
147 mXenInfo.HyperPages = AllocatePages (TransferPages);\r
148 if (!mXenInfo.HyperPages) {\r
149 return EFI_OUT_OF_RESOURCES;\r
150 }\r
151\r
152 for (Index = 0; Index < TransferPages; Index++) {\r
153 AsmWriteMsr64 (TransferReg,\r
154 (UINTN) mXenInfo.HyperPages +\r
155 (Index << EFI_PAGE_SHIFT) + Index);\r
156 }\r
157\r
12998837
AP
158 //\r
159 // Find out the Xen version\r
160 //\r
161\r
162 AsmCpuid (mXenLeaf + 1, &XenVersion, NULL, NULL, NULL);\r
3b96221f
AP
163 DEBUG ((DEBUG_ERROR, "Detected Xen version %d.%d\n",\r
164 XenVersion >> 16, XenVersion & 0xFFFF));\r
165 mXenInfo.VersionMajor = (UINT16)(XenVersion >> 16);\r
166 mXenInfo.VersionMinor = (UINT16)(XenVersion & 0xFFFF);\r
167\r
8651e1ce
AP
168 //\r
169 // Check if there are information left by hvmloader\r
170 //\r
171\r
172 Info = (EFI_XEN_OVMF_INFO *)(UINTN) OVMF_INFO_PHYSICAL_ADDRESS;\r
173 //\r
174 // Copy the signature, and make it null-terminated.\r
175 //\r
176 AsciiStrnCpyS (Sig, sizeof (Sig), (CHAR8 *) &Info->Signature,\r
177 sizeof (Info->Signature));\r
178 if (AsciiStrCmp (Sig, "XenHVMOVMF") == 0) {\r
179 mXenHvmloaderInfo = Info;\r
180 } else {\r
181 mXenHvmloaderInfo = NULL;\r
182 }\r
3b96221f 183\r
60d26545
AP
184 mXenInfo.RsdpPvh = NULL;\r
185\r
186 //\r
187 // Locate and use information from the start of day structure if we have\r
188 // booted via the PVH entry point.\r
189 //\r
190\r
191 PVHResetVectorData = (VOID *)(UINTN) PcdGet32 (PcdXenPvhStartOfDayStructPtr);\r
192 //\r
193 // That magic value is written in XenResetVector/Ia32/XenPVHMain.asm\r
194 //\r
195 if (PVHResetVectorData[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {\r
196 struct hvm_start_info *HVMStartInfo;\r
197\r
198 HVMStartInfo = (VOID *)(UINTN) PVHResetVectorData[0];\r
199 if (HVMStartInfo->magic == XEN_HVM_START_MAGIC_VALUE) {\r
200 ASSERT (HVMStartInfo->rsdp_paddr != 0);\r
201 if (HVMStartInfo->rsdp_paddr != 0) {\r
202 mXenInfo.RsdpPvh = (VOID *)(UINTN)HVMStartInfo->rsdp_paddr;\r
203 }\r
204 }\r
205 }\r
206\r
3b96221f
AP
207 BuildGuidDataHob (\r
208 &gEfiXenInfoGuid,\r
209 &mXenInfo,\r
210 sizeof(mXenInfo)\r
211 );\r
212\r
80b619d5
AP
213 //\r
214 // Initialize the XenHypercall library, now that the XenInfo HOB is\r
215 // available\r
216 //\r
217 Status = XenHypercallLibInit ();\r
218 ASSERT_RETURN_ERROR (Status);\r
219\r
3b96221f
AP
220 return EFI_SUCCESS;\r
221}\r
222\r
223/**\r
224 Figures out if we are running inside Xen HVM.\r
225\r
226 @retval TRUE Xen was detected\r
227 @retval FALSE Xen was not detected\r
228\r
229**/\r
230BOOLEAN\r
231XenDetect (\r
232 VOID\r
233 )\r
234{\r
235 UINT8 Signature[13];\r
236\r
237 if (mXenLeaf != 0) {\r
238 return TRUE;\r
239 }\r
240\r
241 Signature[12] = '\0';\r
242 for (mXenLeaf = 0x40000000; mXenLeaf < 0x40010000; mXenLeaf += 0x100) {\r
243 AsmCpuid (mXenLeaf,\r
244 NULL,\r
245 (UINT32 *) &Signature[0],\r
246 (UINT32 *) &Signature[4],\r
247 (UINT32 *) &Signature[8]);\r
248\r
249 if (!AsciiStrCmp ((CHAR8 *) Signature, "XenVMMXenVMM")) {\r
250 return TRUE;\r
251 }\r
252 }\r
253\r
254 mXenLeaf = 0;\r
255 return FALSE;\r
256}\r
257\r
4022f7fa
AP
258BOOLEAN\r
259XenHvmloaderDetected (\r
260 VOID\r
261 )\r
262{\r
263 return (mXenHvmloaderInfo != NULL);\r
264}\r
3b96221f 265\r
64eac295
AP
266BOOLEAN\r
267XenPvhDetected (\r
268 VOID\r
269 )\r
270{\r
271 //\r
272 // This function should only be used after XenConnect\r
273 //\r
274 ASSERT (mXenInfo.HyperPages != NULL);\r
275\r
276 return mXenHvmloaderInfo == NULL;\r
277}\r
278\r
3b96221f
AP
279VOID\r
280XenPublishRamRegions (\r
281 VOID\r
282 )\r
283{\r
24465c38
AP
284 EFI_E820_ENTRY64 *E820Map;\r
285 UINT32 E820EntriesCount;\r
286 EFI_STATUS Status;\r
287 EFI_E820_ENTRY64 *Entry;\r
288 UINTN Index;\r
289 UINT64 LapicBase;\r
290 UINT64 LapicEnd;\r
291\r
3b96221f
AP
292\r
293 DEBUG ((DEBUG_INFO, "Using memory map provided by Xen\n"));\r
294\r
295 //\r
296 // Parse RAM in E820 map\r
297 //\r
298 E820EntriesCount = 0;\r
299 Status = XenGetE820Map (&E820Map, &E820EntriesCount);\r
3b96221f
AP
300 ASSERT_EFI_ERROR (Status);\r
301\r
77d35f50
AP
302 AddMemoryBaseSizeHob (0, 0xA0000);\r
303 //\r
304 // Video memory + Legacy BIOS region, to allow Linux to boot.\r
305 //\r
306 AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB - 0xA0000, TRUE);\r
307\r
24465c38
AP
308 LapicBase = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
309 LapicEnd = LapicBase + SIZE_1MB;\r
310 AddIoMemoryRangeHob (LapicBase, LapicEnd);\r
311\r
312 for (Index = 0; Index < E820EntriesCount; Index++) {\r
313 UINT64 Base;\r
314 UINT64 End;\r
315 UINT64 ReservedBase;\r
316 UINT64 ReservedEnd;\r
317\r
318 Entry = &E820Map[Index];\r
319\r
320 //\r
321 // Round up the start address, and round down the end address.\r
322 //\r
323 Base = ALIGN_VALUE (Entry->BaseAddr, (UINT64)EFI_PAGE_SIZE);\r
324 End = (Entry->BaseAddr + Entry->Length) & ~(UINT64)EFI_PAGE_MASK;\r
325\r
77d35f50
AP
326 //\r
327 // Ignore the first 1MB, this is handled before the loop.\r
328 //\r
329 if (Base < BASE_1MB) {\r
330 Base = BASE_1MB;\r
331 }\r
332 if (Base >= End) {\r
333 continue;\r
334 }\r
335\r
24465c38
AP
336 switch (Entry->Type) {\r
337 case EfiAcpiAddressRangeMemory:\r
338 AddMemoryRangeHob (Base, End);\r
339 break;\r
340 case EfiAcpiAddressRangeACPI:\r
341 AddReservedMemoryRangeHob (Base, End, FALSE);\r
342 break;\r
343 case EfiAcpiAddressRangeReserved:\r
344 //\r
345 // hvmloader marks a range that overlaps with the local APIC memory\r
346 // mapped region as reserved, but CpuDxe wants it as mapped IO. We\r
347 // have already added it as mapped IO, so skip it here.\r
348 //\r
3b96221f
AP
349\r
350 //\r
24465c38 351 // add LAPIC predecessor range, if any\r
3b96221f 352 //\r
24465c38
AP
353 ReservedBase = Base;\r
354 ReservedEnd = MIN (End, LapicBase);\r
355 if (ReservedBase < ReservedEnd) {\r
356 AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);\r
3b96221f
AP
357 }\r
358\r
24465c38
AP
359 //\r
360 // add LAPIC successor range, if any\r
361 //\r
362 ReservedBase = MAX (Base, LapicEnd);\r
363 ReservedEnd = End;\r
364 if (ReservedBase < ReservedEnd) {\r
365 AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);\r
366 }\r
367 break;\r
368 default:\r
369 break;\r
3b96221f
AP
370 }\r
371 }\r
372}\r
373\r
374\r
51e0bd28
AP
375EFI_STATUS\r
376PhysicalAddressIdentityMapping (\r
377 IN EFI_PHYSICAL_ADDRESS AddressToMap\r
378 )\r
379{\r
380 INTN Index;\r
381 PAGE_MAP_AND_DIRECTORY_POINTER *L4, *L3;\r
382 PAGE_TABLE_ENTRY *PageTable;\r
383\r
384 DEBUG ((DEBUG_INFO, "Mapping 1:1 of address 0x%lx\n", (UINT64)AddressToMap));\r
385\r
386 // L4 / Top level Page Directory Pointers\r
387\r
388 L4 = (VOID*)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase);\r
389 Index = PML4_OFFSET (AddressToMap);\r
390\r
391 if (!L4[Index].Bits.Present) {\r
392 L3 = AllocatePages (1);\r
393 if (L3 == NULL) {\r
394 return EFI_OUT_OF_RESOURCES;\r
395 }\r
396\r
397 ZeroMem (L3, EFI_PAGE_SIZE);\r
398\r
399 L4[Index].Bits.ReadWrite = 1;\r
400 L4[Index].Bits.Accessed = 1;\r
401 L4[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)L3 >> 12;\r
402 L4[Index].Bits.Present = 1;\r
403 }\r
404\r
405 // L3 / Next level Page Directory Pointers\r
406\r
407 L3 = (VOID*)(EFI_PHYSICAL_ADDRESS)(L4[Index].Bits.PageTableBaseAddress << 12);\r
408 Index = PDP_OFFSET (AddressToMap);\r
409\r
410 if (!L3[Index].Bits.Present) {\r
411 PageTable = AllocatePages (1);\r
412 if (PageTable == NULL) {\r
413 return EFI_OUT_OF_RESOURCES;\r
414 }\r
415\r
416 ZeroMem (PageTable, EFI_PAGE_SIZE);\r
417\r
418 L3[Index].Bits.ReadWrite = 1;\r
419 L3[Index].Bits.Accessed = 1;\r
420 L3[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)PageTable >> 12;\r
421 L3[Index].Bits.Present = 1;\r
422 }\r
423\r
424 // L2 / Page Table Entries\r
425\r
426 PageTable = (VOID*)(EFI_PHYSICAL_ADDRESS)(L3[Index].Bits.PageTableBaseAddress << 12);\r
427 Index = PDE_OFFSET (AddressToMap);\r
428\r
429 if (!PageTable[Index].Bits.Present) {\r
430 PageTable[Index].Bits.ReadWrite = 1;\r
431 PageTable[Index].Bits.Accessed = 1;\r
432 PageTable[Index].Bits.Dirty = 1;\r
433 PageTable[Index].Bits.MustBe1 = 1;\r
434 PageTable[Index].Bits.PageTableBaseAddress = AddressToMap >> 21;\r
435 PageTable[Index].Bits.Present = 1;\r
436 }\r
437\r
438 CpuFlushTlb ();\r
439\r
440 return EFI_SUCCESS;\r
441}\r
c75c6405
AP
442\r
443STATIC\r
444EFI_STATUS\r
445MapSharedInfoPage (\r
446 IN VOID *PagePtr\r
447 )\r
448{\r
449 xen_add_to_physmap_t Parameters;\r
450 INTN ReturnCode;\r
451\r
452 Parameters.domid = DOMID_SELF;\r
453 Parameters.space = XENMAPSPACE_shared_info;\r
454 Parameters.idx = 0;\r
455 Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;\r
456 ReturnCode = XenHypercallMemoryOp (XENMEM_add_to_physmap, &Parameters);\r
457 if (ReturnCode != 0) {\r
458 return EFI_NO_MAPPING;\r
459 }\r
460 return EFI_SUCCESS;\r
461}\r
462\r
463STATIC\r
464VOID\r
465UnmapXenPage (\r
466 IN VOID *PagePtr\r
467 )\r
468{\r
469 xen_remove_from_physmap_t Parameters;\r
470 INTN ReturnCode;\r
471\r
472 Parameters.domid = DOMID_SELF;\r
473 Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;\r
474 ReturnCode = XenHypercallMemoryOp (XENMEM_remove_from_physmap, &Parameters);\r
475 ASSERT (ReturnCode == 0);\r
476}\r
477\r
478\r
479STATIC\r
480UINT64\r
481GetCpuFreq (\r
482 IN XEN_VCPU_TIME_INFO *VcpuTime\r
483 )\r
484{\r
485 UINT32 Version;\r
486 UINT32 TscToSystemMultiplier;\r
487 INT8 TscShift;\r
488 UINT64 CpuFreq;\r
489\r
490 do {\r
491 Version = VcpuTime->Version;\r
492 MemoryFence ();\r
493 TscToSystemMultiplier = VcpuTime->TscToSystemMultiplier;\r
494 TscShift = VcpuTime->TscShift;\r
495 MemoryFence ();\r
496 } while (((Version & 1) != 0) && (Version != VcpuTime->Version));\r
497\r
498 CpuFreq = DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier);\r
499 if (TscShift >= 0) {\r
500 CpuFreq = RShiftU64 (CpuFreq, TscShift);\r
501 } else {\r
502 CpuFreq = LShiftU64 (CpuFreq, -TscShift);\r
503 }\r
504 return CpuFreq;\r
505}\r
506\r
507STATIC\r
508VOID\r
509XenDelay (\r
510 IN XEN_VCPU_TIME_INFO *VcpuTimeInfo,\r
511 IN UINT64 DelayNs\r
512 )\r
513{\r
514 UINT64 Tick;\r
515 UINT64 CpuFreq;\r
516 UINT64 Delay;\r
517 UINT64 DelayTick;\r
518 UINT64 NewTick;\r
519 RETURN_STATUS Status;\r
520\r
521 Tick = AsmReadTsc ();\r
522\r
523 CpuFreq = GetCpuFreq (VcpuTimeInfo);\r
524 Status = SafeUint64Mult (DelayNs, CpuFreq, &Delay);\r
525 if (EFI_ERROR (Status)) {\r
526 DEBUG ((DEBUG_ERROR,\r
527 "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",\r
528 DelayNs, CpuFreq));\r
529 ASSERT_EFI_ERROR (Status);\r
530 CpuDeadLoop ();\r
531 }\r
532\r
533 DelayTick = DivU64x32 (Delay, 1000000000);\r
534\r
535 NewTick = Tick + DelayTick;\r
536\r
537 //\r
538 // Check for overflow\r
539 //\r
540 if (NewTick < Tick) {\r
541 //\r
542 // Overflow, wait for TSC to also overflow\r
543 //\r
544 while (AsmReadTsc () >= Tick) {\r
545 CpuPause ();\r
546 }\r
547 }\r
548\r
549 while (AsmReadTsc () <= NewTick) {\r
550 CpuPause ();\r
551 }\r
552}\r
553\r
554\r
555/**\r
556 Calculate the frequency of the Local Apic Timer\r
557**/\r
558VOID\r
559CalibrateLapicTimer (\r
560 VOID\r
561 )\r
562{\r
563 XEN_SHARED_INFO *SharedInfo;\r
564 XEN_VCPU_TIME_INFO *VcpuTimeInfo;\r
565 UINT32 TimerTick, TimerTick2, DiffTimer;\r
566 UINT64 TscTick, TscTick2;\r
567 UINT64 Freq;\r
568 UINT64 Dividend;\r
569 EFI_STATUS Status;\r
570\r
571\r
b37cfdd2 572 SharedInfo = (VOID*)((UINTN)PcdGet32 (PcdCpuLocalApicBaseAddress) + SIZE_1MB);\r
c75c6405
AP
573 Status = PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS)SharedInfo);\r
574 if (EFI_ERROR (Status)) {\r
575 DEBUG ((DEBUG_ERROR,\r
576 "Failed to add page table entry for Xen shared info page: %r\n",\r
577 Status));\r
578 ASSERT_EFI_ERROR (Status);\r
579 return;\r
580 }\r
581\r
582 Status = MapSharedInfoPage (SharedInfo);\r
583 if (EFI_ERROR (Status)) {\r
584 DEBUG ((DEBUG_ERROR, "Failed to map Xen's shared info page: %r\n",\r
585 Status));\r
586 ASSERT_EFI_ERROR (Status);\r
587 return;\r
588 }\r
589\r
590 VcpuTimeInfo = &SharedInfo->VcpuInfo[0].Time;\r
591\r
592 InitializeApicTimer (1, MAX_UINT32, TRUE, 0);\r
593 DisableApicTimerInterrupt ();\r
594\r
595 TimerTick = GetApicTimerCurrentCount ();\r
596 TscTick = AsmReadTsc ();\r
597 XenDelay (VcpuTimeInfo, 1000000ULL);\r
598 TimerTick2 = GetApicTimerCurrentCount ();\r
599 TscTick2 = AsmReadTsc ();\r
600\r
601\r
602 DiffTimer = TimerTick - TimerTick2;\r
603 Status = SafeUint64Mult (GetCpuFreq (VcpuTimeInfo), DiffTimer, &Dividend);\r
604 if (EFI_ERROR (Status)) {\r
605 DEBUG ((DEBUG_ERROR, "overflow while calculating APIC frequency\n"));\r
606 DEBUG ((DEBUG_ERROR, "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",\r
607 GetCpuFreq (VcpuTimeInfo), DiffTimer));\r
608 ASSERT_EFI_ERROR (Status);\r
609 CpuDeadLoop ();\r
610 }\r
611\r
612 Freq = DivU64x64Remainder (Dividend, TscTick2 - TscTick, NULL);\r
613 DEBUG ((DEBUG_INFO, "APIC Freq % 8lu Hz\n", Freq));\r
614\r
71cdb91f
AP
615 ASSERT (Freq <= MAX_UINT32);\r
616 Status = PcdSet32S (PcdFSBClock, (UINT32)Freq);\r
617 ASSERT_EFI_ERROR (Status);\r
618\r
c75c6405
AP
619 UnmapXenPage (SharedInfo);\r
620}\r