1/** @file
2  Provide legacy thunk interface for accessing Bios Video Rom.
3
4Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
24VOID
25InitializeBiosIntCaller (
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**/
64VOID
65InitializeInterruptRedirection (
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**/
134BOOLEAN
135EFIAPI
136LegacyBiosInt86 (
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