\r
/**\r
Setup separate stacks for certain exception handlers.\r
+ If the input Buffer and BufferSize are both NULL, use global variable if possible.\r
\r
- InitData is optional and processor arch dependent.\r
-\r
- @param[in] InitData Pointer to data optional for information about how\r
- to assign stacks for certain exception handlers.\r
+ @param[in] Buffer Point to buffer used to separate exception stack.\r
+ @param[in, out] BufferSize On input, it indicates the byte size of Buffer.\r
+ If the size is not enough, the return status will\r
+ be EFI_BUFFER_TOO_SMALL, and output BufferSize\r
+ will be the size it needs.\r
\r
@retval EFI_SUCCESS The stacks are assigned successfully.\r
@retval EFI_UNSUPPORTED This function is not supported.\r
-\r
+ @retval EFI_BUFFER_TOO_SMALL This BufferSize is too small.\r
**/\r
EFI_STATUS\r
EFIAPI\r
InitializeSeparateExceptionStacks (\r
- IN CPU_EXCEPTION_INIT_DATA *InitData OPTIONAL\r
+ IN VOID *Buffer,\r
+ IN OUT UINTN *BufferSize\r
)\r
{\r
- if (InitData == NULL) {\r
+ CPU_EXCEPTION_INIT_DATA EssData;\r
+ IA32_DESCRIPTOR Idtr;\r
+ IA32_DESCRIPTOR Gdtr;\r
+ UINTN NeedBufferSize;\r
+ UINTN StackTop;\r
+ UINT8 *NewGdtTable;\r
+\r
+ //\r
+ // X64 needs only one TSS of current task working for all exceptions\r
+ // because of its IST feature. IA32 needs one TSS for each exception\r
+ // in addition to current task. To simplify the code, we report the\r
+ // needed memory for IA32 case to cover both IA32 and X64 exception\r
+ // stack switch.\r
+ //\r
+ // Layout of memory needed for each processor:\r
+ // --------------------------------\r
+ // | Alignment | (just in case)\r
+ // --------------------------------\r
+ // | |\r
+ // | Original GDT |\r
+ // | |\r
+ // --------------------------------\r
+ // | Current task descriptor |\r
+ // --------------------------------\r
+ // | |\r
+ // | Exception task descriptors | X ExceptionNumber\r
+ // | |\r
+ // --------------------------------\r
+ // | Current task-state segment |\r
+ // --------------------------------\r
+ // | |\r
+ // | Exception task-state segment | X ExceptionNumber\r
+ // | |\r
+ // --------------------------------\r
+ //\r
+\r
+ if ((Buffer == NULL) && (BufferSize == NULL)) {\r
return EFI_UNSUPPORTED;\r
}\r
\r
- return ArchSetupExceptionStack (InitData);\r
+ if (BufferSize == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ AsmReadGdtr (&Gdtr);\r
+ //\r
+ // Total needed size includes stack size, new GDT table size, TSS size.\r
+ // Add another DESCRIPTOR size for alignment requiremet.\r
+ //\r
+ NeedBufferSize = CPU_STACK_SWITCH_EXCEPTION_NUMBER * CPU_KNOWN_GOOD_STACK_SIZE +\r
+ CPU_TSS_DESC_SIZE + Gdtr.Limit + 1 +\r
+ CPU_TSS_SIZE +\r
+ sizeof (IA32_TSS_DESCRIPTOR);\r
+ if (*BufferSize < NeedBufferSize) {\r
+ *BufferSize = NeedBufferSize;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ if (Buffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ StackTop = (UINTN)Buffer + CPU_STACK_SWITCH_EXCEPTION_NUMBER * CPU_KNOWN_GOOD_STACK_SIZE;\r
+ NewGdtTable = ALIGN_POINTER (StackTop, sizeof (IA32_TSS_DESCRIPTOR));\r
+\r
+ AsmReadIdtr (&Idtr);\r
+ EssData.X64.Revision = CPU_EXCEPTION_INIT_DATA_REV;\r
+ EssData.X64.KnownGoodStackTop = StackTop;\r
+ EssData.X64.KnownGoodStackSize = CPU_KNOWN_GOOD_STACK_SIZE;\r
+ EssData.X64.StackSwitchExceptions = CPU_STACK_SWITCH_EXCEPTION_LIST;\r
+ EssData.X64.StackSwitchExceptionNumber = CPU_STACK_SWITCH_EXCEPTION_NUMBER;\r
+ EssData.X64.IdtTable = (VOID *)Idtr.Base;\r
+ EssData.X64.IdtTableSize = Idtr.Limit + 1;\r
+ EssData.X64.GdtTable = NewGdtTable;\r
+ EssData.X64.GdtTableSize = CPU_TSS_DESC_SIZE + Gdtr.Limit + 1;\r
+ EssData.X64.ExceptionTssDesc = NewGdtTable + Gdtr.Limit + 1;\r
+ EssData.X64.ExceptionTssDescSize = CPU_TSS_DESC_SIZE;\r
+ EssData.X64.ExceptionTss = NewGdtTable + Gdtr.Limit + 1 + CPU_TSS_DESC_SIZE;\r
+ EssData.X64.ExceptionTssSize = CPU_TSS_SIZE;\r
+\r
+ return ArchSetupExceptionStack (&EssData);\r
}\r