]> git.proxmox.com Git - mirror_edk2.git/blame - IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c
IntelFrameworkModulePkg: Clean up source files
[mirror_edk2.git] / IntelFrameworkModulePkg / Csm / LegacyBiosDxe / Thunk.c
CommitLineData
bcecde14 1/** @file\r
2 Call into 16-bit BIOS code, Use AsmThunk16 function of BaseLib.\r
3\r
0a6f4824 4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
bcecde14 5\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions\r
8of the BSD License which accompanies this distribution. The\r
9full text of the license may be found at\r
10http://opensource.org/licenses/bsd-license.php\r
11\r
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include "LegacyBiosInterface.h"\r
18\r
19THUNK_CONTEXT mThunkContext;\r
20\r
f767f990 21/**\r
22 Sets the counter value for Timer #0 in a legacy 8254 timer.\r
23\r
24 @param Count - The 16-bit counter value to program into Timer #0 of the legacy 8254 timer.\r
25\r
26**/\r
27VOID\r
28SetPitCount (\r
29 IN UINT16 Count\r
30 )\r
31{\r
32 IoWrite8 (TIMER_CONTROL_PORT, TIMER0_CONTROL_WORD);\r
33 IoWrite8 (TIMER0_COUNT_PORT, (UINT8) (Count & 0xFF));\r
34 IoWrite8 (TIMER0_COUNT_PORT, (UINT8) ((Count>>8) & 0xFF));\r
35}\r
36\r
bcecde14 37/**\r
38 Thunk to 16-bit real mode and execute a software interrupt with a vector\r
39 of BiosInt. Regs will contain the 16-bit register context on entry and\r
40 exit.\r
41\r
42 @param This Protocol instance pointer.\r
43 @param BiosInt Processor interrupt vector to invoke\r
44 @param Regs Register contexted passed into (and returned) from thunk to\r
45 16-bit mode\r
46\r
47 @retval FALSE Thunk completed, and there were no BIOS errors in the target code.\r
48 See Regs for status.\r
49 @retval TRUE There was a BIOS erro in the target code.\r
50\r
51**/\r
52BOOLEAN\r
53EFIAPI\r
54LegacyBiosInt86 (\r
55 IN EFI_LEGACY_BIOS_PROTOCOL *This,\r
56 IN UINT8 BiosInt,\r
57 IN EFI_IA32_REGISTER_SET *Regs\r
58 )\r
59{\r
d057d8c4
JW
60 UINT16 Segment;\r
61 UINT16 Offset;\r
26a74a36 62\r
bcecde14 63 Regs->X.Flags.Reserved1 = 1;\r
64 Regs->X.Flags.Reserved2 = 0;\r
65 Regs->X.Flags.Reserved3 = 0;\r
66 Regs->X.Flags.Reserved4 = 0;\r
67 Regs->X.Flags.IOPL = 3;\r
68 Regs->X.Flags.NT = 0;\r
69 Regs->X.Flags.IF = 0;\r
70 Regs->X.Flags.TF = 0;\r
71 Regs->X.Flags.CF = 0;\r
26a74a36 72 //\r
73 // The base address of legacy interrupt vector table is 0.\r
74 // We use this base address to get the legacy interrupt handler.\r
75 //\r
2ea3576e
JW
76 ACCESS_PAGE0_CODE (\r
77 Segment = (UINT16)(((UINT32 *)0)[BiosInt] >> 16);\r
78 Offset = (UINT16)((UINT32 *)0)[BiosInt];\r
79 );\r
0a6f4824 80\r
bcecde14 81 return InternalLegacyBiosFarCall (\r
82 This,\r
d057d8c4
JW
83 Segment,\r
84 Offset,\r
bcecde14 85 Regs,\r
86 &Regs->X.Flags,\r
87 sizeof (Regs->X.Flags)\r
88 );\r
89}\r
90\r
91/**\r
92 Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the\r
93 16-bit register context on entry and exit. Arguments can be passed on\r
94 the Stack argument\r
95\r
96 @param This Protocol instance pointer.\r
97 @param Segment Segemnt of 16-bit mode call\r
98 @param Offset Offset of 16-bit mdoe call\r
99 @param Regs Register contexted passed into (and returned) from\r
100 thunk to 16-bit mode\r
101 @param Stack Caller allocated stack used to pass arguments\r
102 @param StackSize Size of Stack in bytes\r
103\r
104 @retval FALSE Thunk completed, and there were no BIOS errors in\r
105 the target code. See Regs for status.\r
106 @retval TRUE There was a BIOS erro in the target code.\r
107\r
108**/\r
109BOOLEAN\r
110EFIAPI\r
111LegacyBiosFarCall86 (\r
112 IN EFI_LEGACY_BIOS_PROTOCOL *This,\r
113 IN UINT16 Segment,\r
114 IN UINT16 Offset,\r
115 IN EFI_IA32_REGISTER_SET *Regs,\r
116 IN VOID *Stack,\r
117 IN UINTN StackSize\r
118 )\r
119{\r
120 Regs->X.Flags.Reserved1 = 1;\r
121 Regs->X.Flags.Reserved2 = 0;\r
122 Regs->X.Flags.Reserved3 = 0;\r
123 Regs->X.Flags.Reserved4 = 0;\r
124 Regs->X.Flags.IOPL = 3;\r
125 Regs->X.Flags.NT = 0;\r
126 Regs->X.Flags.IF = 1;\r
127 Regs->X.Flags.TF = 0;\r
128 Regs->X.Flags.CF = 0;\r
129\r
130 return InternalLegacyBiosFarCall (This, Segment, Offset, Regs, Stack, StackSize);\r
131}\r
132\r
f767f990 133/**\r
0a6f4824 134 Provide NULL interrupt handler which is used to check\r
f767f990 135 if there is more than one HW interrupt registers with the CPU AP.\r
136\r
137 @param InterruptType - The type of interrupt that occured\r
138 @param SystemContext - A pointer to the system context when the interrupt occured\r
139\r
140**/\r
141VOID\r
142EFIAPI\r
143LegacyBiosNullInterruptHandler (\r
144 IN EFI_EXCEPTION_TYPE InterruptType,\r
145 IN EFI_SYSTEM_CONTEXT SystemContext\r
146 )\r
147{\r
148}\r
149\r
bcecde14 150/**\r
151 Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the\r
152 16-bit register context on entry and exit. Arguments can be passed on\r
153 the Stack argument\r
154\r
155 @param This Protocol instance pointer.\r
156 @param Segment Segemnt of 16-bit mode call\r
157 @param Offset Offset of 16-bit mdoe call\r
158 @param Regs Register contexted passed into (and returned) from thunk to\r
159 16-bit mode\r
160 @param Stack Caller allocated stack used to pass arguments\r
161 @param StackSize Size of Stack in bytes\r
162\r
163 @retval FALSE Thunk completed, and there were no BIOS errors in the target code.\r
164 See Regs for status.\r
165 @retval TRUE There was a BIOS erro in the target code.\r
166\r
167**/\r
168BOOLEAN\r
169EFIAPI\r
170InternalLegacyBiosFarCall (\r
171 IN EFI_LEGACY_BIOS_PROTOCOL *This,\r
172 IN UINT16 Segment,\r
173 IN UINT16 Offset,\r
174 IN EFI_IA32_REGISTER_SET *Regs,\r
175 IN VOID *Stack,\r
176 IN UINTN StackSize\r
177 )\r
178{\r
179 UINTN Status;\r
180 LEGACY_BIOS_INSTANCE *Private;\r
181 UINT16 *Stack16;\r
182 EFI_TPL OriginalTpl;\r
183 IA32_REGISTER_SET ThunkRegSet;\r
184 BOOLEAN InterruptState;\r
f767f990 185 UINT64 TimerPeriod;\r
bcecde14 186\r
187 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);\r
188\r
189 ZeroMem (&ThunkRegSet, sizeof (ThunkRegSet));\r
190 ThunkRegSet.X.DI = Regs->X.DI;\r
191 ThunkRegSet.X.SI = Regs->X.SI;\r
192 ThunkRegSet.X.BP = Regs->X.BP;\r
193 ThunkRegSet.X.BX = Regs->X.BX;\r
194 ThunkRegSet.X.DX = Regs->X.DX;\r
195 //\r
196 // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is\r
197 // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write.\r
198 //\r
199 ThunkRegSet.E.ECX = Regs->E.ECX;\r
200 ThunkRegSet.X.AX = Regs->X.AX;\r
201 ThunkRegSet.E.DS = Regs->X.DS;\r
202 ThunkRegSet.E.ES = Regs->X.ES;\r
203\r
204 CopyMem (&(ThunkRegSet.E.EFLAGS.UintN), &(Regs->X.Flags), sizeof (Regs->X.Flags));\r
205\r
206 //\r
207 // Clear the error flag; thunk code may set it. Stack16 should be the high address\r
208 // Make Statk16 address the low 16 bit must be not zero.\r
209 //\r
210 Stack16 = (UINT16 *)((UINT8 *) mThunkContext.RealModeBuffer + mThunkContext.RealModeBufferSize - sizeof (UINT16));\r
211\r
212 //\r
f767f990 213 // Save current rate of DXE Timer\r
214 //\r
215 Private->Timer->GetTimerPeriod (Private->Timer, &TimerPeriod);\r
216\r
217 //\r
218 // Disable DXE Timer while executing in real mode\r
219 //\r
220 Private->Timer->SetTimerPeriod (Private->Timer, 0);\r
0a6f4824 221\r
f767f990 222 //\r
223 // Save and disable interrupt of debug timer\r
bcecde14 224 //\r
225 InterruptState = SaveAndSetDebugTimerInterrupt (FALSE);\r
226\r
227 //\r
228 // The call to Legacy16 is a critical section to EFI\r
229 //\r
230 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
231\r
f767f990 232 //\r
233 // Check to see if there is more than one HW interrupt registers with the CPU AP.\r
0a6f4824
LG
234 // If there is, then ASSERT() since that is not compatible with the CSM because\r
235 // interupts other than the Timer interrupt that was disabled above can not be\r
f767f990 236 // handled properly from real mode.\r
237 //\r
238 DEBUG_CODE (\r
239 UINTN Vector;\r
240 UINTN Count;\r
0a6f4824 241\r
f767f990 242 for (Vector = 0x20, Count = 0; Vector < 0x100; Vector++) {\r
243 Status = Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, LegacyBiosNullInterruptHandler);\r
244 if (Status == EFI_ALREADY_STARTED) {\r
245 Count++;\r
246 }\r
247 if (Status == EFI_SUCCESS) {\r
248 Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, NULL);\r
249 }\r
250 }\r
251 if (Count >= 2) {\r
252 DEBUG ((EFI_D_ERROR, "ERROR: More than one HW interrupt active with CSM enabled\n"));\r
253 }\r
254 ASSERT (Count < 2);\r
255 );\r
256\r
257 //\r
0a6f4824
LG
258 // If the Timer AP has enabled the 8254 timer IRQ and the current 8254 timer\r
259 // period is less than the CSM required rate of 54.9254, then force the 8254\r
f767f990 260 // PIT counter to 0, which is the CSM required rate of 54.9254 ms\r
261 //\r
262 if (Private->TimerUses8254 && TimerPeriod < 549254) {\r
263 SetPitCount (0);\r
264 }\r
0a6f4824 265\r
bcecde14 266 if (Stack != NULL && StackSize != 0) {\r
267 //\r
268 // Copy Stack to low memory stack\r
269 //\r
270 Stack16 -= StackSize / sizeof (UINT16);\r
271 CopyMem (Stack16, Stack, StackSize);\r
272 }\r
273\r
274 ThunkRegSet.E.SS = (UINT16) (((UINTN) Stack16 >> 16) << 12);\r
275 ThunkRegSet.E.ESP = (UINT16) (UINTN) Stack16;\r
276 ThunkRegSet.E.CS = Segment;\r
277 ThunkRegSet.E.Eip = Offset;\r
278\r
279 mThunkContext.RealModeState = &ThunkRegSet;\r
280\r
281 //\r
282 // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.\r
283 //\r
284 Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL);\r
285 ASSERT_EFI_ERROR (Status);\r
286\r
287 AsmThunk16 (&mThunkContext);\r
288\r
bcecde14 289 if (Stack != NULL && StackSize != 0) {\r
290 //\r
291 // Copy low memory stack to Stack\r
292 //\r
293 CopyMem (Stack, Stack16, StackSize);\r
294 }\r
295\r
296 //\r
297 // Restore protected mode interrupt state\r
298 //\r
299 Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL);\r
300 ASSERT_EFI_ERROR (Status);\r
301\r
302 mThunkContext.RealModeState = NULL;\r
303\r
304 //\r
b17f22f5 305 // Enable and restore rate of DXE Timer\r
bcecde14 306 //\r
b17f22f5 307 Private->Timer->SetTimerPeriod (Private->Timer, TimerPeriod);\r
bcecde14 308\r
309 //\r
b17f22f5 310 // End critical section\r
f767f990 311 //\r
b17f22f5 312 gBS->RestoreTPL (OriginalTpl);\r
0a6f4824 313\r
2ea3576e
JW
314 //\r
315 // OPROM may allocate EBDA range by itself and change EBDA base and EBDA size.\r
316 // Get the current EBDA base address, and compared with pre-allocate minimum\r
317 // EBDA base address, if the current EBDA base address is smaller, it indicates\r
318 // PcdEbdaReservedMemorySize should be adjusted to larger for more OPROMs.\r
319 //\r
320 DEBUG_CODE (\r
321 {\r
322 UINTN EbdaBaseAddress;\r
323 UINTN ReservedEbdaBaseAddress;\r
324\r
325 ACCESS_PAGE0_CODE (\r
326 EbdaBaseAddress = (*(UINT16 *) (UINTN) 0x40E) << 4;\r
327 ReservedEbdaBaseAddress = CONVENTIONAL_MEMORY_TOP\r
328 - PcdGet32 (PcdEbdaReservedMemorySize);\r
329 ASSERT (ReservedEbdaBaseAddress <= EbdaBaseAddress);\r
330 );\r
331 }\r
332 );\r
333\r
f767f990 334 //\r
335 // Restore interrupt of debug timer\r
bcecde14 336 //\r
337 SaveAndSetDebugTimerInterrupt (InterruptState);\r
338\r
339 Regs->E.EDI = ThunkRegSet.E.EDI;\r
340 Regs->E.ESI = ThunkRegSet.E.ESI;\r
341 Regs->E.EBP = ThunkRegSet.E.EBP;\r
342 Regs->E.EBX = ThunkRegSet.E.EBX;\r
343 Regs->E.EDX = ThunkRegSet.E.EDX;\r
344 Regs->E.ECX = ThunkRegSet.E.ECX;\r
345 Regs->E.EAX = ThunkRegSet.E.EAX;\r
346 Regs->X.SS = ThunkRegSet.E.SS;\r
347 Regs->X.CS = ThunkRegSet.E.CS;\r
348 Regs->X.DS = ThunkRegSet.E.DS;\r
349 Regs->X.ES = ThunkRegSet.E.ES;\r
350\r
351 CopyMem (&(Regs->X.Flags), &(ThunkRegSet.E.EFLAGS.UintN), sizeof (Regs->X.Flags));\r
352\r
353 return (BOOLEAN) (Regs->X.Flags.CF == 1);\r
354}\r
355\r
356/**\r
357 Allocate memory < 1 MB and copy the thunker code into low memory. Se up\r
358 all the descriptors.\r
359\r
360 @param Private Private context for Legacy BIOS\r
361\r
362 @retval EFI_SUCCESS Should only pass.\r
363\r
364**/\r
365EFI_STATUS\r
366LegacyBiosInitializeThunk (\r
367 IN LEGACY_BIOS_INSTANCE *Private\r
368 )\r
369{\r
f767f990 370 EFI_STATUS Status;\r
bcecde14 371 EFI_PHYSICAL_ADDRESS MemoryAddress;\r
f767f990 372 UINT8 TimerVector;\r
bcecde14 373\r
374 MemoryAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) Private->IntThunk;\r
375\r
376 mThunkContext.RealModeBuffer = (VOID *) (UINTN) (MemoryAddress + ((sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 1) * EFI_PAGE_SIZE);\r
377 mThunkContext.RealModeBufferSize = EFI_PAGE_SIZE;\r
378 mThunkContext.ThunkAttributes = THUNK_ATTRIBUTE_BIG_REAL_MODE | THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15;\r
379\r
380 AsmPrepareThunk16 (&mThunkContext);\r
381\r
f767f990 382 //\r
383 // Get the interrupt vector number corresponding to IRQ0 from the 8259 driver\r
384 //\r
385 TimerVector = 0;\r
386 Status = Private->Legacy8259->GetVector (Private->Legacy8259, Efi8259Irq0, &TimerVector);\r
387 ASSERT_EFI_ERROR (Status);\r
0a6f4824 388\r
f767f990 389 //\r
390 // Check to see if the Timer AP has hooked the IRQ0 from the 8254 PIT\r
0a6f4824 391 //\r
f767f990 392 Status = Private->Cpu->RegisterInterruptHandler (\r
0a6f4824
LG
393 Private->Cpu,\r
394 TimerVector,\r
f767f990 395 LegacyBiosNullInterruptHandler\r
396 );\r
397 if (Status == EFI_SUCCESS) {\r
398 //\r
0a6f4824 399 // If the Timer AP has not enabled the 8254 timer IRQ, then force the 8254 PIT\r
f767f990 400 // counter to 0, which is the CSM required rate of 54.9254 ms\r
401 //\r
402 Private->Cpu->RegisterInterruptHandler (\r
0a6f4824
LG
403 Private->Cpu,\r
404 TimerVector,\r
f767f990 405 NULL\r
406 );\r
407 SetPitCount (0);\r
0a6f4824 408\r
f767f990 409 //\r
410 // Save status that the Timer AP is not using the 8254 PIT\r
411 //\r
412 Private->TimerUses8254 = FALSE;\r
413 } else if (Status == EFI_ALREADY_STARTED) {\r
414 //\r
415 // Save status that the Timer AP is using the 8254 PIT\r
416 //\r
417 Private->TimerUses8254 = TRUE;\r
418 } else {\r
419 //\r
420 // Unexpected status from CPU AP RegisterInterruptHandler()\r
421 //\r
422 ASSERT (FALSE);\r
423 }\r
0a6f4824 424\r
bcecde14 425 return EFI_SUCCESS;\r
426}\r