]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/CpuExceptionHandlerLib/UnitTest/CpuExceptionHandlerTestCommon.c
UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
[mirror_edk2.git] / UefiCpuPkg / Library / CpuExceptionHandlerLib / UnitTest / CpuExceptionHandlerTestCommon.c
CommitLineData
beabde58
TD
1/** @file\r
2 Unit tests of the CpuExceptionHandlerLib.\r
3\r
4 Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include "CpuExceptionHandlerTest.h"\r
10\r
11//\r
12// Length of the assembly falut instruction.\r
13//\r
14UINTN mFaultInstructionLength = 0;\r
15EFI_EXCEPTION_TYPE mExceptionType = 256;\r
16UINTN mNumberOfProcessors = 1;\r
17UINTN mRspAddress[2] = { 0 };\r
18\r
19//\r
20// Error code flag indicating whether or not an error code will be\r
21// pushed on the stack if an exception occurs.\r
22//\r
23// 1 means an error code will be pushed, otherwise 0\r
24//\r
25CONST UINT32 mErrorCodeExceptionFlag = 0x20227d00;\r
26\r
27/**\r
28 Special handler for exception triggered by INTn instruction.\r
29 This hanlder only modifies a global variable for check.\r
30\r
31 @param ExceptionType Exception type.\r
32 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.\r
33**/\r
34VOID\r
35EFIAPI\r
36INTnExceptionHandler (\r
37 IN EFI_EXCEPTION_TYPE ExceptionType,\r
38 IN EFI_SYSTEM_CONTEXT SystemContext\r
39 )\r
40{\r
41 mExceptionType = ExceptionType;\r
42}\r
43\r
44/**\r
45 Restore cpu original registers before exit test case.\r
46\r
47 @param[in] Buffer Argument of the procedure.\r
48**/\r
49VOID\r
50EFIAPI\r
51RestoreRegistersPerCpu (\r
52 IN VOID *Buffer\r
53 )\r
54{\r
55 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer;\r
56 UINT16 Tr;\r
57 IA32_TSS_DESCRIPTOR *Tss;\r
58\r
59 CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer;\r
60\r
61 AsmWriteGdtr (&(CpuOriginalRegisterBuffer->OriginalGdtr));\r
62 AsmWriteIdtr (&(CpuOriginalRegisterBuffer->OriginalIdtr));\r
63 Tr = CpuOriginalRegisterBuffer->Tr;\r
64 if ((Tr != 0) && (Tr < CpuOriginalRegisterBuffer->OriginalGdtr.Limit)) {\r
65 Tss = (IA32_TSS_DESCRIPTOR *)(CpuOriginalRegisterBuffer->OriginalGdtr.Base + Tr);\r
66 if (Tss->Bits.P == 1) {\r
67 //\r
68 // Clear busy bit of TSS before write Tr\r
69 //\r
70 Tss->Bits.Type &= 0xD;\r
71 AsmWriteTr (Tr);\r
72 }\r
73 }\r
74}\r
75\r
76/**\r
77 Restore cpu original registers before exit test case.\r
78\r
79 @param[in] MpServices MpServices.\r
80 @param[in] CpuOriginalRegisterBuffer Address of CpuOriginalRegisterBuffer.\r
81 @param[in] BspProcessorNum Bsp processor number.\r
82**/\r
83VOID\r
84RestoreAllCpuRegisters (\r
85 MP_SERVICES *MpServices, OPTIONAL\r
86 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer,\r
87 UINTN BspProcessorNum\r
88 )\r
89{\r
90 UINTN Index;\r
91 EFI_STATUS Status;\r
92\r
93 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
94 if (Index == BspProcessorNum) {\r
95 RestoreRegistersPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);\r
96 continue;\r
97 }\r
98\r
99 ASSERT (MpServices != NULL);\r
100 Status = MpServicesUnitTestStartupThisAP (\r
101 *MpServices,\r
102 (EFI_AP_PROCEDURE)RestoreRegistersPerCpu,\r
103 Index,\r
104 0,\r
105 (VOID *)&CpuOriginalRegisterBuffer[Index]\r
106 );\r
107 ASSERT_EFI_ERROR (Status);\r
108 }\r
109}\r
110\r
111/**\r
112 Store cpu registers before the test case starts.\r
113\r
114 @param[in] Buffer Argument of the procedure.\r
115**/\r
116VOID\r
117EFIAPI\r
118SaveRegisterPerCpu (\r
119 IN VOID *Buffer\r
120 )\r
121{\r
122 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer;\r
123 IA32_DESCRIPTOR Gdtr;\r
124 IA32_DESCRIPTOR Idtr;\r
125\r
126 CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer;\r
127\r
128 AsmReadGdtr (&Gdtr);\r
129 AsmReadIdtr (&Idtr);\r
130 CpuOriginalRegisterBuffer->OriginalGdtr.Base = Gdtr.Base;\r
131 CpuOriginalRegisterBuffer->OriginalGdtr.Limit = Gdtr.Limit;\r
132 CpuOriginalRegisterBuffer->OriginalIdtr.Base = Idtr.Base;\r
133 CpuOriginalRegisterBuffer->OriginalIdtr.Limit = Idtr.Limit;\r
134 CpuOriginalRegisterBuffer->Tr = AsmReadTr ();\r
135}\r
136\r
137/**\r
138 Store cpu registers before the test case starts.\r
139\r
140 @param[in] MpServices MpServices.\r
141 @param[in] BspProcessorNum Bsp processor number.\r
142\r
143 @return Pointer to the allocated CPU_REGISTER_BUFFER.\r
144**/\r
145CPU_REGISTER_BUFFER *\r
146SaveAllCpuRegisters (\r
147 MP_SERVICES *MpServices, OPTIONAL\r
148 UINTN BspProcessorNum\r
149 )\r
150{\r
151 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer;\r
152 EFI_STATUS Status;\r
153 UINTN Index;\r
154\r
155 CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (CPU_REGISTER_BUFFER));\r
156 ASSERT (CpuOriginalRegisterBuffer != NULL);\r
157\r
158 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
159 if (Index == BspProcessorNum) {\r
160 SaveRegisterPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);\r
161 continue;\r
162 }\r
163\r
164 ASSERT (MpServices != NULL);\r
165 Status = MpServicesUnitTestStartupThisAP (\r
166 *MpServices,\r
167 (EFI_AP_PROCEDURE)SaveRegisterPerCpu,\r
168 Index,\r
169 0,\r
170 (VOID *)&CpuOriginalRegisterBuffer[Index]\r
171 );\r
172 ASSERT_EFI_ERROR (Status);\r
173 }\r
174\r
175 return CpuOriginalRegisterBuffer;\r
176}\r
177\r
178/**\r
179 Initialize Ap Idt Procedure.\r
180\r
181 @param[in] Buffer Argument of the procedure.\r
182**/\r
183VOID\r
184EFIAPI\r
185InitializeIdtPerAp (\r
186 IN VOID *Buffer\r
187 )\r
188{\r
189 AsmWriteIdtr (Buffer);\r
190}\r
191\r
192/**\r
193 Initialize all Ap Idt.\r
194\r
195 @param[in] MpServices MpServices.\r
196 @param[in] BspIdtr Pointer to IA32_DESCRIPTOR allocated by Bsp.\r
197**/\r
198VOID\r
199InitializeApIdt (\r
200 MP_SERVICES MpServices,\r
201 VOID *BspIdtr\r
202 )\r
203{\r
204 EFI_STATUS Status;\r
205\r
206 Status = MpServicesUnitTestStartupAllAPs (\r
207 MpServices,\r
208 (EFI_AP_PROCEDURE)InitializeIdtPerAp,\r
209 FALSE,\r
210 0,\r
211 BspIdtr\r
212 );\r
213 ASSERT_EFI_ERROR (Status);\r
214}\r
215\r
216/**\r
217 Check if exception handler can registered/unregistered for no error code exception.\r
218\r
219 @param[in] Context [Optional] An optional parameter that enables:\r
220 1) test-case reuse with varied parameters and\r
221 2) test-case re-entry for Target tests that need a\r
222 reboot. This parameter is a VOID* and it is the\r
223 responsibility of the test author to ensure that the\r
224 contents are well understood by all test cases that may\r
225 consume it.\r
226\r
227 @retval UNIT_TEST_PASSED The Unit test has completed and the test\r
228 case was successful.\r
229 @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed.\r
230**/\r
231UNIT_TEST_STATUS\r
232EFIAPI\r
233TestRegisterHandlerForNoErrorCodeException (\r
234 IN UNIT_TEST_CONTEXT Context\r
235 )\r
236{\r
237 EFI_STATUS Status;\r
238 UINTN Index;\r
239 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer;\r
240 VOID *NewIdtr;\r
241\r
242 CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0);\r
243 NewIdtr = InitializeBspIdt ();\r
244 Status = InitializeCpuExceptionHandlers (NULL);\r
245 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
246\r
247 for (Index = 0; Index < SPEC_MAX_EXCEPTION_NUM; Index++) {\r
248 //\r
249 // Only test no error code exception by INT n instruction.\r
250 //\r
251 if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) {\r
252 continue;\r
253 }\r
254\r
255 DEBUG ((DEBUG_INFO, "TestCase1: ExceptionType is %d\n", Index));\r
256 Status = RegisterCpuInterruptHandler (Index, INTnExceptionHandler);\r
257 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
258\r
259 TriggerINTnException (Index);\r
260 UT_ASSERT_EQUAL (mExceptionType, Index);\r
261 Status = RegisterCpuInterruptHandler (Index, NULL);\r
262 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
263 }\r
264\r
265 RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0);\r
266 FreePool (CpuOriginalRegisterBuffer);\r
267 FreePool (NewIdtr);\r
268 return UNIT_TEST_PASSED;\r
269}\r
270\r
271/**\r
272 Get Bsp stack base.\r
273\r
274 @param[out] StackBase Pointer to stack base of BSP.\r
275**/\r
276VOID\r
277GetBspStackBase (\r
278 OUT UINTN *StackBase\r
279 )\r
280{\r
281 EFI_PEI_HOB_POINTERS Hob;\r
282 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;\r
283\r
284 //\r
285 // Get the base of stack from Hob.\r
286 //\r
287 ASSERT (StackBase != NULL);\r
288 Hob.Raw = GetHobList ();\r
289 while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {\r
290 MemoryHob = Hob.MemoryAllocation;\r
291 if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) {\r
292 DEBUG ((\r
293 DEBUG_INFO,\r
294 "%a: Bsp StackBase = 0x%016lx StackSize = 0x%016lx\n",\r
295 __FUNCTION__,\r
296 MemoryHob->AllocDescriptor.MemoryBaseAddress,\r
297 MemoryHob->AllocDescriptor.MemoryLength\r
298 ));\r
299\r
300 *StackBase = (UINTN)MemoryHob->AllocDescriptor.MemoryBaseAddress;\r
301 //\r
302 // Ensure the base of the stack is page-size aligned.\r
303 //\r
304 ASSERT ((*StackBase & EFI_PAGE_MASK) == 0);\r
305 break;\r
306 }\r
307\r
308 Hob.Raw = GET_NEXT_HOB (Hob);\r
309 }\r
310\r
311 ASSERT (*StackBase != 0);\r
312}\r
313\r
314/**\r
315 Get Ap stack base procedure.\r
316\r
317 @param[out] ApStackBase Pointer to Ap stack base.\r
318**/\r
319VOID\r
320EFIAPI\r
321GetStackBasePerAp (\r
322 OUT VOID *ApStackBase\r
323 )\r
324{\r
325 UINTN ApTopOfStack;\r
326\r
327 ApTopOfStack = ALIGN_VALUE ((UINTN)&ApTopOfStack, (UINTN)PcdGet32 (PcdCpuApStackSize));\r
328 *(UINTN *)ApStackBase = ApTopOfStack - (UINTN)PcdGet32 (PcdCpuApStackSize);\r
329}\r
330\r
331/**\r
332 Get all Cpu stack base.\r
333\r
334 @param[in] MpServices MpServices.\r
335 @param[in] BspProcessorNum Bsp processor number.\r
336\r
337 @return Pointer to the allocated CpuStackBaseBuffer.\r
338**/\r
339UINTN *\r
340GetAllCpuStackBase (\r
341 MP_SERVICES *MpServices,\r
342 UINTN BspProcessorNum\r
343 )\r
344{\r
345 UINTN *CpuStackBaseBuffer;\r
346 EFI_STATUS Status;\r
347 UINTN Index;\r
348\r
349 CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (UINTN));\r
350 ASSERT (CpuStackBaseBuffer != NULL);\r
351\r
352 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
353 if (Index == BspProcessorNum) {\r
354 GetBspStackBase (&CpuStackBaseBuffer[Index]);\r
355 continue;\r
356 }\r
357\r
358 ASSERT (MpServices != NULL);\r
359 Status = MpServicesUnitTestStartupThisAP (\r
360 *MpServices,\r
361 (EFI_AP_PROCEDURE)GetStackBasePerAp,\r
362 Index,\r
363 0,\r
364 (VOID *)&CpuStackBaseBuffer[Index]\r
365 );\r
366 ASSERT_EFI_ERROR (Status);\r
367 DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, CpuStackBaseBuffer[Index]));\r
368 }\r
369\r
370 return CpuStackBaseBuffer;\r
371}\r
372\r
373/**\r
374 Find not present or ReadOnly address in page table.\r
375\r
376 @param[out] PFAddress Access to the address which is not permitted will trigger PF exceptions.\r
377\r
378 @retval TRUE Found not present or ReadOnly address in page table.\r
379 @retval FALSE Failed to found PFAddress in page table.\r
380**/\r
381BOOLEAN\r
382FindPFAddressInPageTable (\r
383 OUT UINTN *PFAddress\r
384 )\r
385{\r
386 IA32_CR0 Cr0;\r
387 IA32_CR4 Cr4;\r
388 UINTN PageTable;\r
389 PAGING_MODE PagingMode;\r
390 BOOLEAN Enable5LevelPaging;\r
391 RETURN_STATUS Status;\r
392 IA32_MAP_ENTRY *Map;\r
393 UINTN MapCount;\r
394 UINTN Index;\r
395 UINTN PreviousAddress;\r
396\r
397 ASSERT (PFAddress != NULL);\r
398\r
399 Cr0.UintN = AsmReadCr0 ();\r
400 if (Cr0.Bits.PG == 0) {\r
401 return FALSE;\r
402 }\r
403\r
404 PageTable = AsmReadCr3 ();\r
405 Cr4.UintN = AsmReadCr4 ();\r
406 if (sizeof (UINTN) == sizeof (UINT32)) {\r
407 ASSERT (Cr4.Bits.PAE == 1);\r
408 PagingMode = PagingPae;\r
409 } else {\r
410 Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);\r
411 PagingMode = Enable5LevelPaging ? Paging5Level : Paging4Level;\r
412 }\r
413\r
414 MapCount = 0;\r
415 Status = PageTableParse (PageTable, PagingMode, NULL, &MapCount);\r
416 ASSERT (Status == RETURN_BUFFER_TOO_SMALL);\r
417 Map = AllocatePages (EFI_SIZE_TO_PAGES (MapCount * sizeof (IA32_MAP_ENTRY)));\r
418 Status = PageTableParse (PageTable, PagingMode, Map, &MapCount);\r
419 ASSERT (Status == RETURN_SUCCESS);\r
420\r
421 PreviousAddress = 0;\r
422 for (Index = 0; Index < MapCount; Index++) {\r
423 DEBUG ((\r
424 DEBUG_ERROR,\r
425 "%02d: %016lx - %016lx, %016lx\n",\r
426 Index,\r
427 Map[Index].LinearAddress,\r
428 Map[Index].LinearAddress + Map[Index].Length,\r
429 Map[Index].Attribute.Uint64\r
430 ));\r
431\r
432 //\r
433 // Not present address in page table.\r
434 //\r
435 if (Map[Index].LinearAddress > PreviousAddress) {\r
436 *PFAddress = PreviousAddress;\r
437 return TRUE;\r
438 }\r
439\r
440 PreviousAddress = (UINTN)(Map[Index].LinearAddress + Map[Index].Length);\r
441\r
442 //\r
443 // ReadOnly address in page table.\r
444 //\r
445 if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) {\r
446 *PFAddress = (UINTN)Map[Index].LinearAddress;\r
447 return TRUE;\r
448 }\r
449 }\r
450\r
451 return FALSE;\r
452}\r
453\r
454/**\r
455 Test if exception handler can registered/unregistered for GP and PF.\r
456\r
457 @param[in] Context [Optional] An optional parameter that enables:\r
458 1) test-case reuse with varied parameters and\r
459 2) test-case re-entry for Target tests that need a\r
460 reboot. This parameter is a VOID* and it is the\r
461 responsibility of the test author to ensure that the\r
462 contents are well understood by all test cases that may\r
463 consume it.\r
464\r
465 @retval UNIT_TEST_PASSED The Unit test has completed and the test\r
466 case was successful.\r
467 @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed.\r
468**/\r
469UNIT_TEST_STATUS\r
470EFIAPI\r
471TestRegisterHandlerForGPAndPF (\r
472 IN UNIT_TEST_CONTEXT Context\r
473 )\r
474{\r
475 EFI_STATUS Status;\r
476 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer;\r
477 UINTN PFAddress;\r
478 VOID *NewIdtr;\r
479\r
480 PFAddress = 0;\r
481 CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0);\r
482 NewIdtr = InitializeBspIdt ();\r
483 Status = InitializeCpuExceptionHandlers (NULL);\r
484\r
485 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
486\r
487 //\r
488 // GP exception.\r
489 //\r
490 DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_GP_FAULT));\r
491 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, AdjustRipForFaultHandler);\r
492 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
493\r
494 TriggerGPException (CR4_RESERVED_BIT);\r
495 UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_GP_FAULT);\r
496 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, NULL);\r
497 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
498\r
499 //\r
500 // PF exception.\r
501 //\r
502 if (FindPFAddressInPageTable (&PFAddress)) {\r
503 DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_PAGE_FAULT));\r
504 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, AdjustRipForFaultHandler);\r
505 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
506 TriggerPFException (PFAddress);\r
507\r
508 UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_PAGE_FAULT);\r
509 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL);\r
510 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
511 }\r
512\r
513 RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0);\r
514 FreePool (CpuOriginalRegisterBuffer);\r
515 FreePool (NewIdtr);\r
516 return UNIT_TEST_PASSED;\r
517}\r
518\r
519/**\r
520 Test if Cpu Context is consistent before and after exception.\r
521\r
522 @param[in] Context [Optional] An optional parameter that enables:\r
523 1) test-case reuse with varied parameters and\r
524 2) test-case re-entry for Target tests that need a\r
525 reboot. This parameter is a VOID* and it is the\r
526 responsibility of the test author to ensure that the\r
527 contents are well understood by all test cases that may\r
528 consume it.\r
529\r
530 @retval UNIT_TEST_PASSED The Unit test has completed and the test\r
531 case was successful.\r
532 @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed.\r
533**/\r
534UNIT_TEST_STATUS\r
535EFIAPI\r
536TestCpuContextConsistency (\r
537 IN UNIT_TEST_CONTEXT Context\r
538 )\r
539{\r
540 EFI_STATUS Status;\r
541 UINTN Index;\r
542 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer;\r
543 UINTN FaultParameter;\r
544 VOID *NewIdtr;\r
545\r
546 FaultParameter = 0;\r
547 CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0);\r
548 NewIdtr = InitializeBspIdt ();\r
549 Status = InitializeCpuExceptionHandlers (NULL);\r
550 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
551\r
552 for (Index = 0; Index < 22; Index++) {\r
553 if (Index == EXCEPT_IA32_PAGE_FAULT) {\r
554 if (!FindPFAddressInPageTable (&FaultParameter)) {\r
555 continue;\r
556 }\r
557 } else if (Index == EXCEPT_IA32_GP_FAULT) {\r
558 FaultParameter = CR4_RESERVED_BIT;\r
559 } else {\r
560 if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) {\r
561 continue;\r
562 }\r
563 }\r
564\r
565 DEBUG ((DEBUG_INFO, "TestCase3: ExceptionType is %d\n", Index));\r
566 Status = RegisterCpuInterruptHandler (Index, AdjustCpuContextHandler);\r
567 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
568\r
569 //\r
570 // Trigger different type exception and compare different stage cpu context.\r
571 //\r
572 AsmTestConsistencyOfCpuContext (Index, FaultParameter);\r
573 CompareCpuContext ();\r
574 Status = RegisterCpuInterruptHandler (Index, NULL);\r
575 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
576 }\r
577\r
578 RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0);\r
579 FreePool (CpuOriginalRegisterBuffer);\r
580 FreePool (NewIdtr);\r
581 return UNIT_TEST_PASSED;\r
582}\r
583\r
584/**\r
585 Initializes CPU exceptions handlers for the sake of stack switch requirement.\r
586\r
587 This function is a wrapper of InitializeSeparateExceptionStacks. It's mainly\r
588 for the sake of AP's init because of EFI_AP_PROCEDURE API requirement.\r
589\r
590 @param[in,out] Buffer The pointer to private data buffer.\r
591\r
592**/\r
593VOID\r
594EFIAPI\r
595InitializeExceptionStackSwitchHandlersPerAp (\r
596 IN OUT VOID *Buffer\r
597 )\r
598{\r
599 EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData;\r
600\r
601 CpuSwitchStackData = (EXCEPTION_STACK_SWITCH_CONTEXT *)Buffer;\r
602\r
603 //\r
604 // This may be called twice for each Cpu. Only run InitializeSeparateExceptionStacks\r
605 // if this is the first call or the first call failed because of size too small.\r
606 //\r
607 if ((CpuSwitchStackData->Status == EFI_NOT_STARTED) || (CpuSwitchStackData->Status == EFI_BUFFER_TOO_SMALL)) {\r
608 CpuSwitchStackData->Status = InitializeSeparateExceptionStacks (CpuSwitchStackData->Buffer, &CpuSwitchStackData->BufferSize);\r
609 }\r
610}\r
611\r
612/**\r
613 Initializes MP exceptions handlers for the sake of stack switch requirement.\r
614\r
615 This function will allocate required resources required to setup stack switch\r
616 and pass them through SwitchStackData to each logic processor.\r
617\r
618 @param[in, out] MpServices MpServices.\r
619 @param[in, out] BspProcessorNum Bsp processor number.\r
620\r
621 @return Pointer to the allocated SwitchStackData.\r
622**/\r
623EXCEPTION_STACK_SWITCH_CONTEXT *\r
624InitializeMpExceptionStackSwitchHandlers (\r
625 MP_SERVICES MpServices,\r
626 UINTN BspProcessorNum\r
627 )\r
628{\r
629 UINTN Index;\r
630 EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData;\r
631 UINTN BufferSize;\r
632 EFI_STATUS Status;\r
633 UINT8 *Buffer;\r
634\r
635 SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof (EXCEPTION_STACK_SWITCH_CONTEXT));\r
636 ASSERT (SwitchStackData != NULL);\r
637 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
638 //\r
639 // Because the procedure may runs multiple times, use the status EFI_NOT_STARTED\r
640 // to indicate the procedure haven't been run yet.\r
641 //\r
642 SwitchStackData[Index].Status = EFI_NOT_STARTED;\r
643 if (Index == BspProcessorNum) {\r
644 InitializeExceptionStackSwitchHandlersPerAp ((VOID *)&SwitchStackData[Index]);\r
645 continue;\r
646 }\r
647\r
648 Status = MpServicesUnitTestStartupThisAP (\r
649 MpServices,\r
650 InitializeExceptionStackSwitchHandlersPerAp,\r
651 Index,\r
652 0,\r
653 (VOID *)&SwitchStackData[Index]\r
654 );\r
655 ASSERT_EFI_ERROR (Status);\r
656 }\r
657\r
658 BufferSize = 0;\r
659 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
660 if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) {\r
661 ASSERT (SwitchStackData[Index].BufferSize != 0);\r
662 BufferSize += SwitchStackData[Index].BufferSize;\r
663 } else {\r
664 ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS);\r
665 ASSERT (SwitchStackData[Index].BufferSize == 0);\r
666 }\r
667 }\r
668\r
669 if (BufferSize != 0) {\r
670 Buffer = AllocateZeroPool (BufferSize);\r
671 ASSERT (Buffer != NULL);\r
672 BufferSize = 0;\r
673 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
674 if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) {\r
675 SwitchStackData[Index].Buffer = (VOID *)(&Buffer[BufferSize]);\r
676 BufferSize += SwitchStackData[Index].BufferSize;\r
677 DEBUG ((\r
678 DEBUG_INFO,\r
679 "Buffer[cpu%lu] for InitializeExceptionStackSwitchHandlersPerAp: 0x%lX with size 0x%lX\n",\r
680 (UINT64)(UINTN)Index,\r
681 (UINT64)(UINTN)SwitchStackData[Index].Buffer,\r
682 (UINT64)(UINTN)SwitchStackData[Index].BufferSize\r
683 ));\r
684 }\r
685 }\r
686\r
687 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
688 if (Index == BspProcessorNum) {\r
689 InitializeExceptionStackSwitchHandlersPerAp ((VOID *)&SwitchStackData[Index]);\r
690 continue;\r
691 }\r
692\r
693 Status = MpServicesUnitTestStartupThisAP (\r
694 MpServices,\r
695 InitializeExceptionStackSwitchHandlersPerAp,\r
696 Index,\r
697 0,\r
698 (VOID *)&SwitchStackData[Index]\r
699 );\r
700 ASSERT_EFI_ERROR (Status);\r
701 }\r
702\r
703 for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
704 ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS);\r
705 }\r
706 }\r
707\r
708 return SwitchStackData;\r
709}\r
710\r
711/**\r
712 Test if stack overflow is captured by CpuStackGuard in both Bsp and AP.\r
713\r
714 @param[in] Context [Optional] An optional parameter that enables:\r
715 1) test-case reuse with varied parameters and\r
716 2) test-case re-entry for Target tests that need a\r
717 reboot. This parameter is a VOID* and it is the\r
718 responsibility of the test author to ensure that the\r
719 contents are well understood by all test cases that may\r
720 consume it.\r
721\r
722 @retval UNIT_TEST_PASSED The Unit test has completed and the test\r
723 case was successful.\r
724 @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed.\r
725**/\r
726UNIT_TEST_STATUS\r
727EFIAPI\r
728TestCpuStackGuardInBspAndAp (\r
729 IN UNIT_TEST_CONTEXT Context\r
730 )\r
731{\r
732 EFI_STATUS Status;\r
733 UINTN OriginalStackBase;\r
734 UINTN NewStackTop;\r
735 UINTN NewStackBase;\r
736 EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData;\r
737 MP_SERVICES MpServices;\r
738 UINTN ProcessorNumber;\r
739 UINTN EnabledProcessorNum;\r
740 CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer;\r
741 UINTN Index;\r
742 UINTN BspProcessorNum;\r
743 VOID *NewIdtr;\r
744 UINTN *CpuStackBaseBuffer;\r
745\r
746 if (!PcdGetBool (PcdCpuStackGuard)) {\r
747 return UNIT_TEST_PASSED;\r
748 }\r
749\r
750 //\r
751 // Get MP Service Protocol\r
752 //\r
753 Status = GetMpServices (&MpServices);\r
754 Status = MpServicesUnitTestGetNumberOfProcessors (MpServices, &ProcessorNumber, &EnabledProcessorNum);\r
755 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
756 Status = MpServicesUnitTestWhoAmI (MpServices, &BspProcessorNum);\r
757 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
758 mNumberOfProcessors = ProcessorNumber;\r
759\r
760 CpuOriginalRegisterBuffer = SaveAllCpuRegisters (&MpServices, BspProcessorNum);\r
761\r
762 //\r
763 // Initialize Bsp and AP Idt.\r
764 // Idt buffer should not be empty or it will hang in MP API.\r
765 //\r
766 NewIdtr = InitializeBspIdt ();\r
767 Status = InitializeCpuExceptionHandlers (NULL);\r
768 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
769 InitializeApIdt (MpServices, NewIdtr);\r
770\r
771 //\r
772 // Get BSP and AP original stack base.\r
773 //\r
774 CpuStackBaseBuffer = GetAllCpuStackBase (&MpServices, BspProcessorNum);\r
775\r
776 //\r
777 // InitializeMpExceptionStackSwitchHandlers and register exception handler.\r
778 //\r
779 SwitchStackData = InitializeMpExceptionStackSwitchHandlers (MpServices, BspProcessorNum);\r
780 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, CpuStackGuardExceptionHandler);\r
781 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
782 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, AdjustRipForFaultHandler);\r
783 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
784\r
785 for (Index = 0; Index < mNumberOfProcessors; Index++) {\r
786 OriginalStackBase = CpuStackBaseBuffer[Index];\r
787 NewStackTop = (UINTN)(SwitchStackData[Index].Buffer) + SwitchStackData[Index].BufferSize;\r
788 NewStackBase = (UINTN)(SwitchStackData[Index].Buffer);\r
789 if (Index == BspProcessorNum) {\r
790 TriggerStackOverflow ();\r
791 } else {\r
792 MpServicesUnitTestStartupThisAP (\r
793 MpServices,\r
794 (EFI_AP_PROCEDURE)TriggerStackOverflow,\r
795 Index,\r
796 0,\r
797 NULL\r
798 );\r
799 }\r
800\r
801 DEBUG ((DEBUG_INFO, "TestCase4: mRspAddress[0] is 0x%x, mRspAddress[1] is 0x%x\n", mRspAddress[0], mRspAddress[1]));\r
802 UT_ASSERT_TRUE ((mRspAddress[0] >= OriginalStackBase) && (mRspAddress[0] <= (OriginalStackBase + SIZE_4KB)));\r
803 UT_ASSERT_TRUE ((mRspAddress[1] >= NewStackBase) && (mRspAddress[1] < NewStackTop));\r
804 }\r
805\r
806 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL);\r
807 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
808 Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, NULL);\r
809 UT_ASSERT_EQUAL (Status, EFI_SUCCESS);\r
810 RestoreAllCpuRegisters (&MpServices, CpuOriginalRegisterBuffer, BspProcessorNum);\r
811 FreePool (SwitchStackData);\r
812 FreePool (CpuOriginalRegisterBuffer);\r
813 FreePool (NewIdtr);\r
814\r
815 return UNIT_TEST_PASSED;\r
816}\r
817\r
818/**\r
819 Create CpuExceptionLibUnitTestSuite and add test case.\r
820\r
821 @param[in] FrameworkHandle Unit test framework.\r
822\r
823 @return EFI_SUCCESS The unit test suite was created.\r
824 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
825 initialize the unit test suite.\r
826**/\r
827EFI_STATUS\r
828AddCommonTestCase (\r
829 IN UNIT_TEST_FRAMEWORK_HANDLE Framework\r
830 )\r
831{\r
832 EFI_STATUS Status;\r
833 UNIT_TEST_SUITE_HANDLE CpuExceptionLibUnitTestSuite;\r
834\r
835 //\r
836 // Populate the Manual Test Cases.\r
837 //\r
838 Status = CreateUnitTestSuite (&CpuExceptionLibUnitTestSuite, Framework, "Test CpuExceptionHandlerLib", "CpuExceptionHandlerLib.Manual", NULL, NULL);\r
839 if (EFI_ERROR (Status)) {\r
840 DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for CpuExceptionHandlerLib Test Cases\n"));\r
841 Status = EFI_OUT_OF_RESOURCES;\r
842 return Status;\r
843 }\r
844\r
845 AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for no error code exception", "TestRegisterHandlerForNoErrorCodeException", TestRegisterHandlerForNoErrorCodeException, NULL, NULL, NULL);\r
846 AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for GP and PF", "TestRegisterHandlerForGPAndPF", TestRegisterHandlerForGPAndPF, NULL, NULL, NULL);\r
847\r
848 AddTestCase (CpuExceptionLibUnitTestSuite, "Check if Cpu Context is consistent before and after exception.", "TestCpuContextConsistency", TestCpuContextConsistency, NULL, NULL, NULL);\r
849 AddTestCase (CpuExceptionLibUnitTestSuite, "Check if stack overflow is captured by CpuStackGuard in Bsp and AP", "TestCpuStackGuardInBspAndAp", TestCpuStackGuardInBspAndAp, NULL, NULL, NULL);\r
850\r
851 return EFI_SUCCESS;\r
852}\r