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