1 /** @file
2   Provide legacy thunk interface for accessing Bios Video Rom.
3 
4 Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "BiosVideo.h"
16 
17 #define EFI_CPU_EFLAGS_IF 0x200
18 
19 /**
20   Initialize legacy environment for BIOS INI caller.
21 
22   @param ThunkContext   the instance pointer of THUNK_CONTEXT
23 **/
24 VOID
InitializeBiosIntCaller(THUNK_CONTEXT * ThunkContext)25 InitializeBiosIntCaller (
26   THUNK_CONTEXT     *ThunkContext
27   )
28 {
29   EFI_STATUS            Status;
30   UINT32                RealModeBufferSize;
31   UINT32                ExtraStackSize;
32   EFI_PHYSICAL_ADDRESS  LegacyRegionBase;
33   UINT32                LegacyRegionSize;
34   //
35   // Get LegacyRegion
36   //
37   AsmGetThunk16Properties (&RealModeBufferSize, &ExtraStackSize);
38   LegacyRegionSize = (((RealModeBufferSize + ExtraStackSize) / EFI_PAGE_SIZE) + 1) * EFI_PAGE_SIZE;
39   LegacyRegionBase = 0x100000;
40   Status = gBS->AllocatePages (
41                   AllocateMaxAddress,
42                   EfiACPIMemoryNVS,
43                   EFI_SIZE_TO_PAGES(LegacyRegionSize),
44                   &LegacyRegionBase
45                   );
46   ASSERT_EFI_ERROR (Status);
47 
48   ThunkContext->RealModeBuffer     = (VOID*)(UINTN)LegacyRegionBase;
49   ThunkContext->RealModeBufferSize = LegacyRegionSize;
50   ThunkContext->ThunkAttributes    = THUNK_ATTRIBUTE_BIG_REAL_MODE|THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15;
51   AsmPrepareThunk16(ThunkContext);
52 }
53 
54 /**
55    Initialize interrupt redirection code and entries, because
56    IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f.
57    Or the interrupt will lost when we do thunk.
58    NOTE: We do not reset 8259 vector base, because it will cause pending
59    interrupt lost.
60 
61    @param Legacy8259  Instance pointer for EFI_LEGACY_8259_PROTOCOL.
62 
63 **/
64 VOID
InitializeInterruptRedirection(IN EFI_LEGACY_8259_PROTOCOL * Legacy8259)65 InitializeInterruptRedirection (
66   IN  EFI_LEGACY_8259_PROTOCOL  *Legacy8259
67   )
68 {
69   EFI_STATUS            Status;
70   EFI_PHYSICAL_ADDRESS  LegacyRegionBase;
71   UINTN                 LegacyRegionLength;
72   UINT32                *IdtArray;
73   UINTN                 Index;
74   UINT8                 ProtectedModeBaseVector;
75   UINT32                InterruptRedirectionCode[] = {
76     0x90CF08CD, // INT8; IRET; NOP
77     0x90CF09CD, // INT9; IRET; NOP
78     0x90CF0ACD, // INTA; IRET; NOP
79     0x90CF0BCD, // INTB; IRET; NOP
80     0x90CF0CCD, // INTC; IRET; NOP
81     0x90CF0DCD, // INTD; IRET; NOP
82     0x90CF0ECD, // INTE; IRET; NOP
83     0x90CF0FCD  // INTF; IRET; NOP
84   };
85 
86   //
87   // Get LegacyRegion
88   //
89   LegacyRegionLength = sizeof(InterruptRedirectionCode);
90   LegacyRegionBase = 0x100000;
91   Status = gBS->AllocatePages (
92                   AllocateMaxAddress,
93                   EfiACPIMemoryNVS,
94                   EFI_SIZE_TO_PAGES(LegacyRegionLength),
95                   &LegacyRegionBase
96                   );
97   ASSERT_EFI_ERROR (Status);
98 
99   //
100   // Copy code to legacy region
101   //
102   CopyMem ((VOID *)(UINTN)LegacyRegionBase, InterruptRedirectionCode, sizeof (InterruptRedirectionCode));
103 
104   //
105   // Get VectorBase, it should be 0x68
106   //
107   Status = Legacy8259->GetVector (Legacy8259, Efi8259Irq0, &ProtectedModeBaseVector);
108   ASSERT_EFI_ERROR (Status);
109 
110   //
111   // Patch IVT 0x68 ~ 0x6f
112   //
113   IdtArray = (UINT32 *) 0;
114   for (Index = 0; Index < 8; Index++) {
115     IdtArray[ProtectedModeBaseVector + Index] = ((EFI_SEGMENT (LegacyRegionBase + Index * 4)) << 16) | (EFI_OFFSET (LegacyRegionBase + Index * 4));
116   }
117 
118   return ;
119 }
120 
121 /**
122   Thunk to 16-bit real mode and execute a software interrupt with a vector
123   of BiosInt. Regs will contain the 16-bit register context on entry and
124   exit.
125 
126   @param  This    Protocol instance pointer.
127   @param  BiosInt Processor interrupt vector to invoke
128   @param  Reg     Register contexted passed into (and returned) from thunk to 16-bit mode
129 
130   @retval TRUE   Thunk completed, and there were no BIOS errors in the target code.
131                  See Regs for status.
132   @retval FALSE  There was a BIOS erro in the target code.
133 **/
134 BOOLEAN
135 EFIAPI
LegacyBiosInt86(IN BIOS_VIDEO_DEV * BiosDev,IN UINT8 BiosInt,IN IA32_REGISTER_SET * Regs)136 LegacyBiosInt86 (
137   IN  BIOS_VIDEO_DEV                 *BiosDev,
138   IN  UINT8                           BiosInt,
139   IN  IA32_REGISTER_SET              *Regs
140   )
141 {
142   UINTN                 Status;
143   IA32_REGISTER_SET     ThunkRegSet;
144   BOOLEAN               Ret;
145   UINT16                *Stack16;
146   BOOLEAN               Enabled;
147 
148   ZeroMem (&ThunkRegSet, sizeof (ThunkRegSet));
149   ThunkRegSet.E.EFLAGS.Bits.Reserved_0 = 1;
150   ThunkRegSet.E.EFLAGS.Bits.Reserved_1 = 0;
151   ThunkRegSet.E.EFLAGS.Bits.Reserved_2 = 0;
152   ThunkRegSet.E.EFLAGS.Bits.Reserved_3 = 0;
153   ThunkRegSet.E.EFLAGS.Bits.IOPL       = 3;
154   ThunkRegSet.E.EFLAGS.Bits.NT         = 0;
155   ThunkRegSet.E.EFLAGS.Bits.IF         = 1;
156   ThunkRegSet.E.EFLAGS.Bits.TF         = 0;
157   ThunkRegSet.E.EFLAGS.Bits.CF         = 0;
158 
159   ThunkRegSet.E.EDI  = Regs->E.EDI;
160   ThunkRegSet.E.ESI  = Regs->E.ESI;
161   ThunkRegSet.E.EBP  = Regs->E.EBP;
162   ThunkRegSet.E.EBX  = Regs->E.EBX;
163   ThunkRegSet.E.EDX  = Regs->E.EDX;
164   ThunkRegSet.E.ECX  = Regs->E.ECX;
165   ThunkRegSet.E.EAX  = Regs->E.EAX;
166   ThunkRegSet.E.DS   = Regs->E.DS;
167   ThunkRegSet.E.ES   = Regs->E.ES;
168 
169   //
170   // The call to Legacy16 is a critical section to EFI
171   //
172   Enabled = SaveAndDisableInterrupts();
173 
174   //
175   // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
176   //
177   Status = BiosDev->Legacy8259->SetMode (BiosDev->Legacy8259, Efi8259LegacyMode, NULL, NULL);
178   ASSERT_EFI_ERROR (Status);
179 
180   Stack16 = (UINT16 *)((UINT8 *) BiosDev->ThunkContext->RealModeBuffer + BiosDev->ThunkContext->RealModeBufferSize - sizeof (UINT16));
181 
182   ThunkRegSet.E.SS   = (UINT16) (((UINTN) Stack16 >> 16) << 12);
183   ThunkRegSet.E.ESP  = (UINT16) (UINTN) Stack16;
184 
185   ThunkRegSet.E.Eip  = (UINT16)((UINT32 *)NULL)[BiosInt];
186   ThunkRegSet.E.CS   = (UINT16)(((UINT32 *)NULL)[BiosInt] >> 16);
187   BiosDev->ThunkContext->RealModeState = &ThunkRegSet;
188   AsmThunk16 (BiosDev->ThunkContext);
189 
190   //
191   // Restore protected mode interrupt state
192   //
193   Status = BiosDev->Legacy8259->SetMode (BiosDev->Legacy8259, Efi8259ProtectedMode, NULL, NULL);
194   ASSERT_EFI_ERROR (Status);
195 
196   //
197   // End critical section
198   //
199   SetInterruptState (Enabled);
200 
201   Regs->E.EDI      = ThunkRegSet.E.EDI;
202   Regs->E.ESI      = ThunkRegSet.E.ESI;
203   Regs->E.EBP      = ThunkRegSet.E.EBP;
204   Regs->E.EBX      = ThunkRegSet.E.EBX;
205   Regs->E.EDX      = ThunkRegSet.E.EDX;
206   Regs->E.ECX      = ThunkRegSet.E.ECX;
207   Regs->E.EAX      = ThunkRegSet.E.EAX;
208   Regs->E.SS       = ThunkRegSet.E.SS;
209   Regs->E.CS       = ThunkRegSet.E.CS;
210   Regs->E.DS       = ThunkRegSet.E.DS;
211   Regs->E.ES       = ThunkRegSet.E.ES;
212 
213   CopyMem (&(Regs->E.EFLAGS), &(ThunkRegSet.E.EFLAGS), sizeof (UINT32));
214 
215   Ret = (BOOLEAN) (Regs->E.EFLAGS.Bits.CF == 1);
216 
217   return Ret;
218 }
219 
220 
221