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