983a01305ac6db6153984edc3d5f7e425bb70266
[mirror_edk2.git] / MdeModulePkg / Universal / DebugSupportDxe / X64 / PlDebugSupport.c
1 /**@file
2 X64 specific debug support functions
3
4 Copyright (c) 2006 - 2007, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 //
16 // private header files
17 //
18 #include "plDebugSupport.h"
19
20 //
21 // This the global main table to keep track of the interrupts
22 //
23 IDT_ENTRY *IdtEntryTable = NULL;
24 DESCRIPTOR NullDesc = {0, 0};
25
26 EFI_STATUS
27 CreateEntryStub (
28 IN EFI_EXCEPTION_TYPE ExceptionType,
29 OUT VOID **Stub
30 )
31 /*++
32
33 Routine Description: Allocate pool for a new IDT entry stub. Copy the generic
34 stub into the new buffer and fixup the vector number and jump target address.
35
36 Arguments:
37 ExceptionType - This is the exception type that the new stub will be created
38 for.
39 Stub - On successful exit, *Stub contains the newly allocated entry stub.
40 Returns:
41 Typically EFI_SUCCESS
42 other possibilities are passed through from AllocatePool
43
44 --*/
45 {
46 UINT8 *StubCopy;
47
48 StubCopy = *Stub;
49
50 //
51 // Fixup the stub code for this vector
52 //
53
54 // The stub code looks like this:
55 //
56 // 00000000 6A 00 push 0 ; push vector number - will be modified before installed
57 // 00000002 E9 db 0e9h ; jump rel32
58 // 00000003 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry
59 //
60
61 //
62 // poke in the exception type so the second push pushes the exception type
63 //
64 StubCopy[0x1] = (UINT8) ExceptionType;
65
66 //
67 // fixup the jump target to point to the common entry
68 //
69 *(UINT32 *) &StubCopy[0x3] = (UINT32)((UINTN) CommonIdtEntry - (UINTN) &StubCopy[StubSize]);
70
71 return EFI_SUCCESS;
72 }
73
74 EFI_STATUS
75 HookEntry (
76 IN EFI_EXCEPTION_TYPE ExceptionType,
77 IN VOID (*NewCallback) ()
78 )
79 /*++
80
81 Routine Description:
82 Creates a nes entry stub. Then saves the current IDT entry and replaces it
83 with an interrupt gate for the new entry point. The IdtEntryTable is updated
84 with the new registered function.
85
86 This code executes in boot services context. The stub entry executes in interrupt
87 context.
88
89 Arguments:
90 ExceptionType - specifies which vector to hook.
91 NewCallback - a pointer to the new function to be registered.
92
93 Returns:
94 EFI_SUCCESS
95 Other possibilities are passed through by CreateEntryStub
96
97 --*/
98 {
99 BOOLEAN OldIntFlagState;
100 EFI_STATUS Status;
101
102 Status = CreateEntryStub (ExceptionType, (VOID **) &IdtEntryTable[ExceptionType].StubEntry);
103 if (Status == EFI_SUCCESS) {
104 OldIntFlagState = WriteInterruptFlag (0);
105 ReadIdt (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc));
106
107 ((UINT16 *) &IdtEntryTable[ExceptionType].OrigVector)[0] = ((UINT16 *) &IdtEntryTable[ExceptionType].OrigDesc.Low)[0];
108 ((UINT16 *) &IdtEntryTable[ExceptionType].OrigVector)[1] = ((UINT16 *) &IdtEntryTable[ExceptionType].OrigDesc.Low)[3];
109 ((UINT32 *) &IdtEntryTable[ExceptionType].OrigVector)[1] = ((UINT32 *) &IdtEntryTable[ExceptionType].OrigDesc.High)[0];
110
111 Vect2Desc (&IdtEntryTable[ExceptionType].NewDesc, IdtEntryTable[ExceptionType].StubEntry);
112 IdtEntryTable[ExceptionType].RegisteredCallback = NewCallback;
113 WriteIdt (ExceptionType, &(IdtEntryTable[ExceptionType].NewDesc));
114 WriteInterruptFlag (OldIntFlagState);
115 }
116
117 return Status;
118 }
119
120 EFI_STATUS
121 UnhookEntry (
122 IN EFI_EXCEPTION_TYPE ExceptionType
123 )
124 /*++
125
126 Routine Description:
127 Undoes HookEntry. This code executes in boot services context.
128
129 Arguments:
130 ExceptionType - specifies which entry to unhook
131
132 Returns:
133 EFI_SUCCESS
134
135 --*/
136 {
137 BOOLEAN OldIntFlagState;
138
139 OldIntFlagState = WriteInterruptFlag (0);
140 WriteIdt (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc));
141 WriteInterruptFlag (OldIntFlagState);
142
143 return EFI_SUCCESS;
144 }
145
146 EFI_STATUS
147 ManageIdtEntryTable (
148 VOID (*NewCallback)(),
149 EFI_EXCEPTION_TYPE ExceptionType
150 )
151 /*++
152
153 Routine Description:
154 This is the main worker function that manages the state of the interrupt
155 handlers. It both installs and uninstalls interrupt handlers based on the
156 value of NewCallback. If NewCallback is NULL, then uninstall is indicated.
157 If NewCallback is non-NULL, then install is indicated.
158
159 Arguments:
160 NewCallback - If non-NULL, NewCallback specifies the new handler to register.
161 If NULL, specifies that the previously registered handler should
162 be uninstalled.
163 ExceptionType - Indicates which entry to manage
164
165 Returns:
166 EFI_SUCCESS
167 EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has
168 no handler registered for it
169 EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered.
170
171 Other possible return values are passed through from UnHookEntry and HookEntry.
172
173 --*/
174 {
175 EFI_STATUS Status;
176
177 Status = EFI_SUCCESS;
178
179 if (CompareDescriptor (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc)) {
180 //
181 // we've already installed to this vector
182 //
183 if (NewCallback != NULL) {
184 //
185 // if the input handler is non-null, error
186 //
187 Status = EFI_ALREADY_STARTED;
188 } else {
189 Status = UnhookEntry (ExceptionType);
190 }
191 } else {
192 //
193 // no user handler installed on this vector
194 //
195 if (NewCallback == NULL) {
196 //
197 // if the input handler is null, error
198 //
199 Status = EFI_INVALID_PARAMETER;
200 } else {
201 Status = HookEntry (ExceptionType, NewCallback);
202 }
203 }
204
205 return Status;
206 }
207
208 EFI_STATUS
209 EFIAPI
210 GetMaximumProcessorIndex (
211 IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
212 OUT UINTN *MaxProcessorIndex
213 )
214 /*++
215
216 Routine Description: This is a DebugSupport protocol member function.
217
218 Arguments:
219 This - The DebugSupport instance
220 MaxProcessorIndex - The maximuim supported processor index
221
222 Returns:
223 Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0
224
225 --*/
226 {
227 *MaxProcessorIndex = 0;
228 return (EFI_SUCCESS);
229 }
230
231 EFI_STATUS
232 EFIAPI
233 RegisterPeriodicCallback (
234 IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
235 IN UINTN ProcessorIndex,
236 IN EFI_PERIODIC_CALLBACK PeriodicCallback
237 )
238 /*++
239
240 Routine Description: This is a DebugSupport protocol member function.
241
242 Arguments:
243 This - The DebugSupport instance
244 ProcessorIndex - Which processor the callback applies to.
245 PeriodicCallback - Callback function
246
247 Returns:
248
249 EFI_SUCCESS
250 EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has
251 no handler registered for it
252 EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered.
253
254 Other possible return values are passed through from UnHookEntry and HookEntry.
255
256 --*/
257 {
258 return ManageIdtEntryTable (PeriodicCallback, SYSTEM_TIMER_VECTOR);
259 }
260
261 EFI_STATUS
262 EFIAPI
263 RegisterExceptionCallback (
264 IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
265 IN UINTN ProcessorIndex,
266 IN EFI_EXCEPTION_CALLBACK NewCallback,
267 IN EFI_EXCEPTION_TYPE ExceptionType
268 )
269 /*++
270
271 Routine Description:
272 This is a DebugSupport protocol member function.
273
274 This code executes in boot services context.
275
276 Arguments:
277 This - The DebugSupport instance
278 ProcessorIndex - Which processor the callback applies to.
279 NewCallback - Callback function
280 ExceptionType - Which exception to hook
281
282 Returns:
283
284 EFI_SUCCESS
285 EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has
286 no handler registered for it
287 EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered.
288
289 Other possible return values are passed through from UnHookEntry and HookEntry.
290
291 --*/
292 {
293 return ManageIdtEntryTable (NewCallback, ExceptionType);
294 }
295
296 EFI_STATUS
297 EFIAPI
298 InvalidateInstructionCache (
299 IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
300 IN UINTN ProcessorIndex,
301 IN VOID *Start,
302 IN UINT64 Length
303 )
304 /*++
305
306 Routine Description:
307 This is a DebugSupport protocol member function.
308 Calls assembly routine to flush cache.
309
310 Arguments:
311 This - The DebugSupport instance
312 ProcessorIndex - Which processor the callback applies to.
313 Start - Physical base of the memory range to be invalidated
314 Length - mininum number of bytes in instruction cache to invalidate
315
316 Returns:
317
318 EFI_SUCCESS - always return success
319
320 --*/
321 {
322 AsmWbinvd ();
323 return EFI_SUCCESS;
324 }
325
326 EFI_STATUS
327 plInitializeDebugSupportDriver (
328 VOID
329 )
330 /*++
331
332 Routine Description:
333 Initializes driver's handler registration database.
334
335 This code executes in boot services context.
336
337 Arguments:
338 None
339
340 Returns:
341 EFI_SUCCESS
342 EFI_UNSUPPORTED - if X64 processor does not support FXSTOR/FXRSTOR instructions,
343 the context save will fail, so these processor's are not supported.
344 EFI_OUT_OF_RESOURCES - not resource to finish initialization
345
346 --*/
347 {
348 EFI_EXCEPTION_TYPE ExceptionType;
349
350 if (!FxStorSupport ()) {
351 return EFI_UNSUPPORTED;
352 }
353
354 IdtEntryTable = AllocateZeroPool (sizeof (IDT_ENTRY) * NUM_IDT_ENTRIES);
355 if (IdtEntryTable == NULL) {
356 return EFI_OUT_OF_RESOURCES;
357 }
358
359 for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) {
360 IdtEntryTable[ExceptionType].StubEntry = (DEBUG_PROC) (UINTN) AllocatePool (StubSize);
361 if (IdtEntryTable[ExceptionType].StubEntry == NULL) {
362 goto ErrorCleanup;
363 }
364
365 CopyMem ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry, InterruptEntryStub, StubSize);
366 }
367 return EFI_SUCCESS;
368
369 ErrorCleanup:
370
371 for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) {
372 if (IdtEntryTable[ExceptionType].StubEntry != NULL) {
373 FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry);
374 }
375 }
376 FreePool (IdtEntryTable);
377
378 return EFI_OUT_OF_RESOURCES;
379 }
380
381 EFI_STATUS
382 EFIAPI
383 plUnloadDebugSupportDriver (
384 IN EFI_HANDLE ImageHandle
385 )
386 /*++
387
388 Routine Description:
389 This is the callback that is written to the LoadedImage protocol instance
390 on the image handle. It uninstalls all registered handlers and frees all entry
391 stub memory.
392
393 This code executes in boot services context.
394
395 Arguments:
396 ImageHandle - The image handle of the unload handler
397
398 Returns:
399
400 EFI_SUCCESS - always return success
401
402 --*/
403 {
404 EFI_EXCEPTION_TYPE ExceptionType;
405
406 for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) {
407 ManageIdtEntryTable (NULL, ExceptionType);
408 }
409
410 FreePool (IdtEntryTable);
411 return EFI_SUCCESS;
412 }
413
414 VOID
415 InterruptDistrubutionHub (
416 EFI_EXCEPTION_TYPE ExceptionType,
417 EFI_SYSTEM_CONTEXT_IA32 *ContextRecord
418 )
419 /*++
420
421 Routine Description: Common piece of code that invokes the registered handlers.
422
423 This code executes in exception context so no efi calls are allowed.
424
425 Arguments:
426 ExceptionType - exception type
427 ContextRecord - system context
428
429 Returns:
430
431 None
432
433 --*/
434 {
435 if (IdtEntryTable[ExceptionType].RegisteredCallback != NULL) {
436 if (ExceptionType != SYSTEM_TIMER_VECTOR) {
437 IdtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, ContextRecord);
438 } else {
439 OrigVector = IdtEntryTable[ExceptionType].OrigVector;
440 IdtEntryTable[ExceptionType].RegisteredCallback (ContextRecord);
441 }
442 }
443 }