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