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
7Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR>
8
9This program and the accompanying materials
10are licensed and made available under the terms and conditions
11of the BSD License which accompanies this distribution.  The
12full text of the license may be found at
13http://opensource.org/licenses/bsd-license.php
14
15THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16WITHOUT 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**/
34EFI_STATUS
35LegacyBiosGetFlatDescs (
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**/
56EFI_STATUS
57BiosIntCall (
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**/
128VOID
129RealModeTemplate (
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**/
156EFI_STATUS
157LegacyBiosInitializeThunk (
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**/
282BOOLEAN
283EFIAPI
284LegacyBiosInt86 (
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**/
422BOOLEAN
423EFIAPI
424LegacyBiosFarCall86 (
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