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