]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Csm/LegacyBiosDxe/Thunk.c
OvmfPkg: Change complex DEBUG_CODE() to DEBUG_CODE_BEGIN/END()
[mirror_edk2.git] / OvmfPkg / Csm / LegacyBiosDxe / Thunk.c
CommitLineData
b522c77b
HW
1/** @file\r
2 Call into 16-bit BIOS code, Use AsmThunk16 function of BaseLib.\r
3\r
4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
5\r
6SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include "LegacyBiosInterface.h"\r
11\r
12THUNK_CONTEXT mThunkContext;\r
13\r
14/**\r
15 Sets the counter value for Timer #0 in a legacy 8254 timer.\r
16\r
17 @param Count - The 16-bit counter value to program into Timer #0 of the legacy 8254 timer.\r
18\r
19**/\r
20VOID\r
21SetPitCount (\r
22 IN UINT16 Count\r
23 )\r
24{\r
25 IoWrite8 (TIMER_CONTROL_PORT, TIMER0_CONTROL_WORD);\r
26 IoWrite8 (TIMER0_COUNT_PORT, (UINT8) (Count & 0xFF));\r
27 IoWrite8 (TIMER0_COUNT_PORT, (UINT8) ((Count>>8) & 0xFF));\r
28}\r
29\r
30/**\r
31 Thunk to 16-bit real mode and execute a software interrupt with a vector\r
32 of BiosInt. Regs will contain the 16-bit register context on entry and\r
33 exit.\r
34\r
35 @param This Protocol instance pointer.\r
36 @param BiosInt Processor interrupt vector to invoke\r
37 @param Regs Register contexted passed into (and returned) from thunk to\r
38 16-bit mode\r
39\r
40 @retval FALSE Thunk completed, and there were no BIOS errors in the target code.\r
41 See Regs for status.\r
42 @retval TRUE There was a BIOS erro in the target code.\r
43\r
44**/\r
45BOOLEAN\r
46EFIAPI\r
47LegacyBiosInt86 (\r
48 IN EFI_LEGACY_BIOS_PROTOCOL *This,\r
49 IN UINT8 BiosInt,\r
50 IN EFI_IA32_REGISTER_SET *Regs\r
51 )\r
52{\r
53 UINT16 Segment;\r
54 UINT16 Offset;\r
55\r
56 Regs->X.Flags.Reserved1 = 1;\r
57 Regs->X.Flags.Reserved2 = 0;\r
58 Regs->X.Flags.Reserved3 = 0;\r
59 Regs->X.Flags.Reserved4 = 0;\r
60 Regs->X.Flags.IOPL = 3;\r
61 Regs->X.Flags.NT = 0;\r
62 Regs->X.Flags.IF = 0;\r
63 Regs->X.Flags.TF = 0;\r
64 Regs->X.Flags.CF = 0;\r
65 //\r
66 // The base address of legacy interrupt vector table is 0.\r
67 // We use this base address to get the legacy interrupt handler.\r
68 //\r
69 ACCESS_PAGE0_CODE (\r
70 Segment = (UINT16)(((UINT32 *)0)[BiosInt] >> 16);\r
71 Offset = (UINT16)((UINT32 *)0)[BiosInt];\r
72 );\r
73\r
74 return InternalLegacyBiosFarCall (\r
75 This,\r
76 Segment,\r
77 Offset,\r
78 Regs,\r
79 &Regs->X.Flags,\r
80 sizeof (Regs->X.Flags)\r
81 );\r
82}\r
83\r
84/**\r
85 Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the\r
86 16-bit register context on entry and exit. Arguments can be passed on\r
87 the Stack argument\r
88\r
89 @param This Protocol instance pointer.\r
90 @param Segment Segemnt of 16-bit mode call\r
91 @param Offset Offset of 16-bit mdoe call\r
92 @param Regs Register contexted passed into (and returned) from\r
93 thunk to 16-bit mode\r
94 @param Stack Caller allocated stack used to pass arguments\r
95 @param StackSize Size of Stack in bytes\r
96\r
97 @retval FALSE Thunk completed, and there were no BIOS errors in\r
98 the target code. See Regs for status.\r
99 @retval TRUE There was a BIOS erro in the target code.\r
100\r
101**/\r
102BOOLEAN\r
103EFIAPI\r
104LegacyBiosFarCall86 (\r
105 IN EFI_LEGACY_BIOS_PROTOCOL *This,\r
106 IN UINT16 Segment,\r
107 IN UINT16 Offset,\r
108 IN EFI_IA32_REGISTER_SET *Regs,\r
109 IN VOID *Stack,\r
110 IN UINTN StackSize\r
111 )\r
112{\r
113 Regs->X.Flags.Reserved1 = 1;\r
114 Regs->X.Flags.Reserved2 = 0;\r
115 Regs->X.Flags.Reserved3 = 0;\r
116 Regs->X.Flags.Reserved4 = 0;\r
117 Regs->X.Flags.IOPL = 3;\r
118 Regs->X.Flags.NT = 0;\r
119 Regs->X.Flags.IF = 1;\r
120 Regs->X.Flags.TF = 0;\r
121 Regs->X.Flags.CF = 0;\r
122\r
123 return InternalLegacyBiosFarCall (This, Segment, Offset, Regs, Stack, StackSize);\r
124}\r
125\r
126/**\r
127 Provide NULL interrupt handler which is used to check\r
128 if there is more than one HW interrupt registers with the CPU AP.\r
129\r
48cf40b8
AC
130 @param InterruptType - The type of interrupt that occurred\r
131 @param SystemContext - A pointer to the system context when the interrupt occurred\r
b522c77b
HW
132\r
133**/\r
134VOID\r
135EFIAPI\r
136LegacyBiosNullInterruptHandler (\r
137 IN EFI_EXCEPTION_TYPE InterruptType,\r
138 IN EFI_SYSTEM_CONTEXT SystemContext\r
139 )\r
140{\r
141}\r
142\r
143/**\r
144 Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the\r
145 16-bit register context on entry and exit. Arguments can be passed on\r
146 the Stack argument\r
147\r
148 @param This Protocol instance pointer.\r
149 @param Segment Segemnt of 16-bit mode call\r
150 @param Offset Offset of 16-bit mdoe call\r
151 @param Regs Register contexted passed into (and returned) from thunk to\r
152 16-bit mode\r
153 @param Stack Caller allocated stack used to pass arguments\r
154 @param StackSize Size of Stack in bytes\r
155\r
156 @retval FALSE Thunk completed, and there were no BIOS errors in the target code.\r
157 See Regs for status.\r
158 @retval TRUE There was a BIOS erro in the target code.\r
159\r
160**/\r
161BOOLEAN\r
162EFIAPI\r
163InternalLegacyBiosFarCall (\r
164 IN EFI_LEGACY_BIOS_PROTOCOL *This,\r
165 IN UINT16 Segment,\r
166 IN UINT16 Offset,\r
167 IN EFI_IA32_REGISTER_SET *Regs,\r
168 IN VOID *Stack,\r
169 IN UINTN StackSize\r
170 )\r
171{\r
172 UINTN Status;\r
173 LEGACY_BIOS_INSTANCE *Private;\r
174 UINT16 *Stack16;\r
175 EFI_TPL OriginalTpl;\r
176 IA32_REGISTER_SET ThunkRegSet;\r
177 BOOLEAN InterruptState;\r
178 UINT64 TimerPeriod;\r
179\r
180 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);\r
181\r
182 ZeroMem (&ThunkRegSet, sizeof (ThunkRegSet));\r
183 ThunkRegSet.X.DI = Regs->X.DI;\r
184 ThunkRegSet.X.SI = Regs->X.SI;\r
185 ThunkRegSet.X.BP = Regs->X.BP;\r
186 ThunkRegSet.X.BX = Regs->X.BX;\r
187 ThunkRegSet.X.DX = Regs->X.DX;\r
188 //\r
189 // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is\r
190 // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write.\r
191 //\r
192 ThunkRegSet.E.ECX = Regs->E.ECX;\r
193 ThunkRegSet.X.AX = Regs->X.AX;\r
194 ThunkRegSet.E.DS = Regs->X.DS;\r
195 ThunkRegSet.E.ES = Regs->X.ES;\r
196\r
197 CopyMem (&(ThunkRegSet.E.EFLAGS.UintN), &(Regs->X.Flags), sizeof (Regs->X.Flags));\r
198\r
199 //\r
200 // Clear the error flag; thunk code may set it. Stack16 should be the high address\r
201 // Make Statk16 address the low 16 bit must be not zero.\r
202 //\r
203 Stack16 = (UINT16 *)((UINT8 *) mThunkContext.RealModeBuffer + mThunkContext.RealModeBufferSize - sizeof (UINT16));\r
204\r
205 //\r
206 // Save current rate of DXE Timer\r
207 //\r
208 Private->Timer->GetTimerPeriod (Private->Timer, &TimerPeriod);\r
209\r
210 //\r
211 // Disable DXE Timer while executing in real mode\r
212 //\r
213 Private->Timer->SetTimerPeriod (Private->Timer, 0);\r
214\r
215 //\r
216 // Save and disable interrupt of debug timer\r
217 //\r
218 InterruptState = SaveAndSetDebugTimerInterrupt (FALSE);\r
219\r
220 //\r
221 // The call to Legacy16 is a critical section to EFI\r
222 //\r
223 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
224\r
225 //\r
226 // Check to see if there is more than one HW interrupt registers with the CPU AP.\r
227 // If there is, then ASSERT() since that is not compatible with the CSM because\r
228 // interupts other than the Timer interrupt that was disabled above can not be\r
229 // handled properly from real mode.\r
230 //\r
8e875037 231 DEBUG_CODE_BEGIN ();\r
b522c77b
HW
232 UINTN Vector;\r
233 UINTN Count;\r
234\r
235 for (Vector = 0x20, Count = 0; Vector < 0x100; Vector++) {\r
236 Status = Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, LegacyBiosNullInterruptHandler);\r
237 if (Status == EFI_ALREADY_STARTED) {\r
238 Count++;\r
239 }\r
240 if (Status == EFI_SUCCESS) {\r
241 Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, NULL);\r
242 }\r
243 }\r
244 if (Count >= 2) {\r
70d5086c 245 DEBUG ((DEBUG_ERROR, "ERROR: More than one HW interrupt active with CSM enabled\n"));\r
b522c77b
HW
246 }\r
247 ASSERT (Count < 2);\r
8e875037 248 DEBUG_CODE_END ();\r
b522c77b
HW
249\r
250 //\r
251 // If the Timer AP has enabled the 8254 timer IRQ and the current 8254 timer\r
252 // period is less than the CSM required rate of 54.9254, then force the 8254\r
253 // PIT counter to 0, which is the CSM required rate of 54.9254 ms\r
254 //\r
255 if (Private->TimerUses8254 && TimerPeriod < 549254) {\r
256 SetPitCount (0);\r
257 }\r
258\r
259 if (Stack != NULL && StackSize != 0) {\r
260 //\r
261 // Copy Stack to low memory stack\r
262 //\r
263 Stack16 -= StackSize / sizeof (UINT16);\r
264 CopyMem (Stack16, Stack, StackSize);\r
265 }\r
266\r
267 ThunkRegSet.E.SS = (UINT16) (((UINTN) Stack16 >> 16) << 12);\r
268 ThunkRegSet.E.ESP = (UINT16) (UINTN) Stack16;\r
269 ThunkRegSet.E.CS = Segment;\r
270 ThunkRegSet.E.Eip = Offset;\r
271\r
272 mThunkContext.RealModeState = &ThunkRegSet;\r
273\r
274 //\r
275 // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.\r
276 //\r
277 Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL);\r
278 ASSERT_EFI_ERROR (Status);\r
279\r
280 AsmThunk16 (&mThunkContext);\r
281\r
282 if (Stack != NULL && StackSize != 0) {\r
283 //\r
284 // Copy low memory stack to Stack\r
285 //\r
286 CopyMem (Stack, Stack16, StackSize);\r
287 }\r
288\r
289 //\r
290 // Restore protected mode interrupt state\r
291 //\r
292 Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL);\r
293 ASSERT_EFI_ERROR (Status);\r
294\r
295 mThunkContext.RealModeState = NULL;\r
296\r
297 //\r
298 // Enable and restore rate of DXE Timer\r
299 //\r
300 Private->Timer->SetTimerPeriod (Private->Timer, TimerPeriod);\r
301\r
302 //\r
303 // End critical section\r
304 //\r
305 gBS->RestoreTPL (OriginalTpl);\r
306\r
307 //\r
308 // OPROM may allocate EBDA range by itself and change EBDA base and EBDA size.\r
309 // Get the current EBDA base address, and compared with pre-allocate minimum\r
310 // EBDA base address, if the current EBDA base address is smaller, it indicates\r
311 // PcdEbdaReservedMemorySize should be adjusted to larger for more OPROMs.\r
312 //\r
8e875037 313 DEBUG_CODE_BEGIN ();\r
b522c77b
HW
314 {\r
315 UINTN EbdaBaseAddress;\r
316 UINTN ReservedEbdaBaseAddress;\r
317\r
318 ACCESS_PAGE0_CODE (\r
319 EbdaBaseAddress = (*(UINT16 *) (UINTN) 0x40E) << 4;\r
320 ReservedEbdaBaseAddress = CONVENTIONAL_MEMORY_TOP\r
321 - PcdGet32 (PcdEbdaReservedMemorySize);\r
322 ASSERT (ReservedEbdaBaseAddress <= EbdaBaseAddress);\r
323 );\r
324 }\r
8e875037 325 DEBUG_CODE_END ();\r
b522c77b
HW
326\r
327 //\r
328 // Restore interrupt of debug timer\r
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
363 EFI_STATUS Status;\r
364 EFI_PHYSICAL_ADDRESS MemoryAddress;\r
365 UINT8 TimerVector;\r
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
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
418 return EFI_SUCCESS;\r
419}\r