Home | History | Annotate | Download | only in Ipf
      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
     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
     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
    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
    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
    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
    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