• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Call into 16-bit BIOS code
3 
4   BugBug: Thunker does A20 gate. Can we get rid of this code or
5           put it into Legacy16 code.
6 
7 Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR>
8 
9 This program and the accompanying materials
10 are licensed and made available under the terms and conditions
11 of the BSD License which accompanies this distribution.  The
12 full text of the license may be found at
13 http://opensource.org/licenses/bsd-license.php
14 
15 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 
18 **/
19 
20 #include "LegacyBiosInterface.h"
21 #include "IpfThunk.h"
22 
23 /**
24   Gets the current flat GDT and IDT descriptors and  store them in
25   Private->IntThunk.  These values are used by the Thunk code.
26   This method must be called before every thunk in order to assure
27   that the correct GDT and IDT are restored after the thunk.
28 
29   @param  Private            Private context for Legacy BIOS
30 
31   @retval EFI_SUCCESS        Should only pass.
32 
33 **/
34 EFI_STATUS
LegacyBiosGetFlatDescs(IN LEGACY_BIOS_INSTANCE * Private)35 LegacyBiosGetFlatDescs (
36   IN  LEGACY_BIOS_INSTANCE    *Private
37   )
38 {
39   return EFI_SUCCESS;
40 }
41 
42 
43 /**
44   BIOS interrupt call function.
45 
46   @param  BiosInt            Int number of BIOS call
47   @param  Segment            Segment number
48   @param  Offset             Offset in segment
49   @param  Regs               IA32 Register set.
50   @param  Stack              Base address of stack
51   @param  StackSize          Size of stack
52 
53   @retval EFI_SUCCESS        BIOS interrupt call succeeds.
54 
55 **/
56 EFI_STATUS
BiosIntCall(IN UINT16 BiosInt,IN UINT16 Segment,IN UINT16 Offset,IN EFI_IA32_REGISTER_SET * Regs,IN VOID * Stack,IN UINTN StackSize)57 BiosIntCall (
58   IN  UINT16                            BiosInt,
59   IN  UINT16                            Segment,
60   IN  UINT16                            Offset,
61   IN  EFI_IA32_REGISTER_SET             *Regs,
62   IN  VOID                              *Stack,
63   IN  UINTN                             StackSize
64   )
65 {
66   IPF_DWORD_REGS  DwordRegs;
67   UINT64          IntTypeVariable;
68 
69   IntTypeVariable = 0x8000000000000000;
70   IntTypeVariable |= (UINT64)BiosInt;
71 
72   DwordRegs.Cs    = Segment;
73   DwordRegs.Eip   = Offset;
74 
75   DwordRegs.Ds    = Regs->X.DS;
76   DwordRegs.Es    = Regs->X.ES;
77   DwordRegs.Fs    = Regs->X.ES;
78   DwordRegs.Gs    = Regs->X.ES;
79   DwordRegs.Ss    = 0xFFFF;
80 
81   DwordRegs.Eax   = Regs->X.AX;
82   DwordRegs.Ebx   = Regs->X.BX;
83   //
84   // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is
85   // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write.
86   //
87   DwordRegs.Ecx   = Regs->E.ECX;
88   DwordRegs.Edx   = Regs->X.DX;
89 
90   DwordRegs.Ebp   = Regs->X.BP;
91   DwordRegs.Eflag = *((UINT16 *) &Regs->X.Flags);
92 
93   DwordRegs.Edi   = Regs->X.DI;
94   DwordRegs.Esi   = Regs->X.SI;
95   DwordRegs.Esp   = 0xFFFFFFFF;
96 
97   EfiIaEntryPoint (IntTypeVariable, &DwordRegs, ((UINTN) Stack + StackSize), StackSize);
98 
99   Regs->X.CS  = DwordRegs.Cs;
100 
101   Regs->X.DS  = (UINT16) DwordRegs.Ds;
102   Regs->X.SS  = (UINT16) DwordRegs.Ss;
103 
104   Regs->E.EAX  = DwordRegs.Eax;
105   Regs->E.EBX  = DwordRegs.Ebx;
106   Regs->E.ECX  = DwordRegs.Ecx;
107   Regs->E.EDX  = DwordRegs.Edx;
108 
109   Regs->E.EBP  = DwordRegs.Ebp;
110   CopyMem (&Regs->X.Flags, &DwordRegs.Eflag, sizeof (EFI_FLAGS_REG));
111 
112   Regs->E.EDI  = DwordRegs.Edi;
113   Regs->E.ESI  = DwordRegs.Esi;
114 
115   return EFI_SUCCESS;
116 }
117 
118 
119 /**
120   Template of real mode code.
121 
122   @param  CodeStart          Start address of code.
123   @param  CodeEnd            End address of code
124   @param  ReverseThunkStart  Start of reverse thunk.
125   @param  IntThunk           Low memory thunk.
126 
127 **/
128 VOID
RealModeTemplate(OUT UINTN * CodeStart,OUT UINTN * CodeEnd,OUT UINTN * ReverseThunkStart,LOW_MEMORY_THUNK * IntThunk)129 RealModeTemplate (
130   OUT UINTN          *CodeStart,
131   OUT UINTN          *CodeEnd,
132   OUT UINTN          *ReverseThunkStart,
133   LOW_MEMORY_THUNK   *IntThunk
134   )
135 {
136   SAL_RETURN_REGS SalStatus;
137 
138   SalStatus           = EsalGetReverseThunkAddress ();
139 
140   *CodeStart          = SalStatus.r9;
141   *CodeEnd            = SalStatus.r10;
142   *ReverseThunkStart  = SalStatus.r11;
143 
144 }
145 
146 
147 /**
148   Allocate memory < 1 MB and copy the thunker code into low memory. Se up
149   all the descriptors.
150 
151   @param  Private            Private context for Legacy BIOS
152 
153   @retval EFI_SUCCESS        Should only pass.
154 
155 **/
156 EFI_STATUS
LegacyBiosInitializeThunk(IN LEGACY_BIOS_INSTANCE * Private)157 LegacyBiosInitializeThunk (
158   IN  LEGACY_BIOS_INSTANCE    *Private
159   )
160 {
161   GDT32               *CodeGdt;
162   GDT32               *DataGdt;
163   UINTN             CodeStart;
164   UINTN             CodeEnd;
165   UINTN             ReverseThunkStart;
166   UINT32            Base;
167   LOW_MEMORY_THUNK  *IntThunk;
168   UINTN             TempData;
169 
170   ASSERT (Private);
171 
172   IntThunk = Private->IntThunk;
173 
174   //
175   // Clear the reserved descriptor
176   //
177   ZeroMem (&(IntThunk->RealModeGdt[0]), sizeof (GDT32));
178 
179   //
180   // Setup a descriptor for real-mode code
181   //
182   CodeGdt = &(IntThunk->RealModeGdt[1]);
183 
184   //
185   // Fill in the descriptor with our real-mode segment value
186   //
187   CodeGdt->Type = 0xA;
188   //
189   // code/read
190   //
191   CodeGdt->System       = 1;
192   CodeGdt->Dpl          = 0;
193   CodeGdt->Present      = 1;
194   CodeGdt->Software     = 0;
195   CodeGdt->Reserved     = 0;
196   CodeGdt->DefaultSize  = 0;
197   //
198   // 16 bit operands
199   //
200   CodeGdt->Granularity  = 0;
201 
202   CodeGdt->LimitHi      = 0;
203   CodeGdt->LimitLo      = 0xffff;
204 
205   Base                  = (*((UINT32 *) &IntThunk->Code));
206   CodeGdt->BaseHi       = (Base >> 24) & 0xFF;
207   CodeGdt->BaseMid      = (Base >> 16) & 0xFF;
208   CodeGdt->BaseLo       = Base & 0xFFFF;
209 
210   //
211   // Setup a descriptor for read-mode data
212   //
213   DataGdt = &(IntThunk->RealModeGdt[2]);
214   CopyMem (DataGdt, CodeGdt, sizeof (GDT32));
215 
216   DataGdt->Type = 0x2;
217   //
218   // read/write data
219   //
220   DataGdt->BaseHi = 0x0;
221   //
222   // Base = 0
223   //
224   DataGdt->BaseMid = 0x0;
225   //
226   DataGdt->BaseLo = 0x0;
227   //
228   DataGdt->LimitHi = 0x0F;
229   //
230   // Limit = 4Gb
231   //
232   DataGdt->LimitLo = 0xFFFF;
233   //
234   DataGdt->Granularity = 0x1;
235   //
236   //
237   // Compute selector value
238   //
239   IntThunk->RealModeGdtDesc.Limit = (UINT16) (sizeof (IntThunk->RealModeGdt) - 1);
240   CopyMem (&IntThunk->RealModeGdtDesc.Base, (UINT32 *) &IntThunk->RealModeGdt, sizeof (UINT32));
241   //
242   //  IntThunk->RealModeGdtDesc.Base = *((UINT32*) &IntThunk->RealModeGdt);
243   //
244   IntThunk->RealModeIdtDesc.Limit = 0xFFFF;
245   IntThunk->RealModeIdtDesc.Base  = 0;
246   IntThunk->LowCodeSelector       = (UINT32) ((UINTN) CodeGdt - IntThunk->RealModeGdtDesc.Base);
247   IntThunk->LowDataSelector       = (UINT32) ((UINTN) DataGdt - IntThunk->RealModeGdtDesc.Base);
248 
249   //
250   // Initialize low real-mode code thunk
251   //
252   RealModeTemplate (&CodeStart, &CodeEnd, &ReverseThunkStart, IntThunk);
253 
254   TempData                        = (UINTN) &(IntThunk->Code);
255   IntThunk->LowReverseThunkStart  = ((UINT32) TempData + (UINT32) (ReverseThunkStart - CodeStart));
256 
257   EsalSetSalDataArea (TempData, (UINTN) IntThunk);
258   CopyMem (IntThunk->Code, (VOID *) CodeStart, CodeEnd - CodeStart);
259 
260   IntThunk->EfiToLegacy16InitTable.ReverseThunkCallSegment = EFI_SEGMENT (*((UINT32 *) &IntThunk->LowReverseThunkStart));
261   IntThunk->EfiToLegacy16InitTable.ReverseThunkCallOffset = EFI_OFFSET (*((UINT32 *) &IntThunk->LowReverseThunkStart));
262 
263   return EFI_SUCCESS;
264 }
265 
266 
267 /**
268   Thunk to 16-bit real mode and execute a software interrupt with a vector
269   of BiosInt. Regs will contain the 16-bit register context on entry and
270   exit.
271 
272   @param  This               Protocol instance pointer.
273   @param  BiosInt            Processor interrupt vector to invoke
274   @param  Regs               Register contexted passed into (and returned) from
275                              thunk to  16-bit mode
276 
277   @retval FALSE              Thunk completed, and there were no BIOS errors in the
278                              target code. See Regs for status.
279   @retval TRUE               There was a BIOS erro in the target code.
280 
281 **/
282 BOOLEAN
283 EFIAPI
LegacyBiosInt86(IN EFI_LEGACY_BIOS_PROTOCOL * This,IN UINT8 BiosInt,IN EFI_IA32_REGISTER_SET * Regs)284 LegacyBiosInt86 (
285   IN EFI_LEGACY_BIOS_PROTOCOL           *This,
286   IN  UINT8                             BiosInt,
287   IN  EFI_IA32_REGISTER_SET             *Regs
288   )
289 {
290   EFI_STATUS            Status;
291   LEGACY_BIOS_INSTANCE  *Private;
292   LOW_MEMORY_THUNK      *IntThunk;
293   UINT16                *Stack16;
294   EFI_TPL               OriginalTpl;
295   UINTN                 IaSegment;
296   UINTN                 IaOffset;
297   UINTN                 *Address;
298   UINTN                 TempData;
299 
300   Private   = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
301   IntThunk  = Private->IntThunk;
302 
303   //
304   // Get the current flat GDT, IDT, and SS and store them in Private->IntThunk.
305   //
306   Status = LegacyBiosGetFlatDescs (Private);
307   ASSERT_EFI_ERROR (Status);
308 
309   Regs->X.Flags.Reserved1 = 1;
310   Regs->X.Flags.Reserved2 = 0;
311   Regs->X.Flags.Reserved3 = 0;
312   Regs->X.Flags.Reserved4 = 0;
313   Regs->X.Flags.IOPL      = 3;
314   Regs->X.Flags.NT        = 0;
315   Regs->X.Flags.IF        = 1;
316   Regs->X.Flags.TF        = 0;
317   Regs->X.Flags.CF        = 0;
318   //
319   // Clear the error flag; thunk code may set it.
320   //
321   Stack16 = (UINT16 *) (IntThunk->Stack + LOW_STACK_SIZE);
322 
323   //
324   // Copy regs to low memory stack
325   //
326   Stack16 -= sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16);
327   CopyMem (Stack16, Regs, sizeof (EFI_IA32_REGISTER_SET));
328 
329   //
330   // Provide low stack esp
331   //
332   TempData            = ((UINTN) Stack16) - ((UINTN) IntThunk);
333   IntThunk->LowStack  = *((UINT32 *) &TempData);
334 
335   //
336   // Stack for reverse thunk flat mode.
337   //    It must point to top of stack (end of stack space).
338   //
339   TempData                = ((UINTN) IntThunk->RevThunkStack) + LOW_STACK_SIZE;
340   IntThunk->RevFlatStack  = *((UINT32 *) &TempData);
341 
342   //
343   // The call to Legacy16 is a critical section to EFI
344   //
345   OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
346 
347   //
348   // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
349   //
350   Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL);
351   ASSERT_EFI_ERROR (Status);
352 
353   //
354   // Call the real mode thunk code
355   //
356   TempData  = BiosInt * 4;
357   Address   = (UINTN *) TempData;
358   IaOffset  = 0xFFFF & (*Address);
359   IaSegment = 0xFFFF & ((*Address) >> 16);
360 
361   Status = BiosIntCall (
362             BiosInt,
363             (UINT16) IaSegment,
364             (UINT16) IaOffset,
365             (EFI_IA32_REGISTER_SET *) Stack16,
366             IntThunk,
367             IntThunk->LowStack
368             );
369 
370   //
371   // Check for errors with the thunk
372   //
373   switch (Status) {
374   case THUNK_OK:
375     break;
376 
377   case THUNK_ERR_A20_UNSUP:
378   case THUNK_ERR_A20_FAILED:
379   default:
380     //
381     // For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error).
382     //
383     Regs->X.Flags.CF = 1;
384     break;
385   }
386 
387   Status  = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL);
388   ASSERT_EFI_ERROR (Status);
389 
390   //
391   // End critical section
392   //
393   gBS->RestoreTPL (OriginalTpl);
394 
395   //
396   // Return the resulting registers
397   //
398   CopyMem (Regs, Stack16, sizeof (EFI_IA32_REGISTER_SET));
399 
400   return (BOOLEAN) (Regs->X.Flags.CF != 0);
401 }
402 
403 
404 /**
405   Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
406   16-bit register context on entry and exit. Arguments can be passed on
407   the Stack argument
408 
409   @param  This               Protocol instance pointer.
410   @param  Segment            Segemnt of 16-bit mode call
411   @param  Offset             Offset of 16-bit mdoe call
412   @param  Regs               Register contexted passed into (and returned) from
413                              thunk to  16-bit mode
414   @param  Stack              Caller allocated stack used to pass arguments
415   @param  StackSize          Size of Stack in bytes
416 
417   @retval FALSE              Thunk completed, and there were no BIOS errors in the
418                              target code. See Regs for status.
419   @retval TRUE               There was a BIOS erro in the target code.
420 
421 **/
422 BOOLEAN
423 EFIAPI
LegacyBiosFarCall86(IN EFI_LEGACY_BIOS_PROTOCOL * This,IN UINT16 Segment,IN UINT16 Offset,IN EFI_IA32_REGISTER_SET * Regs,IN VOID * Stack,IN UINTN StackSize)424 LegacyBiosFarCall86 (
425   IN EFI_LEGACY_BIOS_PROTOCOL           *This,
426   IN  UINT16                            Segment,
427   IN  UINT16                            Offset,
428   IN  EFI_IA32_REGISTER_SET             *Regs,
429   IN  VOID                              *Stack,
430   IN  UINTN                             StackSize
431   )
432 {
433   EFI_STATUS            Status;
434   LEGACY_BIOS_INSTANCE  *Private;
435   LOW_MEMORY_THUNK      *IntThunk;
436   UINT16                *Stack16;
437   EFI_TPL               OriginalTpl;
438   UINTN                 IaSegment;
439   UINTN                 IaOffset;
440   UINTN                 TempData;
441 
442   Private   = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
443   IntThunk  = Private->IntThunk;
444   IaSegment = Segment;
445   IaOffset  = Offset;
446 
447   //
448   // Get the current flat GDT and IDT and store them in Private->IntThunk.
449   //
450   Status = LegacyBiosGetFlatDescs (Private);
451   ASSERT_EFI_ERROR (Status);
452 
453   Regs->X.Flags.Reserved1 = 1;
454   Regs->X.Flags.Reserved2 = 0;
455   Regs->X.Flags.Reserved3 = 0;
456   Regs->X.Flags.Reserved4 = 0;
457   Regs->X.Flags.IOPL      = 3;
458   Regs->X.Flags.NT        = 0;
459   Regs->X.Flags.IF        = 1;
460   Regs->X.Flags.TF        = 0;
461   Regs->X.Flags.CF        = 0;
462   //
463   // Clear the error flag; thunk code may set it.
464   //
465   Stack16 = (UINT16 *) (IntThunk->Stack + LOW_STACK_SIZE);
466   if (Stack != NULL && StackSize != 0) {
467     //
468     // Copy Stack to low memory stack
469     //
470     Stack16 -= StackSize / sizeof (UINT16);
471     CopyMem (Stack16, Stack, StackSize);
472   }
473   //
474   // Copy regs to low memory stack
475   //
476   Stack16 -= sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16);
477   CopyMem (Stack16, Regs, sizeof (EFI_IA32_REGISTER_SET));
478 
479   //
480   // Provide low stack esp
481   //
482   TempData            = ((UINTN) Stack16) - ((UINTN) IntThunk);
483   IntThunk->LowStack  = *((UINT32 *) &TempData);
484 
485   //
486   // The call to Legacy16 is a critical section to EFI
487   //
488   OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
489 
490   //
491   // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
492   //
493   Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL);
494   ASSERT_EFI_ERROR (Status);
495 
496   //
497   // Call the real mode thunk code
498   //
499   Status = BiosIntCall (
500             0x100,
501             (UINT16) IaSegment,
502             (UINT16) IaOffset,
503             (EFI_IA32_REGISTER_SET *) Stack16,
504             IntThunk,
505             IntThunk->LowStack
506             );
507 
508   //
509   // Check for errors with the thunk
510   //
511   switch (Status) {
512   case THUNK_OK:
513     break;
514 
515   case THUNK_ERR_A20_UNSUP:
516   case THUNK_ERR_A20_FAILED:
517   default:
518     //
519     // For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error).
520     //
521     Regs->X.Flags.CF = 1;
522     break;
523   }
524   //
525   // Restore protected mode interrupt state
526   //
527   Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL);
528   ASSERT_EFI_ERROR (Status);
529 
530   //
531   // End critical section
532   //
533   gBS->RestoreTPL (OriginalTpl);
534 
535   //
536   // Return the resulting registers
537   //
538   CopyMem (Regs, Stack16, sizeof (EFI_IA32_REGISTER_SET));
539   Stack16 += sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16);
540 
541   if (Stack != NULL && StackSize != 0) {
542     //
543     // Copy low memory stack to Stack
544     //
545     CopyMem (Stack, Stack16, StackSize);
546     Stack16 += StackSize / sizeof (UINT16);
547   }
548 
549   return (BOOLEAN) (Regs->X.Flags.CF != 0);
550 }
551