1/** @file
2  CPU Exception Library provides PEI/DXE/SMM CPU common exception handler.
3
4Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials are licensed and made available under
6the terms and conditions of the BSD License that accompanies this distribution.
7The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php.
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "CpuExceptionCommon.h"
16#include <Library/DebugLib.h>
17
18/**
19  Internal worker function for common exception handler.
20
21  @param ExceptionType         Exception type.
22  @param SystemContext         Pointer to EFI_SYSTEM_CONTEXT.
23  @param ExceptionHandlerData  Pointer to exception handler data.
24**/
25VOID
26CommonExceptionHandlerWorker (
27  IN EFI_EXCEPTION_TYPE          ExceptionType,
28  IN EFI_SYSTEM_CONTEXT          SystemContext,
29  IN EXCEPTION_HANDLER_DATA      *ExceptionHandlerData
30  )
31{
32  EXCEPTION_HANDLER_CONTEXT      *ExceptionHandlerContext;
33  RESERVED_VECTORS_DATA          *ReservedVectors;
34  EFI_CPU_INTERRUPT_HANDLER      *ExternalInterruptHandler;
35
36  ExceptionHandlerContext  = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);
37  ReservedVectors          = ExceptionHandlerData->ReservedVectors;
38  ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
39
40  switch (ReservedVectors[ExceptionType].Attribute) {
41  case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
42    //
43    // Need to jmp to old IDT handler after this exception handler
44    //
45    ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
46    ExceptionHandlerContext->OldIdtHandler     = ReservedVectors[ExceptionType].ExceptonHandler;
47    break;
48  case EFI_VECTOR_HANDOFF_HOOK_AFTER:
49    while (TRUE) {
50      //
51      // If if anyone has gotten SPIN_LOCK for owner running hook after
52      //
53      if (AcquireSpinLockOrFail (&ReservedVectors[ExceptionType].SpinLock)) {
54        //
55        // Need to execute old IDT handler before running this exception handler
56        //
57        ReservedVectors[ExceptionType].ApicId = GetApicId ();
58        ArchSaveExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
59        ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
60        ExceptionHandlerContext->OldIdtHandler     = ReservedVectors[ExceptionType].ExceptonHandler;
61        return;
62      }
63      //
64      // If failed to acquire SPIN_LOCK, check if it was locked by processor itself
65      //
66      if (ReservedVectors[ExceptionType].ApicId == GetApicId ()) {
67        //
68        // Old IDT handler has been executed, then restore CPU exception content to
69        // run new exception handler.
70        //
71        ArchRestoreExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
72        //
73        // Rlease spin lock for ApicId
74        //
75        ReleaseSpinLock (&ReservedVectors[ExceptionType].SpinLock);
76        break;
77      }
78      CpuPause ();
79    }
80    break;
81  case 0xffffffff:
82    break;
83  default:
84    //
85    // It should never reach here
86    //
87    CpuDeadLoop ();
88    break;
89  }
90
91  if (ExternalInterruptHandler != NULL &&
92      ExternalInterruptHandler[ExceptionType] != NULL) {
93    (ExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext);
94  } else if (ExceptionType < CPU_EXCEPTION_NUM) {
95    //
96    // Get Spinlock to display CPU information
97    //
98    while (!AcquireSpinLockOrFail (&ExceptionHandlerData->DisplayMessageSpinLock)) {
99      CpuPause ();
100    }
101    //
102    // Display ExceptionType, CPU information and Image information
103    //
104    DumpCpuContent (ExceptionType, SystemContext);
105    //
106    // Release Spinlock of output message
107    //
108    ReleaseSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock);
109    //
110    // Enter a dead loop if needn't to execute old IDT handler further
111    //
112    if (ReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) {
113      CpuDeadLoop ();
114    }
115  }
116}
117
118/**
119  Internal worker function to update IDT entries accordling to vector attributes.
120
121  @param[in] IdtTable              Pointer to IDT table.
122  @param[in] TemplateMap           Pointer to a buffer where the address map is
123                                   returned.
124  @param[in] ExceptionHandlerData  Pointer to exception handler data.
125
126**/
127VOID
128UpdateIdtTable (
129  IN IA32_IDT_GATE_DESCRIPTOR        *IdtTable,
130  IN EXCEPTION_HANDLER_TEMPLATE_MAP  *TemplateMap,
131  IN EXCEPTION_HANDLER_DATA          *ExceptionHandlerData
132  )
133{
134  UINT16                             CodeSegment;
135  UINTN                              Index;
136  UINTN                              InterruptHandler;
137  RESERVED_VECTORS_DATA              *ReservedVectors;
138
139  ReservedVectors = ExceptionHandlerData->ReservedVectors;
140  //
141  // Use current CS as the segment selector of interrupt gate in IDT
142  //
143  CodeSegment = AsmReadCs ();
144
145  for (Index = 0; Index < ExceptionHandlerData->IdtEntryCount; Index ++) {
146    IdtTable[Index].Bits.Selector = CodeSegment;
147    //
148    // Check reserved vectors attributes
149    //
150    switch (ReservedVectors[Index].Attribute) {
151    case EFI_VECTOR_HANDOFF_DO_NOT_HOOK:
152      //
153      // Keep original IDT entry
154      //
155      continue;
156    case EFI_VECTOR_HANDOFF_HOOK_AFTER:
157      InitializeSpinLock (&ReservedVectors[Index].SpinLock);
158      CopyMem (
159        (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode,
160        (VOID *) TemplateMap->HookAfterStubHeaderStart,
161        TemplateMap->ExceptionStubHeaderSize
162        );
163      AsmVectorNumFixup (
164        (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode,
165        (UINT8) Index,
166        (VOID *) TemplateMap->HookAfterStubHeaderStart
167        );
168      //
169      // Go on the following code
170      //
171    case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
172      //
173      // Save original IDT handler address
174      //
175      ReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]);
176      //
177      // Go on the following code
178      //
179    default:
180      //
181      // Update new IDT entry
182      //
183      InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize;
184      ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler);
185      break;
186    }
187  }
188}
189
190/**
191  Internal worker function to initialize exception handler.
192
193  @param[in]      VectorInfo            Pointer to reserved vector list.
194  @param[in, out] ExceptionHandlerData  Pointer to exception handler data.
195
196  @retval EFI_SUCCESS           CPU Exception Entries have been successfully initialized
197                                with default exception handlers.
198  @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
199  @retval EFI_UNSUPPORTED       This function is not supported.
200
201**/
202EFI_STATUS
203InitializeCpuExceptionHandlersWorker (
204  IN EFI_VECTOR_HANDOFF_INFO       *VectorInfo OPTIONAL,
205  IN OUT EXCEPTION_HANDLER_DATA    *ExceptionHandlerData
206  )
207{
208  EFI_STATUS                       Status;
209  IA32_DESCRIPTOR                  IdtDescriptor;
210  UINTN                            IdtEntryCount;
211  EXCEPTION_HANDLER_TEMPLATE_MAP   TemplateMap;
212  IA32_IDT_GATE_DESCRIPTOR         *IdtTable;
213  RESERVED_VECTORS_DATA            *ReservedVectors;
214
215  ReservedVectors = ExceptionHandlerData->ReservedVectors;
216  SetMem ((VOID *) ReservedVectors, sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM, 0xff);
217  if (VectorInfo != NULL) {
218    Status = ReadAndVerifyVectorInfo (VectorInfo, ReservedVectors, CPU_EXCEPTION_NUM);
219    if (EFI_ERROR (Status)) {
220      return EFI_INVALID_PARAMETER;
221    }
222  }
223
224  //
225  // Read IDT descriptor and calculate IDT size
226  //
227  AsmReadIdtr (&IdtDescriptor);
228  IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);
229  if (IdtEntryCount > CPU_EXCEPTION_NUM) {
230    //
231    // CPU exeption library only setup CPU_EXCEPTION_NUM exception handler at most
232    //
233    IdtEntryCount = CPU_EXCEPTION_NUM;
234  }
235
236  IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base;
237  AsmGetTemplateAddressMap (&TemplateMap);
238  ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE);
239
240  ExceptionHandlerData->IdtEntryCount = IdtEntryCount;
241  UpdateIdtTable (IdtTable, &TemplateMap, ExceptionHandlerData);
242
243  return EFI_SUCCESS;
244}
245
246/**
247  Registers a function to be called from the processor interrupt handler.
248
249  @param[in]  InterruptType        Defines which interrupt or exception to hook.
250  @param[in]  InterruptHandler     A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
251                                   when a processor interrupt occurs. If this parameter is NULL, then the handler
252                                   will be uninstalled
253  @param[in] ExceptionHandlerData  Pointer to exception handler data.
254
255  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
256  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
257                                previously installed.
258  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
259                                previously installed.
260  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported,
261                                or this function is not supported.
262**/
263EFI_STATUS
264RegisterCpuInterruptHandlerWorker (
265  IN EFI_EXCEPTION_TYPE            InterruptType,
266  IN EFI_CPU_INTERRUPT_HANDLER     InterruptHandler,
267  IN EXCEPTION_HANDLER_DATA        *ExceptionHandlerData
268  )
269{
270  UINTN                          EnabledInterruptNum;
271  RESERVED_VECTORS_DATA          *ReservedVectors;
272  EFI_CPU_INTERRUPT_HANDLER      *ExternalInterruptHandler;
273
274  EnabledInterruptNum      = ExceptionHandlerData->IdtEntryCount;
275  ReservedVectors          = ExceptionHandlerData->ReservedVectors;
276  ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
277
278  if (InterruptType < 0 || InterruptType >= (EFI_EXCEPTION_TYPE)EnabledInterruptNum ||
279      ReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) {
280    return EFI_UNSUPPORTED;
281  }
282
283  if (InterruptHandler == NULL && ExternalInterruptHandler[InterruptType] == NULL) {
284    return EFI_INVALID_PARAMETER;
285  }
286
287  if (InterruptHandler != NULL && ExternalInterruptHandler[InterruptType] != NULL) {
288    return EFI_ALREADY_STARTED;
289  }
290
291  ExternalInterruptHandler[InterruptType] = InterruptHandler;
292  return EFI_SUCCESS;
293}
294
295