VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/UnitTest/CpuExceptionHandlerTestCommon.c

最後變更 在這個檔案是 101291,由 vboxsync 提交於 18 月 前

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette